概述
子设备不直接连接物联网平台,而是通过网关接入物联网平台。首先,需在物联网平台上创建网关和子设备;然后,开发网关设备端SDK,实现网关直连物联网平台;再由网关向物联网平台上报网关与子设备的拓扑关系;通过网关上报子设备证书(一机一密方式)或者子设备动态注册的认证方式,物联网平台校验子设备的身份和该子设备与网关的拓扑关系。所有校验通过,才会建立子设备逻辑通道,并绑定至网关物理通道上,实现子设备通过网关,与物联网平台建立连接,并进行通信。本文主要如何演示使用JAVA SDK实现相关过程。
关系图
操作步骤
1、创建网关和子设备,参考链接。
2、子设备产品的物模型定义:
2、pom.xml
<repositories>
<repository>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.aliyun.alink.linksdk</groupId>
<artifactId>iot-linkkit-java</artifactId>
<version>1.2.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.40</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>iot-java-sdk</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
3、Code Sample
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.cmp.connect.channel.MqttPublishRequest;
import com.aliyun.alink.linksdk.cmp.connect.channel.MqttSubscribeRequest;
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.base.ConnectState;
import com.aliyun.alink.linksdk.cmp.core.listener.IConnectNotifyListener;
import com.aliyun.alink.linksdk.cmp.core.listener.IConnectSendListener;
import com.aliyun.alink.linksdk.cmp.core.listener.IConnectSubscribeListener;
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 SubDeviceDemo {
// 初始化参数
private static String regionId = "cn-shanghai";
private static final String TAG = "TOPO";
//网关设备三元组信息
private static String GWproductKey = "********";
private static String GWdeviceName = "********";
private static String GWdeviceSecret = "********";
public static void main(String[] args) {
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);
//获取网关下topo关系,查询网关与子设备是否已经存在topo关系
//如果已经存在,则直接上线子设备,不存在则需要添加网关子设备
LinkKit.getInstance().getGateway().gatewayGetSubDevices(new IConnectSendListener() {
@Override
public void onResponse(ARequest request, AResponse aResponse) {
ALog.i(TAG, "获取网关的topo关系成功 : " + JSONObject.toJSONString(aResponse));
// 1、获取子设备列表结果
try {
ResponseModel<List<DeviceInfo>> response = JSONObject.parseObject(aResponse.data.toString(), new TypeReference<ResponseModel<List<DeviceInfo>>>() {
}.getType());
// TODO 根据实际应用场景处理
Integer subDeviceCounts = response.data.size();
System.out.println("topo下子设备的数目:" + subDeviceCounts);// topo网管下子设备的数目
// // 2、如果topo下面的子设备数目为0,则通过动态注册获取子设备Secret信息
// if (subDeviceCounts == 0)
// {
// /**
// * 子设备动态注册获取设备deviceSecret,如果设备已知三元组则忽略此步
// * 预注册设备时,可以使用设备的MAC地址或SN序列号等作为DeviceName
// */
// List<BaseInfo> subDevices = new ArrayList<>();
// BaseInfo baseInfo1 = new BaseInfo();
// // 未激活的子设备的信息
// baseInfo1.productKey = "********";
// baseInfo1.deviceName = "********";
// subDevices.add(baseInfo1);
//
// LinkKit.getInstance().getGateway().gatewaySubDevicRegister(subDevices, new IConnectSendListener() {
//
// @Override
// public void onResponse(ARequest request, AResponse response) {
// ALog.i(TAG, "子设备动态注册成功 : " + JSONObject.toJSONString(response));
//
// // 通过动态注册获取的子设备信息,供后续添加子设备到网关设备使用
// //{\"iotId\":\"********\",\"deviceSecret\":\"********\",\"productKey\":\"********\",\"deviceName\":\"********\"}
// }
// @Override
// public void onFailure(ARequest request, AError error) {
// ALog.i(TAG, "子设备注册失败 : " + JSONObject.toJSONString(error));
// }
// });
// }
// 3、使用子设备动态注册获取完子设备三元组信息后,添加到网关设备
List<BaseInfo> subDevices = new ArrayList<>();
BaseInfo baseInfo2 = new BaseInfo();
// 未激活的子设备三元组信息,可以通过上面的注释代码获取
baseInfo2.productKey = "********";
baseInfo2.deviceName = "********";
String deviceSecret = "********";
subDevices.add(baseInfo2);
LinkKit.getInstance().getGateway().gatewayAddSubDevice(baseInfo2, new ISubDeviceConnectListener() {
@Override
public String getSignMethod() {
// 使用的签名方法
return "hmacsha1";
}
@Override
public String getSignValue() {
// 获取签名,用户使用 deviceSecret 获得签名结果
Map<String, String> signMap = new HashMap<>();
signMap.put("productKey", baseInfo2.productKey);
signMap.put("deviceName", baseInfo2.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));
// 子设备添加成功后,代理子设备上线
LinkKit.getInstance().getGateway().gatewaySubDeviceLogin(baseInfo2, new ISubDeviceActionListener() {
@Override
public void onSuccess() {
System.out.println("代理子设备上线成功!");
// 基本信息设置
System.out.println("使用网关的通道执行子设备的数据上下行");
String topic = "/******/******/user/datapubandsub"; // 子设备自定义Topic
String sysSubTopic = "/sys/******/******/thing/event/property/post";// 子设备系统属性设置Topic
String payLoad = "{\"id\":\"123\",\"method\":\"thing.event.property.post\",\"params\":{\"RoomHumidity\":19},\"version\":\"1.0\"}"; //注意此处和第二步子设备物模型定义相关
// 发布系统消息测试
MqttPublishRequest request1 = new MqttPublishRequest();
// // topic 用户根据实际场景填写
request1.topic = sysSubTopic;
request1.payloadObj = payLoad;
LinkKit.getInstance().publish(request1, new IConnectSendListener(){
@Override
public void onResponse(ARequest aRequest, AResponse aResponse) {
System.out.println("发送成功");
}
@Override
public void onFailure(ARequest aRequest, AError aError) {
System.out.println("发送失败");
}
});
// 订阅
MqttSubscribeRequest request = new MqttSubscribeRequest();
// topic 用户根据实际场景填写
request.topic = topic;
request.isSubscribe = true;
// 直接做Topic的订阅,注意自定义Topic需要使用这种方式,否则会出现订阅失败的情况,SDK有Bug,系统Topic没有问题
LinkKit.getInstance().subscribe(request, new IConnectSubscribeListener(){
@Override
public void onSuccess() {
System.out.println("直接订阅成功");
}
@Override
public void onFailure(AError aError) {
System.out.println("直接订阅失败" + aError.getMsg());
}
});
// 注册下行监听
LinkKit.getInstance().registerOnNotifyListener(new IConnectNotifyListener() {
@Override
public void onNotify(String s, String s1, AMessage aMessage) {
System.out.println("下行消息Topic:" + s);
System.out.println("下行消息:" + new String((byte[])aMessage.getData()));
}
@Override
public boolean shouldHandle(String s, String s1) {
return false;
}
@Override
public void onConnectStateChange(String s, ConnectState connectState) {
System.out.println("连接状态发生变化 :" + s + connectState);
}
});
System.out.println("---取消订阅---");
// 取消订阅 参考订阅方法 request.isSubscribe = false; 即可
}
@Override
public void onFailed(AError aError) {
ALog.d(TAG, "onFailed() called with: aError = [" + aError + "]");
System.out.println(aError.getMsg());
}
});
} else {
ALog.i(TAG, "topo关系添加失败 : " + JSONObject.toJSONString(aError));
}
}
@Override
public void onDataPush(String s, AMessage aMessage) {
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onFailure(ARequest request, AError error) {
ALog.i(TAG, "获取网关的topo关系失败 : " + JSONObject.toJSONString(error));
}
});
}
});
}
}
说明
1、首先初始化网关设备,建立连接;
2、检查当前网关设备拓扑关系,获取现有子设备信息,示例演示网关设备尚未添加子设备;
3、动态注册获取网关子设备DeviceSecret,本部分注释,第一次运行需要取消注释;
4、添加子设备三元组信息到网关设备;
5、网关代理子设备进行Topic的订阅、消息的发布及下行监听注册。
注意
替换网关设备三元组信息、子设备三元组信息、子设备Topic信息。
4、运行结果