蓝牙 Mesh (涂鸦)

更新时间:2022-02-17 07:06:03下载pdf

涂鸦蓝牙有三条技术线路,即蓝牙设备与手机一对一相连的蓝牙单点设备、涂鸦自研的蓝牙拓扑通信 涂鸦 Mesh 、蓝牙技术联盟发布的蓝牙拓扑通信 蓝牙 Mesh。本文主要介绍涂鸦 Mesh 相关设备的配网、控制、管理等开发指导。

适用场景

不同的智能产品适用的蓝牙类型如下所示。

分类 产品举例
蓝牙单点 体脂秤、手环、温控器、电动牙刷、门锁等
蓝牙 Mesh 一路、二路、五路等灯泡、插座、传感器等蓝牙 Mesh 子设备
涂鸦 Mesh 与蓝牙 Mesh 产品类似,协议为涂鸦智能自研
双模设备 蓝牙 Mesh 网关、IPC 设备以及新版多协议 Wi-Fi 设备等

除此之外,还有一些多协议设备也会使用到蓝牙技术,例如同时具备 Wi-Fi 能力和蓝牙能力的 双模设备,也可以使用蓝牙进行配网,当然 Wi-Fi 设备原本的配网仍然可用。双模配网的蓝牙配网部分,使用的是蓝牙单点技术为设备配网。详情请参考 蓝牙单点

开发蓝牙 Mesh 功能前,请先熟悉智能生活 App SDK。Mesh 的所有操作都建立在站点数据已经初始化的基础上。一个站点里可以拥有多个 Mesh,但建议只创建一个 Mesh。

准备工作

  • 系统要求:蓝牙使用需要安卓 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 定位权限是否被允许。

    • 检查蓝牙状态是否为开启。

      该部分检查逻辑,智能生活 SDK 未提供 API,您可自行检测。每次扫描和连接前都要进行检测,否则 App 无法正常使用蓝牙。

基础概念

产品大小类

Mesh 产品目前分为:

  • 五大类:
    • 灯大类(01):1-5 路 RGBWC 彩灯
    • 电工类(02):1-6 路插座
    • 传感器类(04):门磁传感器、被动红外(PIR)传感器等
    • 执行器类(10):马达、报警器之类用于执行的设备
    • 适配器(08):网关(带有蓝牙 Mesh 及其他通信节点的适配器)
  • 对应的小类编号:
    • 1-5 路灯(01-05)
    • 1-6 路排插(01-06)
  • 设备类别值举例:
    • 四路灯:0401
    • 五路插座:0502

Mesh 相关名词

相关名词 说明
NodeId 表示 Mesh 节点,为 2 字节字段。NodeId 用于区分每个 Mesh 设备在 Mesh 网中的唯一标识,控制某个设备就向 Mesh 网发此设备对应的 NodeId 命令即可。
LocalId 表示 Mesh 群组,为 2 字节字段。LocalId 用于区分每个 Mesh 群组在 Mesh 网中的唯一标识,控制某个群组中的设备就向 Mesh 网发此群组对应的 LocalId 命令即可。
多步操作 因为设备的操作,例如增删操作、群组操作,都需要本地蓝牙命令执行一次、云端记录一次。所以,向本地 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");
}

重命名子设备

接口说明

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()

移除子设备

接口说明

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();
}
});

查询子设备状态

云端查询到的 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 网内支持创建 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() {
}
});

删除群组

接口说明

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() {
}
});

重命名群组

接口说明

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() {
}
});

控制

ITuyaBlueMeshDevice 类提供了对 Mesh 设备的操作

控制设备

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

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

接口说明

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() {
}
});

控制群组

控制群组通过发送控制指令来实现,控制指令的格式如下:

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

接口说明

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 固件升级 章节。