15 蓝牙基础API - MiEcosystem/NewXmPluginSDK GitHub Wiki

蓝牙基础API


米家扩展程序开发注意事项

  • 一定要在后台配置好设备的配对方式和绑定关系,有必要的需要上传配对引导图。
  • minApiLevel不要设置得过低,米家扩展程序如果调用了某个高版本的API,但是minApiLevel设置的低于这个版本,则在低版本米家APP中会崩溃
  • 建议米家扩展程序中使用米家提供的接口与设备通信,不要使用Android原生蓝牙接口

米家扩展程序安全连接注意事项

  • 不要重复多次调用安全连接,在上一次回调还没回来之前不要再次调用
  • 如果正在连接的时候退出米家扩展程序了,要立即断开连接,不要设置保持时间
  • 建议米家扩展程序中要断线自动重连,只有重连三次失败时才提示用户
  • 当token不匹配时调用removeToken清除token
  • 退出米家扩展程序时要断开连接,可指定一个延时

常见问题

  • 如果米家扩展程序下载到100%后没反应,可能是米家扩展程序包有问题,检查minSdkLevel是否过高,高于当前手机level,导致getPackageArchiveInfo时返回null

蓝牙米家扩展程序Demo

参考蓝牙Demo工程

1、设备连接

符合MiService协议的设备要走安全连接,如果走普通连接会超时自动断开。

普通连接

/**
 * ApiLevel: 15
 */
XmBluetoothManager.getInstance().connect(mac, new Response.BleConnectResponse() {
    @Override
    public void onResponse(int code, Bundle data) {
        if (code == Code.REQUEST_SUCCESS) {
            
        } else {
            
        }
    }
});

安全连接

回调中会带上本地设备的token。
/**
 * ApiLevel: 20
 */
XmBluetoothManager.getInstance().secureConnect(mac, new Response.BleConnectResponse() {
    @Override
    public void onResponse(int code, Bundle data) {
        if (code == Code.REQUEST_SUCCESS) {
            byte[] token = data.getByteArray(XmBluetoothManager.EXTRA_TOKEN);
        } else {
            
        }
        
    }
});
如果安全连接返回的code为TOKEN_NOT_MATCHED,则调用以下接口清除本地token。
/**
 * ApiLevel: 31
 */
XmBluetoothManager.getInstance().removeToken(mac)

长连接

只在MIUI上支持,如果连接失败,则会隔一段时间尝试重连,如果继续失败,则重连间隔会翻倍,直到上限。
/**
 * ApiLevel: 21
 */
void bindDevice(String mac); // 维持长连接
/**
 * ApiLevel: 21
 */
void unBindDevice(String mac); // 解除长连接
支持提醒功能,包括来电,短信或闹钟时通知设备。
/**
 * ApiLevel: 35
 */
public static final int ALERT_INCALL_IN_CONTACTS_ENABLED = 1; // 联系人来电提醒
public static final int ALERT_INCALL_NO_CONTACTS_ENABLED = 2; // 非联系人来电提醒
public static final int ALERT_ALARM_ENABLED = 3;   // 闹钟提醒
public static final int ALERT_SMS_IN_CONTACTS_ENABLED = 4; // 联系人短信提醒
public static final int ALERT_SMS_NO_CONTACTS_ENABLED = 5; // 非联系人短信提醒

public abstract boolean setAlertConfigs(String mac, int alert, boolean enable);

断开连接

退出米家扩展程序时要主动断开设备连接,可以指定一个延时。
/**
 * ApiLevel:15
 */
XmBluetoothManager.getInstance().disconnect(mac);
/**
 * ApiLevel:22
 */
XmBluetoothManager.getInstance().disconnect(mac, 10000);

获取连接状态

/**
 * ApiLevel: 32
 */
int connState = XmBluetoothManager.getInstance().getConnectStatus(mac);
// XmBluetoothManager.STATE_UNKNOWN
// XmBluetoothManager.STATE_CONNECTED
// XmBluetoothManager.STATE_CONNECTING
// XmBluetoothManager.STATE_DISCONNECTED
// XmBluetoothManager.STATE_DISCONNECTING

2、读取设备

/**
 * ApiLevel: 15
 */
XmBluetoothManager.getInstance().read(mac, serviceUUID, characterUUID, new Response.BleReadResponse() {
    @Override
    public void onResponse(int code, byte[] bytes) {
        if (code == Code.REQUEST_SUCCESS) {

        } else {

        }
    }
});

3、写设备

