全部产品
云市场

子设备管理

更新时间:2019-02-21 20:14:59

如果当前设备是一个网关,且该网关下的子设备需接入云端从而可以通过云端对子设备进行控制与管理,此时需要使用子设备管理功能。

网关子设备管理提供了子设备动态注册、获取云端网关下子设备列表、添加子设备、删除子设备、子设备上线、子设备下线、监听子设备禁用和删除的消息、代理子设备数据上下行的能力。

网关本身是一个直连设备,网关产品的开发参见前面章节中介绍的“认证与连接”、“自定义MQTT Topic通信”。网关使用子设备管理的功能前,需要网关已经连接到阿里云物联网平台。

说明 网关子设备管理相关接口参见设备 IGateway

网关开发过程说明

  • 厂商在物联网平台定义网关产品(基础版或者高级版),设置“节点类型”为“网关”,设置网关的身份认证模式,并根据网关功能定义topic或者定义物模型,并参照前面的章节中的说明对网关自身的功能进行开发;

  • 实现子设备的管理功能

    1. 实现子设备的发现与连接功能,该部分功能由厂商自行实现,阿里并未提供网关如何发现以及如何将子设备连接到网关的代码实现
    2. 实现子设备三元组的获取方式,下面的内容有介绍几种获取子设备三元组的方式供厂商参考
    3. 当网关发现并将一个子设备连接到网关后,如果需要该子设备能够通过物联网平台进行远程管理,需要调用SDK的添加子设备接口将其告知物联网平台,添加之前需要先获得该子设备的三元组信息;然后调用SDK提供的子设备上线接口通知物联网平台,因为如果一个子设备处于离线状态,如果远程对子设备进行控制,物联网平台将直接返回失败、而不是将命令发送给网关之后等待错误提示或者超时提示
    4. 当网关告知物联网平台一个子设备上线之后,需要将子设备的状态信息上报云端以保证子设备在云端的状态与当前子设备的状态一致。特别是使用物模型定义子设备功能时,子设备上线需要将属性的最新数值通知云端;
    5. 当网关的一个已添加到物联网平台的子设备离线时,网关需要调用子设备离线接口告知物联网平台这个子设备已离线;
    6. 当一个在线子设备的属性发生变化时,也需要实时告知物联网平台
    7. 当网关离线并再次上线时(比如网络连接断开,或者网关重启),网关需要对所有已添加到云端的子设备再次调用添加子设备接口,再次调用子设备上线接口,如果网关不知道子设备的属性与网关离线前上报到云端的是否一致,那么网关需要将子设备的最新属性再次上报云端。
    8. 当网关接收到来自物联网平台对子设备的控制消息时,网关如何将该消息转换成子设备识别的格式并发送给子设备,由网关厂商进行实现

子设备开发过程

  • 设备厂商在物联网平台定义子设备产品(基础版或者高级版),设置“节点类型”为“设备”,设置产品的身份认证模式
  • 阿里并不在子设备上提供任何SDK,因此网关如何发现子设备、如何连接子设备、网关如何发现子设备上线或者离线、网关如何将来自物联网平台的命令发送给子设备,均由网关厂商与子设备厂商定义协议并实现

子设备三元组的获取方式

子设备是通过网关到阿里云物联网进行注册的,注册时也需要使用到子设备的三元组进行设备验证。下面是网关获取子设备三元组的几种方式,网关厂家根据自己的实际情况进行选用:

  • 网关从子设备获取子设备三元组

由网关与子设备之间定义一套协议,当网关发现与连接子设备之后,获取到子设备的三元组。阿里云并不提供参考协议实现,该协议由网关厂商与子设备厂商自行定义与实现;

  • 网关预置子设备的三元组

如果网关设备预先可以得知自己需要连接的子设备,并且网关提供了某种配置方式输入子设备的三元组信息,那么可以通过这种方式获取子设备的三元组。同样,该功能由网关厂商实现。

  • 网关通过动态注册获取子设备三元组

