A sub-device can be connected to IoT Platform only by using a gateway. After a sub-device is connected to a gateway, the gateway checks whether a topological relationship exists between the gateway and the sub-device. If so, the gateway reports the information about the sub-device to IoT Platform and connects the sub-device to IoT Platform.
Prerequisites
Before you start, make sure that the following operations are performed:
Background information
- Configure a sub-device
A sub-device cannot directly connect to IoT Platform. Therefore, you do not need to install a device SDK that IoT Platform provides on your sub-device. The device SDK of a sub-device must be developed by its provider.
- The SDK package provided by IoT Platform
The DeviceTopoManager file in the java/src/main/java/com/aliyun/iot/api/common/deviceApi directory contains the code that is used by a gateway to manage topological relationships, obtain sub-device certificates, and connect a sub-device to IoT Platform by using the gateway.
Step 1: Manage topological relationships by using the gateway
After the gateway is connected to IoT Platform, the gateway must synchronize its topological relationships to IoT Platform. This way, the gateway can implement communication between sub-devices and IoT Platform. You can view and add topological relationships in the IoT Platform console. You can also perform these operations by using the sample code.
- View and add topological relationships between the gateway and sub-devices in the IoT Platform console.
- In the left-side navigation pane, choose . On the Devices page, find the gateway device.
- Click Sub-device in the Actions column. On the page that appears, view the sub-devices of the gateway.
- Click Add Sub-device. Add the sub-device that you created in the Create a gateway and a sub-device step.
- Query and add topological relationships by using the following sample code.
-
Query topological relationships by using the following sample code:
/** * Obtain the topological relationships of the gateway and check whether a topological relationship exists between the gateway and the sub-device. */ private void getGWDeviceTopo() { LinkKit.getInstance().getGateway().gatewayGetSubDevices(new IConnectSendListener() { @Override public void onResponse(ARequest request, AResponse aResponse) { ALog.i(TAG, "The topological relationships of the gateway are obtained: " + JSONObject.toJSONString(aResponse)); // Obtain a list of sub-devices. try { ResponseModel<List<DeviceInfo>> response = JSONObject.parseObject(aResponse.data.toString(), new TypeReference<ResponseModel<List<DeviceInfo>>>() { }.getType()); // Process the request based on the actual scenario. } catch (Exception e) { e.printStackTrace(); } } @Override public void onFailure(ARequest request, AError error) { ALog.i(TAG, "Failed to obtain the topological relationships of the gateway: " + JSONObject.toJSONString(error)); } }); }
-
Add a topological relationship by using the following sample code:Note
- For information about how to obtain the certification information about the sub-device, see Step 2.
- After IoT Platform validates the topological relationship between the gateway and the sub-device, the sub-device can use the physical channel of the gateway to communicate with IoT Platform.
/** * Specify the information about the sub-device for which you want to add a topological relationship. */ 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() { // The signature method that you use to sign the request. return "hmacsha1"; } @Override public String getSignValue() { // Generate a signature based on the 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() { // Set the clientId parameter as needed. return "id"; } @Override public Map<String, Object> getSignExtraData() { return null; } @Override public void onConnectResult(boolean isSuccess, ISubDeviceChannel iSubDeviceChannel, AError aError) { // Handle the result of the operation for adding the topological relationship between the sub-device and the gateway. if (isSuccess) { // After the topological relationship is added, you can use the gateway to connect the sub-device to IoT Platform. ALog.i(TAG, "The topological relationship is added: " + JSONObject.toJSONString(iSubDeviceChannel)); // Connect the sub-device to IoT Platform by using the gateway. gatewaySubDeviceLogin(); } else { ALog.i(TAG, "Failed to add the topological relationship: " + JSONObject.toJSONString(aError)); } } @Override public void onDataPush(String s, AMessage aMessage) { } }); }
-
Step 2: Obtain the certificate information about the sub-device
- Unique-certificate-per-device
You can obtain the ProductKey, DeviceName, and DeviceSecret of the sub-device on the details page of the sub-device in the IoT Platform console.
- After a gateway discovers a connected sub-device, the gateway can obtain the certificate of the sub-device based on a protocol that is defined between the gateway and the sub-device. The protocol is defined by the provider of the gateway and the provider of the sub-device.
- The provider of the gateway provides a configuration method that allows the gateway to preset certificate information about the sub-device. This feature is implemented by the provider of the gateway.
- Dynamic sub-device registration
The gateway reports the ProductKey and DeviceName of the sub-device to IoT Platform to register the sub-device. After IoT Platform validates the ProductKey and DeviceName of the sub-device, IoT Platform dynamically assigns a DeviceSecret to the sub-device.
- When you create a sub-device in IoT Platform, set the deviceName parameter to the serial number or MAC address of the sub-device. After the sub-device is created, enable dynamic registration for the sub-device.
- When you configure the gateway, make sure that the gateway can obtain the model and unique identifier, which is the serial number or MAC address, of the sub-device. This is made possible by the protocol that is defined between the gateway and the sub-device. In addition, map the sub-device model to the ProductKey in IoT Platform.
- Obtain a DeviceSecret for the sub-device from IoT Platform during dynamic registration.
Sample code:
/** * Obtain a DeviceSecret for the sub-device during dynamic registration. * When you create the sub-device in IoT Platform, set the deviceName parameter to the serial number or MAC address of the sub-device. */ 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, "The sub-device is registered: " + JSONObject.toJSONString(response)); } @Override public void onFailure(ARequest request, AError error) { ALog.i(TAG, "Failed to register the sub-device: " + JSONObject.toJSONString(error)); } }); }
For more information about dynamic registration of devices, see Overview.
Step 3: Connect the sub-device to IoT Platform by using the gateway.
/**
* Before you call an API operation to connect the sub-device to IoT Platform, make sure that a topological relationship is added between the sub-device and the gateway. After the gateway discovers a connected sub-device, the gateway reports the information about the sub-device to IoT Platform.
* After the sub-device is connected to IoT Platform by using the gateway, the gateway can subscribe to topics and publish messages for the sub-device.
*/
public void gatewaySubDeviceLogin(){
BaseInfo baseInfo1 = new BaseInfo();
baseInfo1.productKey = "a1j7SyR****";
baseInfo1.deviceName = "safasdf";
LinkKit.getInstance().getGateway().gatewaySubDeviceLogin(baseInfo1, new ISubDeviceActionListener() {
@Override
public void onSuccess() {
// The gateway connects the sub-device to IoT Platform.
// The gateway can subscribe to topics or publish messages for the sub-device. The gateway can also delete or disable the sub-device.
// subDevDisable(null);
// subDevDelete(null);
}
@Override
public void onFailed(AError aError) {
ALog.d(TAG, "onFailed() called with: aError = [" + aError + "]");
}
});
}
}
Appendix: Sample code
A gateway discovers a connected sub-device and reports the information about the sub-device to IoT Platform. IoT Platform establishes a logical channel between the sub-device and IoT Platform. The sub-device uses the physical channel of the gateway to communicate with IoT Platform. The preceding process can be implemented in the following sample code:
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";
// The information about the gateway device.
private static String GWproductKey = "a1Bxp*********";
private static String GWdeviceName = "XMtrv3y*************";
private static String GWdeviceSecret = "19xJNybifnmgc*************";
public static void main(String[] args) {
/**
* The information about the MQTT connection.
*/
DeviceTopoManager manager = new DeviceTopoManager();
/**
* The Java HTTP client of this device supports TSLv1.2.
*/
System.setProperty("https.protocols", "TLSv2");
manager.init();
}
public void init() {
LinkKitInitParams params = new LinkKitInitParams();
/**
* Specify the parameters for MQTT initialization.
*/
IoTMqttClientConfig config = new IoTMqttClientConfig();
config.productKey = GWproductKey;
config.deviceName = GWdeviceName;
config.deviceSecret = GWdeviceSecret;
config.channelHost = GWproductKey + ".iot-as-mqtt." + regionId + ".aliyuncs.com:1883";
/**
* Specify whether to receive offline messages.
* The cleanSession field that corresponds to the MQTT connection.
*/
config.receiveOfflineMsg = false;
params.mqttClientConfig = config;
ALog.setLevel(LEVEL_DEBUG);
ALog.i(TAG, "mqtt connetcion info=" + params);
/**
* Configure the initialization and pass in the certificate information about the gateway device.
*/
DeviceInfo deviceInfo = new DeviceInfo();
deviceInfo.productKey = GWproductKey;
deviceInfo.deviceName = GWdeviceName;
deviceInfo.deviceSecret = GWdeviceSecret;
params.deviceInfo = deviceInfo;
/**Establish a connection.**/
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);
// Obtain the topological relationships of the gateway and check whether a topological relationship exists between the gateway and the sub-device.
// If a topological relationship exists between the gateway and the sub-device, the gateway connects the sub-device to IoT Platform.
getGWDeviceTopo();
// Dynamically register the sub-device to obtain a DeviceSecret for the sub-device. If the gateway has obtained the certificate of the sub-device, skip this step.
// When you create the sub-device in IoT Platform, set the deviceName parameter to the serial number or MAC address of the sub-device.
gatewaySubDevicRegister();
//Specify the information about the sub-device for which you want to add a topological relationship.
gatewayAddSubDevice();
}
});
}
/**
* Obtain the topological relationships of the gateway and check whether a topological relationship exists between the gateway and the sub-device.
*/
private void getGWDeviceTopo() {
LinkKit.getInstance().getGateway().gatewayGetSubDevices(new IConnectSendListener() {
@Override
public void onResponse(ARequest request, AResponse aResponse) {
ALog.i(TAG, "The topological relationships of the gateway are obtained: " + JSONObject.toJSONString(aResponse));
// Obtain a list of sub-devices.
try {
ResponseModel<List<DeviceInfo>> response = JSONObject.parseObject(aResponse.data.toString(), new TypeReference<ResponseModel<List<DeviceInfo>>>() {
}.getType());
// TODO. Write code based on your actual application scenario.
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onFailure(ARequest request, AError error) {
ALog.i(TAG, "Failed to obtain the topological relationships of the gateway: " + JSONObject.toJSONString(error));
}
});
}
/**
* Dynamically register the sub-device to obtain a DeviceSecret for the sub-device. If the gateway has obtained the certificate of the sub-device, skip this step.
* When you create the sub-device in IoT Platform, set the deviceName parameter to the serial number or MAC address of the sub-device.
*/
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, "The sub-device is registered: " + JSONObject.toJSONString(response));
}
@Override
public void onFailure(ARequest request, AError error) {
ALog.i(TAG, "Failed to register the sub-device: " + JSONObject.toJSONString(error));
}
});
}
/**
* Specify the information about the sub-device for which you want to add a topological relationship.
*/
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() {
// The signature method that you use to sign the request.
return "hmacsha1";
}
@Override
public String getSignValue() {
// Generate a signature based on the 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() {
// Set the clientId parameter as needed.
return "id";
}
@Override
public Map<String, Object> getSignExtraData() {
return null;
}
@Override
public void onConnectResult(boolean isSuccess, ISubDeviceChannel iSubDeviceChannel, AError aError) {
// Handle the result of the operation for adding the topological relationship between the sub-device and the gateway.
if (isSuccess) {
// After the topological relationship is added, you can use the gateway to connect the sub-device to IoT Platform.
ALog.i(TAG, "The topological relationship is added: " + JSONObject.toJSONString(iSubDeviceChannel));
// Connect the sub-device to IoT Platform by using the gateway.
gatewaySubDeviceLogin();
} else {
ALog.i(TAG, "Failed to add the topological relationship: " + 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() {
// The sub-device is deleted. You can disconnect the sub-device before you delete it.
}
@Override
public void onFailed(AError aError) {
// The sub-device failed to be deleted.
}
});
}
/**
* Before you call an API operation to connect the sub-device to IoT Platform, make sure that a topological relationship is added between the sub-device and the gateway. After the gateway discovers a connected sub-device, the gateway reports the information about the sub-device to IoT Platform.
* After the sub-device is connected to IoT Platform, the gateway can subscribe to topics and publish messages for the sub-device.
*/
public void gatewaySubDeviceLogin(){
BaseInfo baseInfo1 = new BaseInfo();
baseInfo1.productKey = "a1j7SyR***********";
baseInfo1.deviceName = "safa*********";
LinkKit.getInstance().getGateway().gatewaySubDeviceLogin(baseInfo1, new ISubDeviceActionListener() {
@Override
public void onSuccess() {
// The gateway connects the sub-device to IoT Platform.
// The gateway can subscribe to topics or publish messages for the sub-device. The gateway can also delete or disable the sub-device.
// subDevDisable(null);
// subDevDelete(null);
}
@Override
public void onFailed(AError aError) {
ALog.d(TAG, "onFailed() called with: aError = [" + aError + "]");
}
});
}
}