子裝置不直接連接物聯網平台,需要通過網關與物聯網平台串連。子裝置串連網關後,網關查詢與當前子裝置間的拓撲關係,將子裝置的資訊上報物聯網平台,代理子裝置接入物聯網平台。
前提條件
您已完成以下操作:
背景資訊
- 開發子裝置
由於子裝置不直接連接物聯網平台,所以無需為子裝置安裝物聯網平台裝置端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 + "]");
}
});
}
}