网关可以通过某种协议发现与连接子设备,并获取到子设备的型号(model)以及唯一标识(比如SN、MAC地址),但是并不知道子设备的DeviceSecret。由于子设备也需要在阿里云物联网平台进行产品定义(云端会为子设备生成ProductKey),网关可以建立子设备型号(model)到阿里云物联网平台ProductKey的映射(网关厂家在网关上实现该映射),并将设备的唯一标识作为阿里云物联网平台的DeviceName,然后通过阿里云物联网平台提供的动态注册功能从云端获取子设备的DeviceSecret,从而得到完整的子设备的三元组信息。

子设备动态注册

子设备厂商需要在云端对子设备开启动态注册功能,并事先上传子设备的唯一标识(比如SN、MAC地址)。

设备端的代码示例如下:

  1. LinkKit.getInstance().getGateway().gatewaySubDevicRegister(getSubDevList(), new IConnectSendListener() {
  2. @Override
  3. public void onResponse(ARequest aRequest, AResponse aResponse) {
  4. ALog.d(TAG, "onResponse() called with: aRequest = [" + aRequest + "], aResponse = [" + (aResponse == null ? "null" : aResponse.data) + "]");
  5. try {
  6. // 子设备动态注册结果
  7. ResponseModel<List<DeviceInfo>> response = JSONObject.parseObject(aResponse.data.toString(), new TypeReference<ResponseModel<List<DeviceInfo>>>() {
  8. }.getType());
  9. if (response != null && "200".equals(response.code)) {
  10. /** 获取 deviceSecret, 存储到本地,在添加子设备的时候需要使用deviceSecret
  11. */
  12. // deviceSecret = response.data.get("deviceSecret");
  13. return;
  14. }
  15. } catch (Exception e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. @Override
  20. public void onFailure(ARequest aRequest, AError aError) {
  21. // 子设备动态注册失败
  22. }
  23. });

获取子设备列表

获取网关当前在云端已经注册了哪些子设备。但是一般来说,网关会记录自己已在云端注册了哪些子设备,不需要从云端去获取该列表。

  1. LinkKit.getInstance().getGateway().gatewayGetSubDevices(new IConnectSendListener() {
  2. @Override
  3. public void onResponse(ARequest aRequest, AResponse aResponse) {
  4. // 获取子设备列表结果
  5. try {
  6. ResponseModel<List<DeviceInfo>> response = JSONObject.parseObject(aResponse.data.toString(), new TypeReference<ResponseModel<List<DeviceInfo>>>() {
  7. }.getType());
  8. // TODO 根据实际应用场景处理
  9. } catch (Exception e) {
  10. e.printStackTrace();
  11. }
  12. }
  13. @Override
  14. public void onFailure(ARequest aRequest, AError aError) {
  15. // 获取子设备列表失败
  16. }
  17. });

添加子设备

网关发现并连接了一个新的子设备、并获取到子设备的三元组后,可以告知云端网关需要添加一个子设备。代码示例如下:

  1. final DeviceInfo info = new DeviceInfo();
  2. info.productKey = productKey; // 三元组 产品型号(必填)
  3. info.deviceName = deviceName; // 三元组 设备标识 (必填)
  4. info.deviceSecret= deviceSecret; // 三元组 设备密钥 (必填)
  5. LinkKit.getInstance().getGateway().gatewayAddSubDevice(info, new ISubDeviceConnectListener() {
  6. @Override
  7. public String getSignMethod() {
  8. // 使用的签名方法
  9. return "hmacsha1";
  10. }
  11. @Override
  12. public String getSignValue() {
  13. // 获取签名,用户使用 deviceSecret 获得签名结果
  14. Map<String, String> signMap = new HashMap<>();
  15. signMap.put("productKey", info.productKey);
  16. signMap.put("deviceName", info.deviceName);
  17. signMap.put("clientId", getClientId());
  18. return SignUtils.hmacSign(signMap, info.deviceSecret);
  19. }
  20. @Override
  21. public String getClientId() {
  22. // clientId 可为任意值
  23. return "id";
  24. }
  25. @Override
  26. public Map<String, Object> getSignExtraData() {
  27. return null;
  28. }
  29. @Override
  30. public void onConnectResult(boolean isSuccess, ISubDeviceChannel iSubDeviceChannel, AError aError) {
  31. // 添加结果
  32. if (isSuccess) {
  33. // 子设备添加成功,接下来可以做子设备上线的逻辑
  34. // subDevOnline(null);
  35. }
  36. }
  37. @Override
  38. public void onDataPush(String s, AMessage message) {
  39. // 收到子设备下行数据 topic=" + s + ", data=" + message
  40. // 如禁用 删除 已经 设置、服务调用等 返回的数据message.data 是 byte[]
  41. }
  42. });

注: 网关重启之后并连接到阿里云之后,对连接的子设备需要再次调用添加子设备方法。

删除子设备

此处指删除网关和子设备的拓扑关系。

  1. final DeviceInfo deviceInfo = new DeviceInfo();
  2. deviceInfo.productKey = productKey; // 三元组 产品型号(必填)
  3. deviceInfo.deviceName = deviceName; // 三元组 设备标识 (必填)
  4. LinkKit.getInstance().getGateway().gatewayDeleteSubDevice(deviceinfo, new ISubDeviceRemoveListener() {
  5. @Override
  6. public void onSuceess() {
  7. // 成功删除子设备 删除之前可先做下线操作
  8. }
  9. @Override
  10. public void onFailed(AError aError) {
  11. // 删除子设备失败
  12. }
  13. });

子设备上线

调用子设备上线之前,请确保已完成子设备添加。网关发现子设备连上网关之后,需要告知云端子设备上线,子设备上线之后可以执行子设备的订阅、发布等操作。注意:由于接口调用都是异步的,子设备上线接口不能在子设备添加的下一行调用,而是要放到子设备添加成功的回调里面调用。

  1. final DeviceInfo deviceInfo = new DeviceInfo();
  2. deviceInfo.productKey = productKey; // 三元组 产品型号(必填)
  3. deviceInfo.deviceName = deviceName; // 三元组 设备标识 (必填)
  4. LinkKit.getInstance().getGateway().gatewaySubDeviceLogin(deviceinfo, new ISubDeviceActionListener() {
  5. @Override
  6. public void onSuccess() {
  7. // 代理子设备上线成功
  8. // 上线之后可订阅 删除和禁用的下行通知
  9. // subDevDisable(null);
  10. }
  11. @Override
  12. public void onFailed(AError aError) {
  13. ALog.d(TAG, "onFailed() called with: aError = [" + aError + "]");
  14. }
  15. });

子设备下线

当子设备离线之后,网关需要告知云端子设备离线,以避免云端向子设备发送数据。子设备下线之后不可以进行子设备的发布、订阅、取消订阅等操作。

  1. final DeviceInfo deviceInfo = new DeviceInfo();
  2. deviceInfo.productKey = productKey; // 三元组 产品型号(必填)
  3. deviceInfo.deviceName = deviceName; // 三元组 设备标识 (必填)
  4. LinkKit.getInstance().getGateway().gatewaySubDeviceLogout(deviceinfo, new ISubDeviceActionListener() {
  5. @Override
  6. public void onSuccess() {
  7. // 代理子设备下线成功
  8. }
  9. @Override
  10. public void onFailed(AError aError) {
  11. // 代理子设备下线失败
  12. }
  13. });

监听子设备禁用

网关设备可以在云端操作子设备,如禁用子设备、启用子设备、删除和子设备的拓扑关系。目前服务端只支持禁用子设备的下行通知。服务端在禁用子设备的时候会对子设备做下线处理,后续网关将不能代理子设备和云端做通信。

  1. final DeviceInfo deviceInfo = new DeviceInfo();
  2. deviceInfo.productKey = productKey; // 三元组 产品型号(必填)
  3. deviceInfo.deviceName = deviceName; // 三元组 设备标识 (必填)
  4. LinkKit.getInstance().getGateway().gatewaySetSubDeviceDisableListener(deviceinfo, new IConnectRrpcListener() {
  5. @Override
  6. public void onSubscribeSuccess(ARequest aRequest) {
  7. // 订阅成功
  8. }
  9. @Override
  10. public void onSubscribeFailed(ARequest aRequest, AError aError) {
  11. // 订阅失败
  12. }
  13. @Override
  14. public void onReceived(ARequest aRequest, IConnectRrpcHandle iConnectRrpcHandle) {
  15. // 子设备禁用通知
  16. iConnectRrpcHandle.onRrpcResponse(null, null);
  17. }
  18. @Override
  19. public void onResponseSuccess(ARequest aRequest) {
  20. Log.d(TAG, "onResponseSuccess() called with: aRequest = [" + aRequest + "]");
  21. }
  22. @Override
  23. public void onResponseFailed(ARequest aRequest, AError aError) {
  24. Log.d(TAG, "onResponseFailed() called with: aRequest = [" + aRequest + "], aError = [" + aError + "]");
  25. }
  26. });

代理子设备物模型上下行

  • 子设备物模型初始化子设备物模型初始化必须在子设备添加到网关下,且子设备已经登录的情况下才可以调用。注意:由于接口调用都是异步的,子设备物模型初始化接口不能在子设备登录的下一行调用,而是要放到子设备登录成功的回调里面调用。
  1. DeviceInfo deviceInfo = new DeviceInfo();
  2. deviceInfo.productKey = productKey;
  3. deviceInfo.deviceName = deviceName;
  4. // deviceInfo.deviceSecret = "xxxx";
  5. Map<String, ValueWrapper> subDevInitState = new HashMap<>();
  6. // subDevInitState.put(); //TODO 用户根据实际情况设置
  7. String tsl = null;// 用户根据实际情况设置,默认为空 直接从云端获取最细的 TSL
  8. LinkKit.getInstance().getGateway().initSubDeviceThing(tsl, deviceInfo, subDevInitState, new IDMCallback<InitResult>() {
  9. @Override
  10. public void onSuccess(InitResult initResult) {
  11. // 物模型初始化成功之后 可以做服务注册 上报等操作
  12. }
  13. @Override
  14. public void onFailure(AError aError) {
  15. // 子设备初始化失败
  16. }
  17. });
  • 子设备物模型使用 接口使用和直连设备的物模型使用一致,获取 IThing 接口实现使用如下方式获取。
  1. // 获取 IThing 实例
  2. IThing thing = LinkKit.getInstance().getGateway().getSubDeviceThing(mBaseInfo).first;
  3. // thing 有可能为空,如子设备未登录、物模型未初始化、子设备未添加到网关、子设备处于离线状态等。
  4. // error 信息在 LinkKit.getInstance().getGateway().getSubDeviceThing(mBaseInfo).second 返回
  5. // 参考示例 注意判空
  6. thing.thingPropertyPost(reportData, new IPublishResourceListener() {
  7. @Override
  8. public void onSuccess(String s, Object o) {
  9. // 设备上报状态成功
  10. }
  11. @Override
  12. public void onError(String s, AError aError) {
  13. // 设备上报状态失败
  14. }
  15. });
  • 子设备物模型销毁 反初始化物模型,后续如果需要重新使用需要重新走登录、物模型初始化流程。
    1. LinkKit.getInstance().getGateway().uninitSubDeviceThing(mBaseInfo);

代理子设备基础上下行

使用网关的通道执行子设备的数据上下行。

  1. final DeviceInfo deviceInfo = new DeviceInfo();
  2. deviceInfo.productKey = productKey; // 三元组 产品型号(必填)
  3. deviceInfo.deviceName = deviceName; // 三元组 设备标识 (必填)
  4. String topic = xxx;
  5. String publishData = xxx;
  6. // 订阅
  7. LinkKit.getInstance().getGateway().gatewaySubDeviceSubscribe(topic, deviceinfo, new ISubDeviceActionListener() {
  8. @Override
  9. public void onSuccess() {
  10. // 代理子设备订阅成功
  11. }
  12. @Override
  13. public void onFailed(AError aError) {
  14. // 代理子设备订阅失败
  15. }
  16. });
  17. //发布
  18. LinkKit.getInstance().getGateway().gatewaySubDevicePublish(topic, publishData, deviceinfo, new ISubDeviceActionListener() {
  19. @Override
  20. public void onSuccess() {
  21. // 代理子设备发布成功
  22. }
  23. @Override
  24. public void onFailed(AError aError) {
  25. // 代理子设备发布失败
  26. }
  27. });
  28. // 取消订阅
  29. LinkKit.getInstance().getGateway().gatewaySubDeviceUnsubscribe(topic, deviceinfo, new ISubDeviceActionListener() {
  30. @Override
  31. public void onSuccess() {
  32. // 代理子设备取消订阅成功
  33. }
  34. @Override
  35. public void onFailed(AError aError) {
  36. // 代理子设备取消订阅事变
  37. }
  38. });