蓝牙 Mesh(涂鸦)

更新时间:2023-06-01 06:18:29下载pdf

通俗地讲,蓝牙 Mesh 就是把多个蓝牙单点设备组成一个 Mesh 网络,每个节点可以和别的节点自由通讯。通过手机直连 Mesh 网中任意一个设备,即能访问控制 Mesh 网中所有的设备。本文介绍涂鸦自研的蓝牙拓扑通信相关实现,以及设备的配网、控制、管理等开发指导。更多有关蓝牙 Mesh 的概念介绍,请参考 蓝牙 Mesh(SIG)

准备工作

  • 手机系统要求:蓝牙使用需要安卓 4.3 以及以上版本,智能生活 App SDK 从安卓 4.4 开始支持。
  • Manifest 权限
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    
  • 蓝牙权限检测:每次扫描和连接前,都要进行检测,否则 App 无法正常使用蓝牙。
    • App 在使用蓝牙连接或者扫描操作前,需要检查 App 定位权限是否被允许。
    • 检查蓝牙状态是否为开启。

      该部分检查逻辑,智能生活 SDK 未提供 API,您可自行检测。

  • 初始化家庭:开发蓝牙 Mesh 时,请先熟悉智能生活 App SDK。蓝牙 Mesh 中所有操作都建立在家庭数据已经初始化的基础上。完全初始化家庭操作,请参考 家庭管理

管理

创建蓝牙 Mesh

一个家庭里可以拥有多个 Mesh,但建议只创建一个 Mesh。

接口说明

void createBlueMesh(String meshName, ITuyaResultCallback<BlueMeshBean> callback);

参数说明

参数 类型 说明
meshName String Mesh 的名称,不能超过 16 字节
callback ITuyaResultCallback 回调

示例代码

TuyaHomeSdk.newHomeInstance("homeId").createBlueMesh("meshName", new ITuyaResultCallback<BlueMeshBean>() {
	@Override
	public void onError(String errorCode, String errorMsg) {
	}

	@Override
	public void onSuccess(BlueMeshBean blueMeshBean) {
	}
});

删除蓝牙 Mesh

接口说明

void removeMesh(IResultCallback callback);

参数说明

参数 类型 说明
callback IResultCallback 回调

示例代码

TuyaHomeSdk.newBlueMeshDeviceInstance(meshId).removeMesh(new IResultCallback() {
	@Override
	public void onError(String errorCode, String errorMsg) {
	}

	@Override
	public void onSuccess() {
	}
});

查询家庭下的 Mesh 列表

接口说明

List<BlueMeshBean> getMeshList();

示例代码

ITuyaHome mTuyaHome = TuyaHomeSdk.newHomeInstance("homeId");
if (mTuyaHome.getHomeBean() != null){
	List<BlueMeshBean> meshList = mTuyaHome.getHomeBean().getMeshList();
	BlueMeshBean meshBean= meshList.get(0);
}

查询 Mesh 子设备列表

接口说明

List<DeviceBean> getMeshSubDevList();

示例代码

List<DeviceBean> meshSubDevList = TuyaHomeSdk.newBlueMeshDeviceInstance("meshId").getMeshSubDevList();

查询 Mesh 实例和销毁

建议在切换家庭时,销毁当前 Mesh,然后重新初始化家庭中的 Mesh。

接口说明

//初始化 Mesh
TuyaHomeSdk.getTuyaBlueMeshClient().initMesh(String meshId);

//销毁当前 Mesh
TuyaHomeSdk.getTuyaBlueMeshClient().destroyMesh();

连接和断开 Mesh 子设备

ITuyaBlueMeshClient 提供开始连接、断开连接、开启扫描、停止扫描等接口。

接口说明

// 开启连接
TuyaHomeSdk.getTuyaBlueMeshClient().startClient(mBlueMeshBean);

//断开连接
TuyaHomeSdk.getTuyaBlueMeshClient().stopClient();

