2015-04-14 01:24:03 +02:00
package nodomain.freeyourgadget.gadgetbridge.miband ;
2015-05-01 01:49:43 +02:00
import android.bluetooth.BluetoothGatt ;
import android.bluetooth.BluetoothGattCharacteristic ;
2015-05-17 22:57:37 +02:00
import android.content.SharedPreferences ;
import android.preference.PreferenceManager ;
2015-05-01 01:49:43 +02:00
2015-05-18 20:56:19 +02:00
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
2015-04-19 02:37:29 +02:00
import java.io.IOException ;
2015-05-12 20:32:34 +02:00
import java.util.Arrays ;
2015-04-23 14:11:57 +02:00
import java.util.Calendar ;
2015-04-26 00:53:48 +02:00
import java.util.UUID ;
2015-04-19 02:37:29 +02:00
2015-04-14 01:24:03 +02:00
import nodomain.freeyourgadget.gadgetbridge.GBCommand ;
2015-04-19 15:11:50 +02:00
import nodomain.freeyourgadget.gadgetbridge.GBDevice.State ;
2015-04-19 02:37:29 +02:00
import nodomain.freeyourgadget.gadgetbridge.btle.AbstractBTLEDeviceSupport ;
import nodomain.freeyourgadget.gadgetbridge.btle.TransactionBuilder ;
2015-04-14 01:24:03 +02:00
2015-05-18 20:56:19 +02:00
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.DEFAULT_VALUE_FLASH_COLOUR ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.DEFAULT_VALUE_FLASH_COUNT ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.DEFAULT_VALUE_FLASH_DURATION ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.DEFAULT_VALUE_FLASH_ORIGINAL_COLOUR ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.DEFAULT_VALUE_VIBRATION_COUNT ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.DEFAULT_VALUE_VIBRATION_DURATION ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.DEFAULT_VALUE_VIBRATION_PAUSE ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.FLASH_COLOUR ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.FLASH_COUNT ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.FLASH_DURATION ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.FLASH_ORIGINAL_COLOUR ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.ORIGIN_GENERIC ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.ORIGIN_K9MAIL ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.ORIGIN_SMS ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.VIBRATION_COUNT ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.VIBRATION_DURATION ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.VIBRATION_PAUSE ;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.getNotificationPrefIntValue ;
2015-05-17 22:57:37 +02:00
2015-04-14 01:24:03 +02:00
public class MiBandSupport extends AbstractBTLEDeviceSupport {
2015-05-12 06:28:11 +02:00
private static final Logger LOG = LoggerFactory . getLogger ( MiBandSupport . class ) ;
2015-04-14 10:29:09 +02:00
2015-04-19 02:37:29 +02:00
public MiBandSupport ( ) {
addSupportedService ( MiBandService . UUID_SERVICE_MIBAND_SERVICE ) ;
2015-04-14 02:03:14 +02:00
}
2015-04-14 01:24:03 +02:00
@Override
2015-04-19 02:37:29 +02:00
protected TransactionBuilder initializeDevice ( TransactionBuilder builder ) {
2015-05-05 23:25:54 +02:00
pair ( builder ) . sendUserInfo ( builder ) . setCurrentTime ( builder ) . requestBatteryInfo ( builder ) ;
2015-04-19 02:37:29 +02:00
return builder ;
2015-04-14 01:24:03 +02:00
}
@Override
2015-04-19 02:37:29 +02:00
public boolean useAutoConnect ( ) {
return true ;
}
2015-04-19 11:28:03 +02:00
2015-05-05 00:48:02 +02:00
@Override
public void pair ( ) {
for ( int i = 0 ; i < 5 ; i + + ) {
if ( connect ( ) ) {
return ;
}
}
}
2015-04-19 02:37:29 +02:00
private byte [ ] getDefaultNotification ( ) {
final int vibrateTimes = 1 ;
final long vibrateDuration = 250l ;
final int flashTimes = 1 ;
final int flashColour = 0xFFFFFFFF ;
final int originalColour = 0xFFFFFFFF ;
final long flashDuration = 250l ;
return getNotification ( vibrateDuration , vibrateTimes , flashTimes , flashColour , originalColour , flashDuration ) ;
}
2015-04-14 01:24:03 +02:00
2015-04-19 02:37:29 +02:00
private void sendDefaultNotification ( TransactionBuilder builder ) {
BluetoothGattCharacteristic characteristic = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_CONTROL_POINT ) ;
2015-05-12 06:28:11 +02:00
LOG . info ( " Sending notification to MiBand: " + characteristic ) ;
2015-04-19 02:37:29 +02:00
builder . write ( characteristic , getDefaultNotification ( ) ) . queue ( getQueue ( ) ) ;
}
2015-05-17 22:57:37 +02:00
private void sendCustomNotification ( int vibrateDuration , int vibrateTimes , int pause , int flashTimes , int flashColour , int originalColour , long flashDuration , TransactionBuilder builder ) {
BluetoothGattCharacteristic controlPoint = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_CONTROL_POINT ) ;
int vDuration = Math . min ( 500 , vibrateDuration ) ; // longer than 500ms is not possible
for ( int i = 0 ; i < vibrateTimes ; i + + ) {
builder . write ( controlPoint , startVibrate ) ;
builder . wait ( vDuration ) ;
builder . write ( controlPoint , stopVibrate ) ;
if ( pause > 0 ) {
builder . wait ( pause ) ;
}
}
LOG . info ( " Sending notification to MiBand: " + controlPoint ) ;
builder . queue ( getQueue ( ) ) ;
}
2015-05-18 20:56:19 +02:00
private static final byte [ ] startVibrate = new byte [ ] { 8 , 1 } ;
private static final byte [ ] stopVibrate = new byte [ ] { 19 } ;
private static final byte [ ] reboot = new byte [ ] { 12 } ;
2015-05-17 22:57:37 +02:00
2015-04-19 02:37:29 +02:00
private byte [ ] getNotification ( long vibrateDuration , int vibrateTimes , int flashTimes , int flashColour , int originalColour , long flashDuration ) {
2015-04-19 11:28:03 +02:00
byte [ ] vibrate = new byte [ ] { ( byte ) 8 , ( byte ) 1 } ;
2015-04-19 02:37:29 +02:00
byte r = 6 ;
byte g = 0 ;
byte b = 6 ;
boolean display = true ;
// byte[] flashColor = new byte[]{ 14, r, g, b, display ? (byte) 1 : (byte) 0 };
return vibrate ;
}
/ * *
* Part of device initialization process . Do not call manually .
2015-04-19 11:28:03 +02:00
*
2015-04-19 02:37:29 +02:00
* @param builder
* @return
* /
private MiBandSupport sendUserInfo ( TransactionBuilder builder ) {
2015-05-12 06:28:11 +02:00
LOG . debug ( " Writing User Info! " ) ;
2015-04-19 02:37:29 +02:00
BluetoothGattCharacteristic characteristic = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_USER_INFO ) ;
2015-05-07 23:51:03 +02:00
builder . write ( characteristic , MiBandCoordinator . getAnyUserInfo ( getDevice ( ) . getAddress ( ) ) . getData ( ) ) ;
2015-04-19 02:37:29 +02:00
return this ;
}
2015-05-05 23:25:54 +02:00
private MiBandSupport requestBatteryInfo ( TransactionBuilder builder ) {
2015-05-12 06:28:11 +02:00
LOG . debug ( " Requesting Battery Info! " ) ;
2015-05-05 23:25:54 +02:00
BluetoothGattCharacteristic characteristic = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_BATTERY ) ;
builder . read ( characteristic ) ;
return this ;
}
2015-04-19 02:37:29 +02:00
/ * *
* Part of device initialization process . Do not call manually .
2015-04-19 11:28:03 +02:00
*
2015-05-07 23:51:03 +02:00
* @param transaction
2015-04-19 02:37:29 +02:00
* @return
* /
private MiBandSupport pair ( TransactionBuilder transaction ) {
2015-05-12 06:28:11 +02:00
LOG . info ( " Attempting to pair MI device... " ) ;
2015-04-19 02:37:29 +02:00
BluetoothGattCharacteristic characteristic = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_PAIR ) ;
if ( characteristic ! = null ) {
transaction . write ( characteristic , new byte [ ] { 2 } ) ;
} else {
2015-05-12 06:28:11 +02:00
LOG . info ( " Unable to pair MI device -- characteristic not available " ) ;
2015-04-19 02:37:29 +02:00
}
return this ;
}
private void performDefaultNotification ( String task ) {
try {
TransactionBuilder builder = performInitialized ( task ) ;
sendDefaultNotification ( builder ) ;
} catch ( IOException ex ) {
2015-05-12 06:28:11 +02:00
LOG . error ( " Unable to send notification to MI device " , ex ) ;
2015-04-19 02:37:29 +02:00
}
2015-04-14 01:24:03 +02:00
}
2015-05-17 22:57:37 +02:00
// private void performCustomNotification(String task, int vibrateDuration, int vibrateTimes, int pause, int flashTimes, int flashColour, int originalColour, long flashDuration) {
// try {
// TransactionBuilder builder = performInitialized(task);
// sendCustomNotification(vibrateDuration, vibrateTimes, pause, flashTimes, flashColour, originalColour, flashDuration, builder);
// } catch (IOException ex) {
// LOG.error("Unable to send notification to MI device", ex);
// }
// }
private void performPreferredNotification ( String task , String notificationOrigin ) {
try {
TransactionBuilder builder = performInitialized ( task ) ;
SharedPreferences prefs = PreferenceManager . getDefaultSharedPreferences ( getContext ( ) ) ;
int vibrateDuration = getPreferredVibrateDuration ( notificationOrigin , prefs ) ;
int vibrateTimes = getPreferredVibrateCount ( notificationOrigin , prefs ) ;
int vibratePause = getPreferredVibratePause ( notificationOrigin , prefs ) ;
int flashTimes = getPreferredFlashCount ( notificationOrigin , prefs ) ;
int flashColour = getPreferredFlashColour ( notificationOrigin , prefs ) ;
int originalColour = getPreferredOriginalColour ( notificationOrigin , prefs ) ;
int flashDuration = getPreferredFlashDuration ( notificationOrigin , prefs ) ;
sendCustomNotification ( vibrateDuration , vibrateTimes , vibratePause , flashTimes , flashColour , originalColour , flashDuration , builder ) ;
} catch ( IOException ex ) {
LOG . error ( " Unable to send notification to MI device " , ex ) ;
}
}
private int getPreferredFlashDuration ( String notificationOrigin , SharedPreferences prefs ) {
return getNotificationPrefIntValue ( FLASH_DURATION , notificationOrigin , prefs , DEFAULT_VALUE_FLASH_DURATION ) ;
}
private int getPreferredOriginalColour ( String notificationOrigin , SharedPreferences prefs ) {
return getNotificationPrefIntValue ( FLASH_ORIGINAL_COLOUR , notificationOrigin , prefs , DEFAULT_VALUE_FLASH_ORIGINAL_COLOUR ) ;
}
private int getPreferredFlashColour ( String notificationOrigin , SharedPreferences prefs ) {
return getNotificationPrefIntValue ( FLASH_COLOUR , notificationOrigin , prefs , DEFAULT_VALUE_FLASH_COLOUR ) ;
}
private int getPreferredFlashCount ( String notificationOrigin , SharedPreferences prefs ) {
return getNotificationPrefIntValue ( FLASH_COUNT , notificationOrigin , prefs , DEFAULT_VALUE_FLASH_COUNT ) ;
}
private int getPreferredVibratePause ( String notificationOrigin , SharedPreferences prefs ) {
return getNotificationPrefIntValue ( VIBRATION_PAUSE , notificationOrigin , prefs , DEFAULT_VALUE_VIBRATION_PAUSE ) ;
}
private int getPreferredVibrateCount ( String notificationOrigin , SharedPreferences prefs ) {
return getNotificationPrefIntValue ( VIBRATION_COUNT , notificationOrigin , prefs , DEFAULT_VALUE_VIBRATION_COUNT ) ;
}
private int getPreferredVibrateDuration ( String notificationOrigin , SharedPreferences prefs ) {
return getNotificationPrefIntValue ( VIBRATION_DURATION , notificationOrigin , prefs , DEFAULT_VALUE_VIBRATION_DURATION ) ;
}
2015-04-14 01:24:03 +02:00
@Override
2015-04-19 02:37:29 +02:00
public void onSMS ( String from , String body ) {
2015-05-17 22:57:37 +02:00
// performCustomNotification("sms received", 500, 3, 2000, 0, 0, 0, 0);
performPreferredNotification ( " sms received " , ORIGIN_SMS ) ;
2015-04-19 02:37:29 +02:00
}
2015-04-14 01:24:03 +02:00
2015-04-19 02:37:29 +02:00
@Override
public void onEmail ( String from , String subject , String body ) {
2015-05-17 22:57:37 +02:00
performPreferredNotification ( " email received " , ORIGIN_K9MAIL ) ;
2015-04-14 01:24:03 +02:00
}
2015-05-13 21:55:22 +02:00
@Override
public void onGenericNotification ( String title , String details ) {
2015-05-17 22:57:37 +02:00
performPreferredNotification ( " generic notification received " , ORIGIN_GENERIC ) ;
2015-05-13 21:55:22 +02:00
}
2015-04-14 01:24:03 +02:00
@Override
public void onSetTime ( long ts ) {
2015-04-27 21:43:57 +02:00
try {
TransactionBuilder builder = performInitialized ( " Set date and time " ) ;
setCurrentTime ( builder ) ;
builder . queue ( getQueue ( ) ) ;
} catch ( IOException ex ) {
2015-05-12 06:28:11 +02:00
LOG . error ( " Unable to set time on MI device " , ex ) ;
2015-04-27 21:43:57 +02:00
}
}
/ * *
* Sets the current time to the Mi device using the given builder .
2015-05-01 01:49:43 +02:00
*
2015-04-27 21:43:57 +02:00
* @param builder
* /
private MiBandSupport setCurrentTime ( TransactionBuilder builder ) {
2015-04-26 00:53:48 +02:00
Calendar now = Calendar . getInstance ( ) ;
byte [ ] time = new byte [ ] {
( byte ) ( now . get ( Calendar . YEAR ) - 2000 ) ,
( byte ) now . get ( Calendar . MONTH ) ,
( byte ) now . get ( Calendar . DATE ) ,
( byte ) now . get ( Calendar . HOUR_OF_DAY ) ,
( byte ) now . get ( Calendar . MINUTE ) ,
( byte ) now . get ( Calendar . SECOND ) ,
( byte ) 0x0f ,
( byte ) 0x0f ,
( byte ) 0x0f ,
( byte ) 0x0f ,
( byte ) 0x0f ,
( byte ) 0x0f
} ;
2015-04-27 21:43:57 +02:00
BluetoothGattCharacteristic characteristic = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_DATE_TIME ) ;
if ( characteristic ! = null ) {
builder . write ( characteristic , time ) ;
} else {
2015-05-12 06:28:11 +02:00
LOG . info ( " Unable to set time -- characteristic not available " ) ;
2015-04-23 14:11:57 +02:00
}
2015-04-27 21:43:57 +02:00
return this ;
2015-04-14 01:24:03 +02:00
}
@Override
public void onSetCallState ( String number , String name , GBCommand command ) {
2015-04-19 15:15:53 +02:00
if ( GBCommand . CALL_INCOMING . equals ( command ) ) {
performDefaultNotification ( " incoming call " ) ;
}
2015-04-14 01:24:03 +02:00
}
@Override
public void onSetMusicInfo ( String artist , String album , String track ) {
2015-05-07 23:51:03 +02:00
// not supported
2015-04-14 01:24:03 +02:00
}
@Override
public void onFirmwareVersionReq ( ) {
2015-04-19 22:20:47 +02:00
try {
2015-04-19 22:31:09 +02:00
TransactionBuilder builder = performInitialized ( " Get MI Band device info " ) ;
2015-04-19 22:20:47 +02:00
BluetoothGattCharacteristic characteristic = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_DEVICE_INFO ) ;
builder . read ( characteristic ) . queue ( getQueue ( ) ) ;
} catch ( IOException ex ) {
2015-05-12 06:28:11 +02:00
LOG . error ( " Unable to read device info from MI " , ex ) ;
2015-04-19 22:20:47 +02:00
}
2015-04-14 01:24:03 +02:00
}
2015-04-19 22:31:09 +02:00
@Override
public void onBatteryInfoReq ( ) {
try {
TransactionBuilder builder = performInitialized ( " Get MI Band battery info " ) ;
2015-05-05 23:25:54 +02:00
requestBatteryInfo ( builder ) ;
builder . queue ( getQueue ( ) ) ;
2015-04-19 22:31:09 +02:00
} catch ( IOException ex ) {
2015-05-12 06:28:11 +02:00
LOG . error ( " Unable to read battery info from MI " , ex ) ;
2015-04-19 22:31:09 +02:00
}
}
2015-05-17 22:57:37 +02:00
@Override
public void onReboot ( ) {
try {
TransactionBuilder builder = performInitialized ( " Reboot " ) ;
builder . write ( getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_CONTROL_POINT ) , reboot ) ;
builder . queue ( getQueue ( ) ) ;
} catch ( IOException ex ) {
LOG . error ( " Unable to reboot MI " , ex ) ;
}
}
2015-04-14 01:24:03 +02:00
@Override
public void onAppInfoReq ( ) {
2015-05-07 23:51:03 +02:00
// not supported
2015-04-14 01:24:03 +02:00
}
2015-05-18 22:20:01 +02:00
@Override
public void onAppStart ( UUID uuid ) {
// not supported
}
2015-04-14 01:24:03 +02:00
@Override
2015-05-18 20:56:19 +02:00
public void onAppDelete ( UUID uuid ) {
2015-05-07 23:51:03 +02:00
// not supported
2015-04-14 01:24:03 +02:00
}
@Override
public void onPhoneVersion ( byte os ) {
2015-05-07 23:51:03 +02:00
// not supported
2015-04-14 01:24:03 +02:00
}
2015-04-19 02:37:29 +02:00
2015-04-19 22:20:47 +02:00
@Override
public void onCharacteristicRead ( BluetoothGatt gatt ,
2015-04-20 11:58:59 +02:00
BluetoothGattCharacteristic characteristic , int status ) {
2015-04-19 22:20:47 +02:00
super . onCharacteristicRead ( gatt , characteristic , status ) ;
2015-04-19 22:31:09 +02:00
UUID characteristicUUID = characteristic . getUuid ( ) ;
if ( MiBandService . UUID_CHARACTERISTIC_DEVICE_INFO . equals ( characteristicUUID ) ) {
2015-04-19 22:20:47 +02:00
handleDeviceInfo ( characteristic . getValue ( ) , status ) ;
2015-04-19 22:31:09 +02:00
} else if ( MiBandService . UUID_CHARACTERISTIC_BATTERY . equals ( characteristicUUID ) ) {
handleBatteryInfo ( characteristic . getValue ( ) , status ) ;
2015-04-19 22:20:47 +02:00
}
}
2015-04-19 02:37:29 +02:00
@Override
public void onCharacteristicWrite ( BluetoothGatt gatt ,
2015-04-19 11:28:03 +02:00
BluetoothGattCharacteristic characteristic , int status ) {
2015-04-19 02:37:29 +02:00
UUID characteristicUUID = characteristic . getUuid ( ) ;
if ( MiBandService . UUID_CHARACTERISTIC_PAIR . equals ( characteristicUUID ) ) {
handlePairResult ( characteristic . getValue ( ) , status ) ;
2015-04-19 15:11:50 +02:00
} else if ( MiBandService . UUID_CHARACTERISTIC_USER_INFO . equals ( characteristicUUID ) ) {
handleUserInfoResult ( characteristic . getValue ( ) , status ) ;
2015-04-19 02:37:29 +02:00
}
}
2015-04-19 22:31:09 +02:00
private void handleDeviceInfo ( byte [ ] value , int status ) {
if ( status = = BluetoothGatt . GATT_SUCCESS ) {
DeviceInfo info = new DeviceInfo ( value ) ;
getDevice ( ) . setFirmwareVersion ( info . getFirmwareVersion ( ) ) ;
getDevice ( ) . sendDeviceUpdateIntent ( getContext ( ) ) ;
}
}
private void handleBatteryInfo ( byte [ ] value , int status ) {
if ( status = = BluetoothGatt . GATT_SUCCESS ) {
BatteryInfo info = new BatteryInfo ( value ) ;
getDevice ( ) . setBatteryLevel ( ( short ) info . getLevelInPercent ( ) ) ;
getDevice ( ) . setBatteryState ( info . getStatus ( ) ) ;
getDevice ( ) . sendDeviceUpdateIntent ( getContext ( ) ) ;
}
}
2015-04-19 15:11:50 +02:00
private void handleUserInfoResult ( byte [ ] value , int status ) {
// successfully transfered user info means we're initialized
if ( status = = BluetoothGatt . GATT_SUCCESS ) {
setConnectionState ( State . INITIALIZED ) ;
}
}
private void setConnectionState ( State newState ) {
getDevice ( ) . setState ( newState ) ;
getDevice ( ) . sendDeviceUpdateIntent ( getContext ( ) ) ;
}
2015-04-19 02:37:29 +02:00
private void handlePairResult ( byte [ ] pairResult , int status ) {
if ( status ! = BluetoothGatt . GATT_SUCCESS ) {
2015-05-12 06:28:11 +02:00
LOG . info ( " Pairing MI device failed: " + status ) ;
2015-04-19 02:37:29 +02:00
return ;
}
2015-05-12 20:32:34 +02:00
String value = null ;
2015-04-19 02:37:29 +02:00
if ( pairResult ! = null ) {
if ( pairResult . length = = 1 ) {
try {
2015-05-12 20:32:34 +02:00
if ( pairResult [ 0 ] = = 2 ) {
2015-05-12 06:28:11 +02:00
LOG . info ( " Successfully paired MI device " ) ;
2015-04-19 02:37:29 +02:00
return ;
}
} catch ( Exception ex ) {
2015-05-12 06:28:11 +02:00
LOG . warn ( " Error identifying pairing result " , ex ) ;
2015-04-19 02:37:29 +02:00
return ;
}
}
2015-05-12 20:32:34 +02:00
value = Arrays . toString ( pairResult ) ;
2015-04-19 02:37:29 +02:00
}
2015-05-12 06:28:11 +02:00
LOG . info ( " MI Band pairing result: " + value ) ;
2015-04-19 02:37:29 +02:00
}
2015-04-14 01:24:03 +02:00
}