子设备不直接连接物联网平台,需要通过网关与物联网平台连接。子设备连接网关后,网关查询与当前子设备间的拓扑关系,将子设备的信息上报物联网平台,代理子设备接入物联网平台。
前提条件
您已完成以下操作:
背景信息
- 开发子设备
由于子设备不直接连接物联网平台,所以无需为子设备安装物联网平台设备端SDK。子设备的设备端由厂商自行开发。
- 本示例Demo
java/src/main/java/com/aliyun/iot/api/common/deviceApi目录下的DeviceTopoManager文件中包含网关管理拓扑关系、获取子设备证书和子设备上线的代码。
步骤一:网关管理拓扑关系
网关接入物联网平台后,需将拓扑关系同步至物联网平台,才能代理子设备与物联网平台通信。您可以直接在控制台查看、添加拓扑关系,也可以使用示例代码完成这一步。
- 在物联网平台控制台下查看、添加网关与子设备的拓扑关系。
- 在左侧导航栏,选择 ,在列表中找到网关设备。
- 单击网关设备对应的子设备,进入子设备管理页面。查看网关产品下的子设备信息。
- 单击添加子设备,将创建网关和子设备步骤中的子设备添加到网关下。
- 通过以下示例代码查询、添加拓扑关系。
查询拓扑关系:
/** * 获取网关下topo关系,查询网关与子设备是否已经存在topo关系。 */ private void getGWDeviceTopo() { LinkKit.getInstance().getGateway().gatewayGetSubDevices(new IConnectSendListener() { @Override public void onResponse(ARequest request, AResponse aResponse) { ALog.i(TAG, "获取网关的topo关系成功 : " + JSONObject.toJSONString(aResponse)); // 获取子设备列表结果。 try { ResponseModel<List<DeviceInfo>> response = JSONObject.parseObject(aResponse.data.toString(), new TypeReference<ResponseModel<List<DeviceInfo>>>() { }.getType()); // TODO,根据实际应用场景处理。 } catch (Exception e) { e.printStackTrace(); } } @Override public void onFailure(ARequest request, AError error) { ALog.i(TAG, "获取网关的topo关系失败 : " + JSONObject.toJSONString(error)); } }); }
- 添加拓扑关系:说明
- 子设备证书信息的获取方法请参见下一步。
- 物联网平台系统确认子设备和网关的拓扑关系后,子设备便可上线,复用网关的物理通道与物联网平台进行通信。
/** * 待添加拓扑关系的子设备信息。 */ private void gatewayAddSubDevice() { BaseInfo baseInfo1 = new BaseInfo(); baseInfo1.productKey = "a1j7SyR****"; baseInfo1.deviceName = "safa***"; String deviceSecret = "7lzCJIWHmGFpZpDKbJdVucDHUz6C****"; LinkKit.getInstance().getGateway().gatewayAddSubDevice(baseInfo1, new ISubDeviceConnectListener() { @Override public String getSignMethod() { // 使用的签名方法。 return "hmacsha1"; } @Override public String getSignValue() { // 获取签名,用户使用deviceSecret获得签名结果。 Map<String, String> signMap = new HashMap<>(); signMap.put("productKey", baseInfo1.productKey); signMap.put("deviceName", baseInfo1.deviceName); //signMap.put("timestamp", String.valueOf(System.currentTimeMillis())); signMap.put("clientId", getClientId()); return SignUtils.hmacSign(signMap, deviceSecret); } @Override public String getClientId() { // clientId可为任意值。 return "id"; } @Override public Map<String, Object> getSignExtraData() { return null; } @Override public void onConnectResult(boolean isSuccess, ISubDeviceChannel iSubDeviceChannel, AError aError) { // 添加结果 if (isSuccess) { // 子设备添加成功,接下来可以做子设备上线的逻辑 ALog.i(TAG, "topo关系添加成功 : " + JSONObject.toJSONString(iSubDeviceChannel)); //子设备上线 gatewaySubDeviceLogin(); } else { ALog.i(TAG, "topo关系添加失败 : " + JSONObject.toJSONString(aError)); } } @Override public void onDataPush(String s, AMessage aMessage) { } }); }
步骤二:获取子设备证书
子设备创建成功后,物联网平台会颁发设备证书。网关可通过以下方法,获取子设备证书信息。
- 使用一机一密的认证方式。
在设备创建成功后,在控制台的设备详情页面,获取ProductKey、DeviceName和DeviceSecret。
- 在网关与子设备之间定义协议,实现网关发现子设备,获取子设备的设备证书。该协议由网关厂商与子设备厂商自行定义。
- 网关厂商可以在网关上提供某种配置方式,预置子设备的证书信息。该功能由网关厂商自行实现。
- 使用子设备动态注册的方式。
由网关向物联网平台上报子设备的ProductKey和DeviceName进行注册。物联网平台校验子设备ProductKey和DeviceName通过后,动态下发子设备的DeviceSecret。
- 创建子设备时,以设备的SN码或MAC地址作为DeviceName。设备创建成功后,开启产品的动态注册功能。
- 开发网关时,实现网关通过某种协议发现子设备,获取子设备的型号(model)和唯一标识(SN码或MAC地址);并实现子设备型号(model)与阿里云物联网平台ProductKey的映射。
- 通过物联网平台的动态注册功能,从物联网平台获取子设备的DeviceSecret。
代码示例:
/** * 子设备动态注册获取设备deviceSecret。 * 在物联网平台上提前创建子设备时,可以使用子设备的MAC地址或SN序列号等作为DeviceName。 */ private void gatewaySubDevicRegister() { List<BaseInfo> subDevices = new ArrayList<>(); BaseInfo baseInfo1 = new BaseInfo(); baseInfo1.productKey = "a1j7SyR***"; baseInfo1.deviceName = "safasdf"; subDevices.add(baseInfo1); LinkKit.getInstance().getGateway().gatewaySubDevicRegister(subDevices, new IConnectSendListener() { @Override public void onResponse(ARequest request, AResponse response) { ALog.i(TAG, "子设备注册成功 : " + JSONObject.toJSONString(response)); } @Override public void onFailure(ARequest request, AError error) { ALog.i(TAG, "子设备注册失败 : " + JSONObject.toJSONString(error)); } }); }
关于设备动态注册的详细说明,请参见子设备动态注册。
步骤三:子设备上线
/**
* 调用子设备上线接口之前,请确保已建立topo关系。网关发现子设备连接之后,需要告知物联网平台子设备上线。
* 子设备上线之后可以执行子设备的订阅、发布等操作。
*/
public void gatewaySubDeviceLogin(){
BaseInfo baseInfo1 = new BaseInfo();
baseInfo1.productKey = "a1j7SyR****";
baseInfo1.deviceName = "safasdf";
LinkKit.getInstance().getGateway().gatewaySubDeviceLogin(baseInfo1, new ISubDeviceActionListener() {
@Override
public void onSuccess() {
// 代理子设备上线成功。
// 上线之后可订阅、发布消息,并可以删除和禁用子设备。
// subDevDisable(null);
// subDevDelete(null);
}
@Override
public void onFailed(AError aError) {
ALog.d(TAG, "onFailed() called with: aError = [" + aError + "]");
}
});
}
}
附录:代码Demo
由网关发现并上报子设备信息,建立子设备与物联网平台的逻辑通道,及子设备复用网关物理通道接入物联网平台的完整示例代码如下:
package com.aliyun.iot.api.common.deviceApi;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.aliyun.alink.dm.api.BaseInfo;
import com.aliyun.alink.dm.api.DeviceInfo;
import com.aliyun.alink.dm.api.InitResult;
import com.aliyun.alink.dm.api.SignUtils;
import com.aliyun.alink.dm.model.ResponseModel;
import com.aliyun.alink.linkkit.api.ILinkKitConnectListener;
import com.aliyun.alink.linkkit.api.IoTMqttClientConfig;
import com.aliyun.alink.linkkit.api.LinkKit;
import com.aliyun.alink.linkkit.api.LinkKitInitParams;
import com.aliyun.alink.linksdk.channel.gateway.api.subdevice.ISubDeviceActionListener;
import com.aliyun.alink.linksdk.channel.gateway.api.subdevice.ISubDeviceChannel;
import com.aliyun.alink.linksdk.channel.gateway.api.subdevice.ISubDeviceConnectListener;
import com.aliyun.alink.linksdk.channel.gateway.api.subdevice.ISubDeviceRemoveListener;
import com.aliyun.alink.linksdk.cmp.core.base.AMessage;
import com.aliyun.alink.linksdk.cmp.core.base.ARequest;
import com.aliyun.alink.linksdk.cmp.core.base.AResponse;
import com.aliyun.alink.linksdk.cmp.core.listener.IConnectSendListener;
import com.aliyun.alink.linksdk.tools.AError;
import com.aliyun.alink.linksdk.tools.ALog;
import java.util.*;
import static com.aliyun.alink.linksdk.tools.ALog.LEVEL_DEBUG;
public class DeviceTopoManager {
private static String regionId = "cn-shanghai";
private static final String TAG = "TOPO";
//网关设备
private static String GWproductKey = "a1Bxp*********";
private static String GWdeviceName = "XMtrv3y*************";
private static String GWdeviceSecret = "19xJNybifnmgc*************";
public static void main(String[] args) {
/**
* mqtt连接信息
*/
DeviceTopoManager manager = new DeviceTopoManager();
/**
* 服务器端的java http客户端使用TSLv1.2。
*/
System.setProperty("https.protocols", "TLSv2");
manager.init();
}
public void init() {
LinkKitInitParams params = new LinkKitInitParams();
/**
* 设置mqtt初始化参数。
*/
IoTMqttClientConfig config = new IoTMqttClientConfig();
config.productKey = GWproductKey;
config.deviceName = GWdeviceName;
config.deviceSecret = GWdeviceSecret;
config.channelHost = GWproductKey + ".iot-as-mqtt." + regionId + ".aliyuncs.com:1883";
/**
* 是否接受离线消息。
* 对应mqtt的cleanSession字段。
*/
config.receiveOfflineMsg = false;
params.mqttClientConfig = config;
ALog.setLevel(LEVEL_DEBUG);
ALog.i(TAG, "mqtt connetcion info=" + params);
/**
* 设置初始化,传入网关的设备证书信息。
*/
DeviceInfo deviceInfo = new DeviceInfo();
deviceInfo.productKey = GWproductKey;
deviceInfo.deviceName = GWdeviceName;
deviceInfo.deviceSecret = GWdeviceSecret;
params.deviceInfo = deviceInfo;
/**建立连接**/
LinkKit.getInstance().init(params, new ILinkKitConnectListener() {
public void onError(AError aError) {
ALog.e(TAG, "Init Error error=" + aError);
}
public void onInitDone(InitResult initResult) {
ALog.i(TAG, "onInitDone result=" + initResult);
//获取网关下拓扑关系,查询网关与子设备是否已经存在拓扑关系。
//如果已经存在,则直接上线子设备。
getGWDeviceTopo();
//子设备动态注册获取DeviceSecret,如果设备已知设备证书则忽略此步,直接添加拓扑关系。
//在物联网平台上提前创建设备时,可以使用设备的MAC地址或SN序列号等作为DeviceName。
gatewaySubDevicRegister();
//待添加拓扑关系的子设备信息。
gatewayAddSubDevice();
}
});
}
/**
* 获取网关下拓扑关系,查询网关与子设备是否已经存在拓扑关系。
*/
private void getGWDeviceTopo() {
LinkKit.getInstance().getGateway().gatewayGetSubDevices(new IConnectSendListener() {
@Override
public void onResponse(ARequest request, AResponse aResponse) {
ALog.i(TAG, "获取网关的topo关系成功 : " + JSONObject.toJSONString(aResponse));
// 获取子设备列表结果。
try {
ResponseModel<List<DeviceInfo>> response = JSONObject.parseObject(aResponse.data.toString(), new TypeReference<ResponseModel<List<DeviceInfo>>>() {
}.getType());
// TODO 根据实际应用场景处理。
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onFailure(ARequest request, AError error) {
ALog.i(TAG, "获取网关的topo关系失败 : " + JSONObject.toJSONString(error));
}
});
}
/**
* 子设备动态注册获取设备deviceSecret,如果网关已获得子设备证书则忽略此步。
* 在物联网平台上提前创建设备时,可以使用设备的MAC地址或SN序列号等作为DeviceName。
*/
private void gatewaySubDevicRegister() {
List<BaseInfo> subDevices = new ArrayList<>();
BaseInfo baseInfo1 = new BaseInfo();
baseInfo1.productKey = "a1j7SyR**********";
baseInfo1.deviceName = "test123*********";
subDevices.add(baseInfo1);
LinkKit.getInstance().getGateway().gatewaySubDevicRegister(subDevices, new IConnectSendListener() {
@Override
public void onResponse(ARequest request, AResponse response) {
ALog.i(TAG, "子设备注册成功 : " + JSONObject.toJSONString(response));
}
@Override
public void onFailure(ARequest request, AError error) {
ALog.i(TAG, "子设备注册失败 : " + JSONObject.toJSONString(error));
}
});
}
/**
* 待添加拓扑关系的子设备信息。
*/
private void gatewayAddSubDevice() {
BaseInfo baseInfo1 = new BaseInfo();
baseInfo1.productKey = "a1j7Sy*************";
baseInfo1.deviceName = "safasd********";
String deviceSecret = "7lzCJIWHmGF**************";
LinkKit.getInstance().getGateway().gatewayAddSubDevice(baseInfo1, new ISubDeviceConnectListener() {
@Override
public String getSignMethod() {
// 使用的签名方法
return "hmacsha1";
}
@Override
public String getSignValue() {
// 获取签名,用户使用DeviceSecret获得签名结果。
Map<String, String> signMap = new HashMap<>();
signMap.put("productKey", baseInfo1.productKey);
signMap.put("deviceName", baseInfo1.deviceName);
// signMap.put("timestamp", String.valueOf(System.currentTimeMillis()));
signMap.put("clientId", getClientId());
return SignUtils.hmacSign(signMap, deviceSecret);
}
@Override
public String getClientId() {
// clientId可为任意值。
return "id";
}
@Override
public Map<String, Object> getSignExtraData() {
return null;
}
@Override
public void onConnectResult(boolean isSuccess, ISubDeviceChannel iSubDeviceChannel, AError aError) {
// 添加结果
if (isSuccess) {
// 子设备添加成功,接下来可以做子设备上线的逻辑。
ALog.i(TAG, "topo关系添加成功 : " + JSONObject.toJSONString(iSubDeviceChannel));
//子设备上线
gatewaySubDeviceLogin();
} else {
ALog.i(TAG, "topo关系添加失败 : " + JSONObject.toJSONString(aError));
}
}
@Override
public void onDataPush(String s, AMessage aMessage) {
}
});
}
public void gatewayDeleteSubDevice(){
BaseInfo baseInfo1 = new BaseInfo();
baseInfo1.productKey = "a1j7S**************";
baseInfo1.deviceName = "saf*********";
LinkKit.getInstance().getGateway().gatewayDeleteSubDevice(baseInfo1, new ISubDeviceRemoveListener() {
@Override
public void onSuceess() {
// 成功删除子设备。删除之前可先做下线操作。
}
@Override
public void onFailed(AError aError) {
// 删除子设备失败。
}
});
}
/**
* 调用子设备上线之前,请确保已建立拓扑关系。网关发现子设备连接后,需要告知物联网平台子设备上线。
* 子设备上线之后可以执行子设备的订阅、发布等操作。
*/
public void gatewaySubDeviceLogin(){
BaseInfo baseInfo1 = new BaseInfo();
baseInfo1.productKey = "a1j7SyR***********";
baseInfo1.deviceName = "safa*********";
LinkKit.getInstance().getGateway().gatewaySubDeviceLogin(baseInfo1, new ISubDeviceActionListener() {
@Override
public void onSuccess() {
// 代理子设备上线成功。
// 上线之后可订阅、发布消息,并可以删除和禁用子设备。
// subDevDisable(null);
// subDevDelete(null);
}
@Override
public void onFailed(AError aError) {
ALog.d(TAG, "onFailed() called with: aError = [" + aError + "]");
}
});
}
}