//开启扫描
TuyaHomeSdk.getTuyaBlueMeshClient().startSearch()

//停止扫描
TuyaHomeSdk.getTuyaBlueMeshClient().stopSearch();

开启连接后,会在应用后台不断的去扫描周围可连接设备,直到连接成功为止。后台一直扫描会消耗资源,可以通过开启扫描和停止扫描来控制后台的扫描:

  • 当未开启连接(startClient())时,调用 startSearch()stopSearch() 属于无效操作。
  • 当已经连接到 Mesh 的时候,调用 startSearch()stopSearch() 也属于无效操作。

配网

Mesh 配网主要分为两种:

  • 普通蓝牙 Mesh 设备,又称 Mesh 子设备

  • Mesh 网关配网

    常见的子设备有智能灯、智能插座、低功耗产品等。只要不带 Mesh 网关,都可以等同于普通蓝牙 Mesh 设备。

重置设备

处于重置状态的设备,默认名字为 out_of_mesh,默认密码为 123456。重置设备可以通过以下常见方式操作:

产品类型 重置操作 可配网现象
智能灯 连续开关三次 灯快闪
智能插座 长按开关 3s 插座指示灯快闪
网关产品 长按开关 3s 红灯和蓝灯快闪
低功耗设备 长按开关 3s 再按一次出现长亮即可配网,且配网需在灯亮期间完成
报警器 长按开关 3s 灯快闪

扫描待配网子设备

扫描前需要检查蓝牙和位置权限,扫描附近的待配网 Mesh 设备。

接口说明

//开启扫描
void startSearch();
//停止扫描
void stopSearch();

示例代码

ITuyaBlueMeshSearchListener iTuyaBlueMeshSearchListener = new ITuyaBlueMeshSearchListener() {
	@Override
	public void onSearched(SearchDeviceBean deviceBean) {
	}

	@Override
	public void onSearchFinish() {
	}
};

SearchBuilder searchBuilder = new SearchBuilder()
				//要扫描设备的名称(默认会是 out_of_mesh,设备处于配网状态下的名称)
				.setMeshName("out_of_mesh")
				.setTimeOut(100) //扫描时长 单位秒
				.setTuyaBlueMeshSearchListener(iTuyaBlueMeshSearchListener).build();

ITuyaBlueMeshSearch mMeshSearch = TuyaHomeSdk.getTuyaBlueMeshConfig().newTuyaBlueMeshSearch(searchBuilder);

//开启扫描
mMeshSearch.startSearch();

//停止扫描
mMeshSearch.stopSearch();

Mesh 子设备配网

接口说明

//开启配网
void startActivator();

//停止配网
void stopActivator();

参数说明

参数 String 说明
mSearchDeviceBeans List 待配网的设备集合
timeout Int 配网的超时时间设置,默认是 100s
ssid String 配网之后,设备连接的 Wi-Fi 的名称
password String 配网之后,设备连接的 Wi-Fi 的密码
mMeshBean MeshBean MeshBean
homeId Long 设备要加入的 Mesh 网的所属家庭
version String
  • 普通设备配网:1.0
  • 网关配网:2.2

示例代码

//普通设备入网
TuyaBlueMeshActivatorBuilder tuyaBlueMeshActivatorBuilder = new TuyaBlueMeshActivatorBuilder()
				.setSearchDeviceBeans(foundDevices)
				.setVersion("1.0")
				.setBlueMeshBean(mMeshBean)
				.setTimeOut(timeOut)
				.setTuyaBlueMeshActivatorListener(new ITuyaBlueMeshActivatorListener() {
	@Override
	public void onSuccess(DeviceBean deviceBean) {
	}

	@Override
	public void onError(String errorCode, String errorMsg) {
	}

	@Override
	public void onFinish() {
	}});

ITuyaBlueMeshActivator iTuyaBlueMeshActivator = TuyaHomeSdk.getTuyaBlueMeshConfig().newActivator(tuyaBlueMeshActivatorBuilder);
//开始入网
iTuyaBlueMeshActivator.startActivator();

