サブデバイスは IoT Platform に直接接続しません。代わりに、ゲートウェイを介して接続します。サブデバイスがゲートウェイに接続した後、ゲートウェイはトポロジー関係を照会し、サブデバイスの情報を IoT Platform に報告してから、サブデバイスの IoT Platform への接続をプロキシします。
前提条件
以下が完了していることを確認してください。
背景情報
- サブデバイスの開発
サブデバイスは IoT Platform に直接接続しないため、IoT Platform デバイス SDK をインストールする必要はありません。サブデバイスのメーカーがデバイス側のソフトウェアを開発します。
- このデモ
DeviceTopoManager ファイル (java/src/main/java/com/aliyun/iot/api/common/deviceApi ディレクトリ内) には、トポロジー関係の管理、サブデバイス証明書の取得、サブデバイスのログイン用コードが含まれています。
ステップ 1:トポロジー関係の管理
ゲートウェイが IoT Platform に接続した後、IoT Platform とトポロジー関係を同期させる必要があります。これにより、ゲートウェイはサブデバイスと IoT Platform 間の通信をプロキシできます。コンソールまたはサンプルコードを使用して、トポロジー関係を表示および追加できます。
- IoT Platform コンソールで、対応する で、ゲートウェイとサブデバイス間のトポロジー関係を表示および追加します。
- 左側メニューで、 を選択し、リストからゲートウェイデバイスを見つけます。
- ゲートウェイの行にある [サブデバイス] をクリックして、サブデバイス管理ページに移動します。ゲートウェイのサブデバイスに関する情報を表示します。
- [サブデバイスの追加] をクリックして、「ゲートウェイとサブデバイスの作成」の手順で作成したサブデバイスを追加します。
- 次のサンプルコードを使用して、トポロジー関係を照会および追加します。
-
トポロジー関係の照会:
/** * ゲートウェイのトポロジー関係を取得し、ゲートウェイとサブデバイス間に既存の関係があるかどうかを確認します。 */ private void getGWDeviceTopo() { LinkKit.getInstance().getGateway().gatewayGetSubDevices(new IConnectSendListener() { @Override public void onResponse(ARequest request, AResponse aResponse) { ALog.i(TAG, "ゲートウェイのトポロジー関係の取得に成功しました: " + 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, "ゲートウェイのトポロジー関係の取得に失敗しました: " + JSONObject.toJSONString(error)); } }); } -
トポロジー関係の追加:説明
- 次のステップでは、サブデバイスのデバイス証明書を取得する方法について説明します。
- IoT Platform がサブデバイスとゲートウェイ間のトポロジー関係を確認すると、サブデバイスはオンラインになり、ゲートウェイの物理チャネルを再利用して IoT Platform と通信できます。
/** * トポロジー関係を追加するサブデバイスに関する情報。 */ private void gatewayAddSubDevice() { BaseInfo baseInfo1 = new BaseInfo(); baseInfo1.productKey = "a1j7SyR****"; baseInfo1.deviceName = "safasdf"; 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, "トポロジー関係の追加に成功しました: " + JSONObject.toJSONString(iSubDeviceChannel)); //サブデバイスにログインします。 gatewaySubDeviceLogin(); } else { ALog.i(TAG, "トポロジー関係の追加に失敗しました: " + JSONObject.toJSONString(aError)); } } @Override public void onDataPush(String s, AMessage aMessage) { } }); }
-
ステップ 2:サブデバイス証明書の取得
- デバイスごとの一意の証明書認証
サブデバイスが作成されたら、コンソールのデバイス詳細ページからサブデバイスの ProductKey、DeviceName、および DeviceSecret を取得します。
- ゲートウェイとサブデバイスの間でプロトコルを定義し、ゲートウェイがサブデバイスを検出し、サブデバイスのデバイス証明書を取得できるようにします。このプロトコルは、ゲートウェイとサブデバイスのメーカーによって定義されます。
- あるいは、ゲートウェイのメーカーが、ゲートウェイにサブデバイスの証明書情報を事前設定する方法を提供することもできます。
- サブデバイスの動的登録
ゲートウェイは、サブデバイスの ProductKey と DeviceName を IoT Platform に報告することで、サブデバイスを登録します。IoT Platform が ProductKey と DeviceName を検証した後、サブデバイスの DeviceSecret を動的に発行します。
- サブデバイスを作成するときは、サブデバイスのシリアル番号 (SN) または MAC アドレスを DeviceName として使用します。サブデバイスの作成後、プロダクトの動的登録を有効にします。IoT Platform コンソールで、対応するプロダクトのプロダクト詳細ページに移動し、動的登録を有効にします。
- ゲートウェイを開発する際には、ゲートウェイがサブデバイスを検出し、サブデバイスのモデルと一意の識別子 (SN または MAC アドレス) を取得できるプロトコルを実装します。また、サブデバイスのモデルを Alibaba Cloud IoT Platform の ProductKey にマッピングする必要もあります。
- IoT Platform の動的登録機能を使用して、サブデバイスの DeviceSecret を取得します。
サンプルコード:
/** * 動的登録を使用して、サブデバイスの DeviceSecret を取得します。 * IoT Platform 上で事前にサブデバイスを作成する場合、サブデバイスの 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)); } }); }サブデバイスの動的登録の詳細については、「サブデバイスの動的登録」をご参照ください。
- サブデバイスを作成するときは、サブデバイスのシリアル番号 (SN) または MAC アドレスを DeviceName として使用します。サブデバイスの作成後、プロダクトの動的登録を有効にします。
ステップ 3:サブデバイスのログイン
/**
* API を呼び出してサブデバイスにログインする前に、トポロジー関係が確立されていることを確認してください。ゲートウェイは、接続されたサブデバイスを検出した後、サブデバイスがオンラインであることを IoT Platform に通知する必要があります。
* サブデバイスがオンラインになった後、トピックのサブスクライブやメッセージのパブリッシュなどの操作を実行できます。
*/
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 + "]");
}
});
}
}
付録:コードデモ
次のコードは、ゲートウェイがサブデバイス情報を検出して報告し、サブデバイスと IoT Platform 間に論理チャネルを確立し、サブデバイスがゲートウェイの物理チャネルを再利用して IoT Platform に接続する方法を示しています。
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 クライアントは TLSv1.2 を使用します。
*/
System.setProperty("https.protocols", "TLSv1.2");
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 connection info=" + params);
/**
* 初期化パラメーターを設定し、ゲートウェイのデバイス証明書を渡します。
*/
DeviceInfo deviceInfo = new DeviceInfo();
deviceInfo.productKey = GWproductKey;
deviceInfo.deviceName = GWdeviceName;
deviceInfo.deviceSecret = GWdeviceSecret;
params.deviceInfo = deviceInfo;
/** LinkKit インスタンスを初期化し、接続を確立します。 **/
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 を取得します。デバイス証明書が既知の場合は、このステップをスキップして、トポロジー関係を直接追加します。
// IoT Platform 上で事前にデバイスを作成する場合、デバイスの 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, "ゲートウェイのトポロジー関係の取得に成功しました: " + 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, "ゲートウェイのトポロジー関係の取得に失敗しました: " + JSONObject.toJSONString(error));
}
});
}
/**
* 動的登録を使用して、サブデバイスの DeviceSecret を取得します。ゲートウェイがすでにサブデバイスのデバイス証明書を取得している場合は、このステップをスキップします。
* IoT Platform 上で事前にデバイスを作成する場合、デバイスの 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, "トポロジー関係の追加に成功しました: " + JSONObject.toJSONString(iSubDeviceChannel));
//サブデバイスにログインします。
gatewaySubDeviceLogin();
} else {
ALog.i(TAG, "トポロジー関係の追加に失敗しました: " + 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 onSuccess() {
// このリスナーは、サブデバイスが正常に削除されたときに呼び出されます。 削除する前にサブデバイスをオフラインにすることができます。
}
@Override
public void onFailed(AError aError) {
// サブデバイスの削除に失敗しました。
}
});
}
/**
* API を呼び出してサブデバイスにログインする前に、トポロジー関係が確立されていることを確認してください。ゲートウェイは、接続されたサブデバイスを検出した後、サブデバイスがオンラインであることを IoT Platform に通知する必要があります。
* サブデバイスがオンラインになった後、トピックのサブスクライブやメッセージのパブリッシュなどの操作を実行できます。
*/
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 + "]");
}
});
}
}