/**
 * ApiLevel: 15
 */
XmBluetoothManager.getInstance().write(mac, serviceUUID, characterUUID, bytes, new Response.BleWriteResponse() {

    @Override
    public void onResponse(int code, Void data) {

    }
});
如果希望蓝牙写时带WRITE_TYPE_NO_RESPONSE标志,则用如下接口
/**
 * ApiLevel: 31
 */
XmBluetoothManager.getInstance().writeNoRsp(mac, serviceUUID, characterUUID, bytes, new Response.BleWriteResponse() {

    @Override
    public void onResponse(int code, Void data) {

    }
});

4、设备通知

打开notify成功后,参考第六条监听notify广播。
/**
 * ApiLevel: 15
 */
XmBluetoothManager.getInstance().notify(mac, serviceUUID, characterUUID, new Response.BleNotifyResponse() {
    @Override
    public void onResponse(int code, Void data) {
        
    }
});

/**
 * ApiLevel: 15
 */
XmBluetoothManager.getInstance().unnotify(mac, serviceUUID, characterUUID);

5、读取RSSI

/**
 * ApiLevel: 15
 */
XmBluetoothManager.getInstance().readRemoteRssi(mac, new Response.BleReadRssiResponse() {
    @Override
    public void onResponse(int code, Integer rssi) {
        
    }
});

6、状态通知

这里可监听连接、notify和写状态。
IntentFilter filter = new IntentFilter(XmBluetoothManager.ACTION_CHARACTER_CHANGED);
filter.addAction(XmBluetoothManager.ACTION_CONNECT_STATUS_CHANGED);
filter.addAction(XmBluetoothManager.ACTION_CHARACTER_WRITE);
registerReceiver(mReceiver, filter);

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent == null) {
            return;
        }

        String action = intent.getAction();
        String mac = intent.getStringExtra(XmBluetoothManager.KEY_DEVICE_ADDRESS);
        // 收到数据后需要先判断下是不是自己设备发送过来的
        if (TextUtils.equals(mac, mDevice.getMac())) {
            if (XmBluetoothManager.ACTION_CHARACTER_CHANGED.equalsIgnoreCase(action)) {
                UUID service = (UUID) intent.getSerializableExtra(XmBluetoothManager.KEY_SERVICE_UUID);
                UUID character = (UUID) intent.getSerializableExtra(XmBluetoothManager.KEY_CHARACTER_UUID);
                byte[] value = intent.getByteArrayExtra(XmBluetoothManager.KEY_CHARACTER_VALUE);
                processNotify(service, character, value);
            } else if (XmBluetoothManager.ACTION_CONNECT_STATUS_CHANGED.equalsIgnoreCase(action)) {
                int status = intent.getIntExtra(XmBluetoothManager.KEY_CONNECT_STATUS, XmBluetoothManager.STATUS_UNKNOWN);
                if (status == XmBluetoothManager.STATUS_CONNECTED) {

                } else if (status == XmBluetoothManager.STATUS_DISCONNECTED) {

                }
                processConnectStatusChanged(status);
            } else if (XmBluetoothManager.ACTION_CHARACTER_WRITE.equalsIgnoreCase(action)) {
                UUID service = (UUID) intent.getSerializableExtra(XmBluetoothManager.KEY_SERVICE_UUID);
                UUID character = (UUID) intent.getSerializableExtra(XmBluetoothManager.KEY_CHARACTER_UUID);
                byte[] value = intent.getByteArrayExtra(XmBluetoothManager.KEY_CHARACTER_VALUE);
                int status = intent.getIntExtra(XmBluetoothManager.KEY_CHARACTER_WRITE_STATUS, XmBluetoothManager.STATUS_UNKNOWN);
                processCharacterWrited(status);
            }
        }
    }
};

7、设备更名

支持同步的设备更名后会同步到云端,否则只是保存在本地。传入设备mac或did皆可。
/**
 * ApiLevel: 21
 */
XmBluetoothManager.getInstance().deviceRename(mac, name);
米家扩展程序中设置页中重命名后会发送广播通知结果,米家扩展程序可监听如下广播:
private class PluginReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent != null && "action.more.rename".equals(intent.getAction())) {
            String name = intent.getStringExtra("name");
            int result = intent.getIntExtra("result", 0);
        }
    }
}

8、数据上报