//停止入网
iTuyaBlueMeshActivator.stopActivator();

Mesh 网关配网

接口说明

//开启配网
void startActivator();
//停止配网
void stopActivator();

参数说明

参数 String 说明
mSearchDeviceBeans List 待配网的设备集合
timeout Int 配网的超时时间设置,默认值:100s
ssid String 配网之后,设备连接的 Wi-Fi 的名称
password String 配网之后,设备连接的 Wi-Fi 的密码
mMeshBean MeshBean MeshBean
homeId Long 设备要加入的 Mesh 网的所属家庭
version String
  • 普通设备配网:1.0
  • 网关配网:2.2

示例代码

//网关入网
TuyaBlueMeshActivatorBuilder tuyaBlueMeshActivatorBuilder = new TuyaBlueMeshActivatorBuilder()
				.setWifiSsid(mSsid)
				.setWifiPassword(mPassword)
				.setSearchDeviceBeans(foundDevices)
				.setVersion("2.2 ")
				.setBlueMeshBean(mMeshBean)
				.setHomeId("homeId")
				.setTuyaBlueMeshActivatorListener(new ITuyaBlueMeshActivatorListener() {
				@Override
				public void onSuccess(DeviceBean devBean) {
					//单个设备配网成功回调
					L.d(TAG, "startConfig success");
				}

				@Override
				public void onError(String errorCode, String errorMsg) {
					//单个设备配网失败回调
					//errorCode 见错误码
				L.d(TAG, "errorCode: " + errorCode + " errorMsg: " + errorMsg);
				}

				@Override
				public void onFinish() {
					//所有设备配网结束回调
				}
				});
ITuyaBlueMeshActivator iTuyaBlueMeshActivator = TuyaHomeSdk.getTuyaBlueMeshConfig().newWifiActivator(tuyaBlueMeshActivatorBuilder);

//开始入网
iTuyaBlueMeshActivator.startActivator();

//停止入网
//iTuyaBlueMeshActivator.stopActivator();

配网错误码

错误码 说明
13007 登录设备失败
13004 重置设备地址失败
13005 设备地址已满
13007 SSID 为空
13011 配网超时

设备

判断 Mesh 设备类型

接口说明

DeviceBean deviceBean=TuyaHomeSdk.getDataInstance().getDeviceBean(mDevId);
// 蓝牙 Mesh 设备判断 (子设备+网关)
if(deviceBean.isBlueMesh()){
	L.d(TAG, "This device is blue mesh device");
}

// 蓝牙 Mesh 网关设备判断
if(deviceBean.isBlueMeshWifi()){
	L.d(TAG, "This device is blue mesh wifi device");
}

重命名 Mesh 子设备

接口说明

void renameMeshSubDev(String devId, String name, IResultCallback callback);

参数说明

参数 类型 说明
devId String 设备 ID
name String 设备新名称
callback IResultCallback 回调

示例代码

mTuyaBlueMesh.renameMeshSubDev(devBean.getDevId(),"设备名称", new IResultCallback() {
			@Override
			public void onError(String code, String errorMsg) {
			}

			@Override
			public void onSuccess() {
			}
		});

判断 Mesh 本地连接和网关连接

示例代码

DeviceBean deviceBean=TuyaHomeSdk.getDataInstance().getDeviceBean(mDevId);
DeviceBean gwBean=TuyaHomeSdk.getDataInstance().getDeviceBean(deviceBean.getParentId());

//综合在线状态 (包括本地在线和网关在线)
boolean online=deviceBean.getIsOnline()
//设备本地蓝牙在线状态
boolean localOnline=deviceBean.getIsLocalOnline()
//设备网关在线状态 (需要网关在线且子设备在线 才认为网关真实在线)
boolean wifiOnline=deviceBean.isCloudOnline() && gwBean.getIsOnline()

移除 Mesh 子设备

接口说明

void removeMeshSubDev(String devId, IResultCallback callback);

参数说明

参数 类型 说明
devId String 设备 ID
pcc String 设备大小类
callback IResultCallback 回调

示例代码

mTuyaBlueMesh.removeMeshSubDev(devBean.getDevId(), new IResultCallback(){
@Override
public void onError(String code, String errorMsg) {
	Toast.makeText(mContext, "子设备移除失败 "+ errorMsg,   Toast.LENGTH_LONG).show();
}

@Override
public void onSuccess() {
	Toast.makeText(mContext, "子设备移除成功", Toast.LENGTH_LONG).show();
}
});

查询 Mesh 子设备状态

云端查询到的 DP 数据可能不是当前设备实时的数据。您可以通过该命令去查询设备的当前数据值,结果通过 IMeshDevListeneronDpUpdate 方法返回。

接口说明

void querySubDevStatusByLocal(String pcc, String nodeId, IResultCallback callback);

参数说明

参数 类型 说明
pcc String 设备大小类
nodeId String 设备 NodeId
callback IResultCallback 回调

示例代码

mTuyaBlueMeshDevice.querySubDevStatusByLocal(devBean.getCategory(), devBean.getNodeId(), new IResultCallback() {
			@Override
			public void onError(String code, String errorMsg) {
			}

			@Override
			public void onSuccess() {
			}
		});

群组

ITuyaGroup 类提供了对 Mesh 群组的操作。

判断是否为 Mesh 群组

可以通过群组中是否具备 MeshId 来区分 Mesh 群组和普通 Wi-Fi 群组。

接口说明

GroupBean groupBean=TuyaHomeSdk.getDataInstance().getGroupBean("groupId");
if(!TextUtils.isEmpty(groupBean.getMeshId())){
	L.d(TAG, "This group is a Bluetooth mesh group");
}

创建 Mesh 群组

Mesh 网内支持创建 28672 个群组,返回时 ID 范围为 16 进制的 8000 ~ EFFF,由本地进行维护。

接口说明

void addGroup(String name, String pcc, String localId,IAddGroupCallback callback);

参数说明

参数 说明
name 群组名称
pcc 群组中设备的大小类,支持跨小类创建,FF01 表示覆盖灯大类
localId 群组的 LocalId,范围为 16 进制的 8000 ~ EFFF
callback 回调

示例代码

mITuyaBlueMesh.addGroup("群组名称","大小类", "8001", new IAddGroupCallback() {
@Override
public void onError(String errorCode, String errorMsg) {
}

@Override
public void onSuccess(long groupId) {
}
});

添加子设备到群组

接口说明

void addDevice(String devId,IResultCallback callback);

参数说明

参数 类型 说明
devId String 设备 ID
callback IResultCallback 回调

示例代码

ITuyaGroup mGroup = TuyaHomeSdk.newBlueMeshGroupInstance(groupId);
mGroup.addDevice("devId", new IResultCallback() {
@Override
public void onError(String code, String errorMsg) {
}

@Override
public void onSuccess() {
}
});

从群组移除子设备

接口说明

void removeDevice(String devId,IResultCallback callback);

参数说明

参数 类型 说明
devId String 设备 ID
callback IResultCallback 回调

示例代码

ITuyaGroup mGroup = TuyaHomeSdk.newBlueMeshGroupInstance(groupId);
mGroup.removeDevice("devId", new IResultCallback() {
@Override
public void onError(String code, String errorMsg) {
}

@Override
public void onSuccess() {
}
});

删除 Mesh 群组

接口说明

void dismissGroup(IResultCallback callback);

参数说明

参数 类型 说明
callback IResultCallback 回调

接口说明

ITuyaGroup mGroup = TuyaHomeSdk.newBlueMeshGroupInstance(groupId);
mGroup.dismissGroup(new IResultCallback() {
@Override
public void onError(String code, String errorMsg) {
}

@Override
public void onSuccess() {
}
});

重命名 Mesh 群组

接口说明