上报数据都是封装成XmBluetoothRecord,里面包括type, key, value。其中type只能为TYPE_PROP或TYPE_EVENT。支持多条数据同时上报,回调每条数据的上报结果。
List<XmBluetoothRecord> records = new ArrayList<XmBluetoothRecord>();
XmBluetoothRecord record = new XmBluetoothRecord();
record.type = XmBluetoothRecord.TYPE_PROP;
record.key = "color";
record.value = "red";
record.trigger = null;
records.add(record);

/**
 * ApiLevel: 20
 */
XmPluginHostApi.instance().reportBluetoothRecords(did, model, records, new Callback<List<Boolean>>() {

    @Override
    public void onSuccess(List<Boolean> booleans) {

    }

    @Override
    public void onFailure(int i, String s) {

    }
};

9、更改副标题

/**
 * ApiLevel: 20
 */
XmPluginHostApi.instance().setBleDeviceSubtitle(mac, subtitle);

10、开关蓝牙

打开蓝牙,需要询问用户

/**
 * ApiLevel: 15
 */
XmBluetoothManager.getInstance().openBluetooth(context);

静默打开蓝牙

/**
 * ApiLevel: 15
 */
XmBluetoothManager.getInstance().openBluetoothSilently();

判断蓝牙是否打开

/**
 * ApiLevel: 15
 */
XmBluetoothManager.getInstance().isBluetoothOpen();

11、扫描蓝牙设备

扫描经典蓝牙或BLE蓝牙

/**
 * ApiLevel: 25
 * durationInMillis为扫描持续时间
 * type: XmBluetoothManager.SCAN_CLASSIC 扫描经典蓝牙,type: XmBluetoothManager.SCAN_BLE 扫描BLE蓝牙
 */
XmBluetoothManager.getInstance().startScan(durationInMillis, type, new XmBluetoothManager.BluetoothSearchResponse() {
	@Override
	public void onSearchStarted() {

	}

	@Override
	public void onDeviceFounded(XmBluetoothDevice xmBluetoothDevice) {

	}

	@Override
	public void onSearchStopped() {

	}

	@Override
	public void onSearchCanceled() {

	}
});

扫描BLE蓝牙

/**
 * ApiLevel: 33
 * durationInMillis为扫描持续时间
 * serviceUuids为要过滤的service uuid
 */
XmBluetoothManager.getInstance().startLeScan(durationInMillis, serviceUuids, new XmBluetoothManager.BluetoothSearchResponse() {
	@Override
	public void onSearchStarted() {

	}

	@Override
	public void onDeviceFounded(XmBluetoothDevice xmBluetoothDevice) {

	}

	@Override
	public void onSearchStopped() {

	}

	@Override
	public void onSearchCanceled() {

	}
});

停止蓝牙扫描

/**
 * ApiLevel: 25
 */
XmBluetoothManager.getInstance().stopScan();

12、注册MediaButton广播

注册MediaButton广播接收器

/**
 * ApiLevel: 25
 * IXmPluginMessageReceiver中,MSG_BROADCAST返回MediaButton事件
 */
XmBluetoothManager.getInstance().registerMediaButtonReceiver(model);

取消注册MediaButton广播接收器

/**
 * ApiLevel: 25
 */
XmBluetoothManager.getInstance().unRegisterMediaButtonReceiver(model);

13、后台接收蓝牙数据

注册蓝牙数据监听器

/**
 * ApiLevel: 45
 * 监听底层ble蓝牙接收到的数据(就算退出米家扩展程序也可以收到数据),监听到的数据主进程通过发送{IXmPluginMessageReceiver.MSG_BLE_CHARACTER_CHANGED}给米家扩展程序
 * registerCharacterChanged不会主动使能Characteristic的notify接口,必须米家扩展程序主动调用notify
 */
XmBluetoothManager.getInstance().registerCharacterChanged(mac, serviceId, characterId, new Response.BleWriteResponse() {

	@Override
	public void onResponse(int code, Void aVoid) {
		
	}
});

取消蓝牙数据监听器

/**
 * ApiLevel: 45
 * 取消registerCharacterChanged注册的监听器
 */
XmBluetoothManager.getInstance().unregisterCharacterChanged(mac);

14、判断设备是否通过蓝牙网关扫描到

/**
 * ApiLevel: 55
 * 判断当前设备是否通过蓝牙网关扫描到了
 * response code = 0 : 网关扫描到了设备
 *          code != 0, 网关没有扫描到设备
 */
public abstract void isBleGatewayConnected(String mac, Response.BleResponse<Void> response);
⚠️ **GitHub.com Fallback** ⚠️