void renameGroup(String groupName,IResultCallback callback);

参数说明

参数 类型 说明
groupName String 新的群组名称
callback IResultCallback 回调

示例代码

ITuyaGroup mGroup = TuyaHomeSdk.newBlueMeshGroupInstance(groupId);
mGroup.renameGroup("群组名称",new IResultCallback() {
@Override
public void onError(String code, String errorMsg) {
}

@Override
public void onSuccess() {
}
});

控制

由于涂鸦使用 DP 管理设备的功能,因此,蓝牙 Mesh 相关的控制是根据设备的 DP 信息来进行操作。ITuyaBlueMeshDevice 类提供了对 Mesh 设备的操作。

指令格式

发送控制指令按照以下格式:

{
	"(dpId)":"(dpValue)"
}

DP 指令详情,请参考 设备控制

控制设备

接口说明

void publishDps(String nodeId, String pcc, String dps, IResultCallback callback);

参数说明

参数 说明
nodeId 子设备本地编号
pcc 设备产品大小类
dps 控制指令
callback 回调

示例代码

String dps = {"1":false};
ITuyaBlueMeshDevice mTuyaBlueMeshDevice=TuyaHomeSdk.newBlueMeshDeviceInstance("meshId");
mTuyaBlueMeshDevice.publishDps(devBean.getNodeId(), devBean.getCategory(), dps, new IResultCallback() {
@Override
public void onError(String s, String s1) {
}

@Override
public void onSuccess() {
}
});

控制群组

接口说明

void multicastDps(String localId, String pcc, String dps, IResultCallback callback)

参数说明

参数 说明
localId 群组本地编号
pcc 设备产品大小类
dps 控制指令
callback 回调

示例代码

String dps = {"1":false};
ITuyaBlueMeshDevice mTuyaBlueMeshDevice= TuyaHomeSdk.newBlueMeshDeviceInstance("meshId");
mTuyaBlueMeshDevice.multicastDps(groupBean.getLocalId(), devBean.getCategory(), dps, new IResultCallback() {
@Override
public void onError(String errorCode, String errorMsg) {
}

@Override
public void onSuccess() {
}
});

数据监听

Mesh 网内相关信息,例如 DP 数据、状态变更、设备名称、设备移除等,会实时同步到 IMeshDevListener

示例代码

mTuyaBlueMeshDevice.registerMeshDevListener(new IMeshDevListener() {

/**
	* 数据更新
	* @param nodeId    更新设备的 nodeId
	* @param dps       dp 数据
	* @param isFromLocal   数据来源 true 表示从本地蓝牙  false 表示从云端
	*/
@Override
public void onDpUpdate(String nodeId, String dps,boolean isFromLocal) {
//可以通过 node 来找到相对应的 DeviceBean
DeviceBean deviceBean = mTuyaBlueMeshDevice.getMeshSubDevBeanByNodeId(nodeId);
}

/**
	* 设备状态的上报
	* @param online    在线设备列表
	* @param offline   离线设备列表
	* @param gwId      状态的来源 gwId 不为空表示来自云端(gwId 是上报数据的网关 Id)   为空则表示来自本地蓝牙
	*/
@Override
public void onStatusChanged(List<String> online, List<String> offline,String gwId) {
}

/**
	* 网络状态变化
	* @param devId
	* @param status
	*/
@Override
public void onNetworkStatusChanged(String devId, boolean status) {

}

/**
	* raw 类型数据上报
	* @param bytes
	*/
@Override
public void onRawDataUpdate(byte[] bytes) {

}

/**
	* 设备信息变更(名称等)
	* @param bytes
	*/
@Override
public void onDevInfoUpdate(String devId) {
}
/**
	* 设备移除
	* @param devId
	*/
@Override
public void onRemoved(String devId) {
}
});

升级

涂鸦 Mesh 设备的固件升级与蓝牙 Mesh 逻辑一致,详情请参考 蓝牙 Mesh 固件升级 章节。