更新时间:2024-12-30 GMT+08:00
分享

IoT Device SDK使用指南(ArkTS)

IoT Device SDK(ArkTS)提供设备接入华为云IoT物联网平台的ArkTS版本的SDK,提供设备和平台之间通讯能力,并且针对各种场景提供了丰富的demo代码。相关集成指导请参考IoT Device SDK(ArkTS)使用指南

准备工作

使用说明

  • 下载安装:在DevEco Studio中执行以下命令引入并安装SDK。
    ohpm install @huaweicloud/iot-device-sdk
  • 权限配置:使用SDK需要网络连接的权限,需要在module.json5的requestPermissions中增加"ohos.permission.INTERNET"的权限,如下所示:
    {
      "module": {
        "requestPermissions": [
          {
            "name": "ohos.permission.INTERNET"
          }
        ]
      }
    }

具体使用方式请看gitHub上的README文档

创建产品

为了方便体验,我们提供了一个烟感的产品模型,烟感会上报烟雾值、温度、湿度、烟雾报警、还支持响铃报警命令。以烟感例,体验消息上报、属性上报等功能。

  1. 访问设备接入服务,单击“管理控制台”进入设备接入控制台,选择您的实例,单击实例卡片进入。查看MQTTS设备接入域名,保存该地址。
  2. 单击左侧导航栏“产品”,单击页面左侧的“创建产品”
  3. 根据页面提示填写参数,然后单击“确定”完成产品的创建。

    基本信息

    所属资源空间

    平台自动将新创建的产品归属在默认资源空间下。如需归属在其他资源空间下,下拉选择所属的资源空间。如无对应的资源空间,请先创建资源空间

    产品名称

    自定义。支持字母、数字、下划线(_)、连字符(-)的字符组合。

    协议类型

    选择“MQTT”

    数据格式

    选择“JSON”

    设备类型选择

    选择”自定义类型”

    设备类型

    填写“smokeDetector”

    高级配置

    产品ID

    不填写

    产品描述

    请根据实际情况填写。

上传产品模型

  1. 单击下载烟感产品模型smokeDetector,获取产品模型文件。
  2. 找到步骤3创建的产品,单击产品进入产品详情页。
  3. 选择“基本信息”页签,单击“上传模型文件”,上传步骤1获取的产品模型文件。

    图1 产品-上传产品模型

注册设备

  1. 选择左侧导航栏“设备 > 所有设备”,单击“注册设备”
  2. 根据页面提示信息填写参数,然后单击“确定”

    参数名称

    说明

    所属资源空间

    确保和步骤3创建的产品归属在同一个资源空间。

    所属产品

    选择步骤3创建的产品。

    设备标识码

    即nodeID,设备唯一物理标识。可自定义,由英文字母和数字组成。

    设备名称

    即device_name,可自定义。

    设备认证类型

    选择“密钥”

    密钥

    设备密钥,可自定义。若不填写密钥,物联网平台会自动生成密钥。

    设备注册成功后保存设备标识码、设备ID、密钥。

设备初始化

设备初始化Demo,可参考entry/src/main/ets/pages/Index.ets。

  1. 创建设备需要输入注册设备时获取的设备ID、密码,以及1中获取的设备对接信息,注意格式为ssl://域名信息:端口号ssl://IP地址:端口号
    1
    2
    3
    4
    private device: IoTDevice | null = null;
    
    // 用户请替换为自己的接入地址, 设备ID,设备密钥及证书路径(证书文件放在resource/resfile下,连接华为云时请使用对应的证书,可以在资源获取中下载证书文件)。
    this.device = new IoTDevice("ssl://域名信息:8883","deviceId", "mySecret", "filePath");
    
  2. 调用init建立连接。您可以使用异步方式初始化或使用同步方式初始化。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 使用异步方式初始化
    this.device.init().then((data: boolean) => {
      // 连接成功处理
    }).catch((err: string) => {
      // 连接失败处理
    })
    
    // 或使用同步方式初始化
    // await this.device.init();
    
  3. 查看设备日志打印,设备连接成功。
    1
    IoTDA_SDK# connect result is {"code":0,"message":"Connect Success"}
    
  4. 创建设备并连接成功后,可以开始进行设备通信。调用IoT Device 的client接口获取设备客户端,客户端提供了消息、属性、命令等通讯接口。

消息上报

消息上报Demo,可参考entry/src/main/ets/pages/MessageSample.ets。

消息上报是指设备向平台上报消息。

  1. 在初始化并连接平台成功后,调用客户端的reportDeviceMessage接口上报设备消息,publishRawMessage上报自定义Topic的消息。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
      // 上报系统topic消息  
      const reportMessage: DeviceMessage = { content: this.message }
      this.device.client.reportDeviceMessage(reportMessage)
        .then((data: IoTMqttResponse) => {
          LogUtil.info(TAG, `report deviceMessage success ${JSON.stringify(data)}. DeviceMessage is ${JSON.stringify(reportMessage)}`);
        })
        .catch((error: IoTMqttResponse | string) => {
          LogUtil.error(TAG, `report deviceMessage failed ${JSON.stringify(error)}`);
        })
    
      // 上报自定义topic消息($oc开头,注意需要先在平台配置自定义topic)
      const topic = `$oc/devices/${this.device?.deviceId}/user/test`;
      const rawMessage: RawMessage = {
        topic: topic,
        qos: 0,
        payload: this.message
      }
      this.device?.client.publishRawMessage((rawMessage)).then((res: IoTMqttResponse) => {
        LogUtil.info(TAG, `publish rawMessage(${rawMessage.topic}) success, message is ${JSON.stringify(rawMessage)}}`);
      }).catch((error: IoTMqttResponse | string) => {
        LogUtil.error(TAG, `publish rawMessage(${rawMessage.topic}) failed, error is ${JSON.stringify(error)}}`);
      });
    
      // 上报自定义topic消息(非$oc开头,可用设备topic策略控制权限)
      const topic = "hello/world";
      const rawMessage: RawMessage = {
        topic: topic,
        qos: 0,
        payload: this.message
      }
      this.device?.client.publishRawMessage((rawMessage)).then((res: IoTMqttResponse) => {
        LogUtil.info(TAG, `publish rawMessage(${rawMessage.topic}) success, message is ${JSON.stringify(rawMessage)}}`);
      }).catch((error: IoTMqttResponse | string) => {
        LogUtil.error(TAG, `publish rawMessage(${rawMessage.topic}) failed, error is ${JSON.stringify(error)}}`);
      });
    
  2. 上报成功,查看日志发送消息成功。
    图2 上报系统Topic消息日志
    图3 上报自定义Topic($oc开头) 消息日志
    图4 上报自定义Topic(非$oc开头)消息日志
  3. 在设备接入控制台,选择设备 > 所有设备-查看设备是否在线。
    图5 设备列表-设备在线
  4. 选择对应设备,单击“详情”,进入设备详情页面启动设备消息跟踪。
    图6 消息跟踪-启动消息跟踪
  5. 消息跟踪显示平台成功接收到设备的消息。
    图7 消息跟踪-查看device_sdk_java消息跟踪

    消息跟踪会有一定的延时,如果没有看到数据,请等待后刷新。

属性上报

属性上报Demo,可参考entry/src/main/ets/pages/PropertySample.ets文件。

1. 在初始化并连接平台成功后,调用客户端的reportProperties接口上报设备属性。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
const properties: ServiceProperty[] = 
[
  {
	"service_id": "smokeDetector",
	"properties": {
	  "alarm": 1,
	  "temperature": Math.random() * 100,
	  "humidity": Math.random() * 100,
	  "smokeConcentration": Math.random() * 100,
	}
  }
]
;
this.device.client.reportProperties(properties)
  .then((data: IoTMqttResponse) => {
	LogUtil.info(TAG, `report properties success ${JSON.stringify(data)}, properties is ${JSON.stringify(properties)}}`);  })
  .catch((error: IoTMqttResponse | string) => {
	LogUtil.error(TAG, `report properties failed ${JSON.stringify(error)}`);
  })

2. 上报成功,查看日志发送属性成功。

图8 上报属性日志

3. 在设备接入控制台,选择设备 > 所有设备,选择对应设备,单击“详情”,进入设备详情页面可以看到最新上报的属性值。

图9 物模型-属性上报

属性读写

属性读写Demo,可参考entry/src/main/ets/pages/PropertySample.ets。

  1. 在初始化连接成功后,调用客户端的propertyListener方法来设置属性回调接口。
    • 写属性处理:实现了alarm属性的写操作,其他属性不支持写操作。
    • 读属性处理:将本地属性值按照接口格式进行拼装。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    let propertyListener: PropertyListener = {
      onPropertiesSet: (requestId: string, services: ServiceProperty[]): void => {
    	this.logArr.unshift(`${new Date()}: onPropertiesSet requestId is ${requestId}, services is ${JSON.stringify(services)}`)
    	// 遍历services
    	services.forEach(serviceProperty => {
    	  LogUtil.info("onPropertiesSet, serviceId is ", serviceProperty.service_id);
    	  // 遍历属性
    	  Object.keys(serviceProperty.properties).forEach(name => {
    		LogUtil.log(TAG, `property name is ${name}`);
    		LogUtil.log(TAG, `set property value is ${serviceProperty.properties[name]}`);
    	  })
    	})
    
    	// 修改本地的属性
    	this.device?.client.respondPropsSet(requestId, IotResult.SUCCESS);
      },
      onPropertiesGet: (requestId: string, serviceId?: string): void => {
    	this.logArr.unshift(`${new Date()}: onPropertiesGet requestId is ${requestId}, serviceId is ${serviceId} and respondPropsGet`)
    	LogUtil.info(TAG, `onPropertiesGet, the serviceId is ${serviceId}`);
    	const serviceProperties: ServiceProperty[] = [
    	  {
    		"service_id": "smokeDetector",
    		"properties": {
    		  "alarm": 1,
    		  "temperature": Math.random() * 100,
    		  "humidity": Math.random() * 100,
    		  "smokeConcentration": Math.random() * 100,
    		}
    	  }
    	];
    	this.device?.client.respondPropsGet(requestId, serviceProperties);
      }
    }
    // 设置属性监听器
    this.device.client.propertyListener = propertyListener;
    
    1. 属性读写接口需要调用respondPropsGet和respondPropsSet接口来上报操作结果。
    2. 如果设备不支持平台主动到设备读,onPropertiesGet接口可以空实现。
  2. 执行上述代码,设置属性监听器,在平台上设备影子页面查看当前alarm属性值为1,修改alarm属性为0后,查看设备侧日志,看到设备收到属性设置alarm属性为0。
    图10 设备影子-查看alarm属性
    图11 设备影子-属性配置alarm
    图12 查看属性设置alarm为0

命令下发

命令下发Demo,可参考entry/src/main/ets/pages/CommandSample.ets。

设置命令监听器用来接收平台下发的命令,在回调接口里,需要对命令进行处理,并上报响应。

  1. 在CommandSample例子中实现了命令的处理,收到命令后仅进行打印,然后调用respondCommand上报响应。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     let commandListener: CommandListener = {
        onCommand: (requestId: string, serviceId: string, commandName: string, paras: object): void => {
          const command = `requestId is ${requestId}, serviceId is ${serviceId}, commandName is ${commandName}, paras is ${JSON.stringify(paras)}`;
          LogUtil.info(TAG, `received command is ${command}`);
          // 用户可以在该处进行命令处理
    
          const commandRsp: CommandRsp = {
            result_code: 0
          }
          this.device?.client.respondCommand(requestId, commandRsp).then((data: IoTMqttResponse) => {
             LogUtil.info(TAG, `respond command success ${JSON.stringify(data)}, commandRsp is ${commandRsp}}`);      
          }).catch((err: IoTMqttResponse | string) => {
            LogUtil.error(TAG, `respond command failed ${JSON.stringify(err)}`);
          })
        }
      }
      this.device.client.commandListener = commandListener;
    		
    
  2. 执行上述代码设置命令监听后,在平台执行命令下发,其中serviceId为“smokeDetector”、命令名为“ringAlarm”、参数携带duration为整数20。
  3. 查看日志,设备收到命令并成功上报响应。

面向物模型编程

面向物模型编程Demo,可参考entry/src/main/ets/pages/ProfileSample.ets。

前面介绍了直接调用设备客户端的接口和平台进行通讯的方法,这种方式比较灵活,但用户需要妥善处理每一个接口,实现比较复杂。

SDK提供了一种更简单的方式,即面向物模型编程。面向物模型编程指基于SDK提供的物模型抽象能力,设备代码按照物模型定义设备服务,然后可以直接访问设备服务(即调用设备服务的属性读写接口),SDK就能自动和平台通讯,完成属性的同步和命令的调用。

相比直接调用客户端接口和平台进行通讯,面向物模型编程更简单,它简化了设备侧代码的复杂度,让设备代码只需要关注业务,而不用关注和平台的通讯过程。这种方式适合多数场景。

ProfileSample例子演示了如何面向物模型编程:

  1. 首先定义一个烟感服务类,继承自AbstractService。(如果有多个服务,则需要定义多个服务类):
    1
    2
    3
    class SmokeDetector extends AbstractService  {
    
    }
    
  2. 定义服务属性,私有变量以下划线开头,使用@Reflect.metadata("Property", { name: "string", writeable: boolean })注解表示一个属性,其中中name和产品模型中属性名保持一致。writeable用来标识属性是否可写
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
        @Reflect.metadata("Property", { name: "alarm", writeable: true })
        private _smokeAlarm: number = 1;
    
        @Reflect.metadata("Property", { name: "smokeConcentration", writeable: false })
        private _concentration: number = 0;
    
        @Reflect.metadata("Property", { name: "humidity", writeable: false })
        private _humidity: number = 0;
    
        @Reflect.metadata("Property", { name: "temperature", writeable: false })
        private _temperature: number = 10;
    
  3. 定义服务的命令。设备收到平台下发的命令时,SDK会自动调用这里定义的命令。注解中name对应物模型的command_name,method对应接收命令的处理方法,命令的入参和返回值类型固定不能修改。

    这里定义的是一个响铃报警命令,命令名为ringAlarm,下发参数为”duration”,表示响铃报警的持续时间。

    1
    2
    3
    4
    5
    6
    7
    8
    9
     @Reflect.metadata("DeviceCommand", {
        name: "ringAlarm",
        method: (paras: object): CommandRsp => {
          let duration: number = paras['duration'];
          LogUtil.log(TAG, `duration is ${duration}`);
    			return IotResult.SUCCESS;
    		}
      })
      private _alarm: Function = () => {};
    
  4. 定义getter和setter接口。
    • 当设备收到平台下发的查询属性以及设备上报属性时,会自动调用getter方法。getter方法需要读取设备的属性值,可以实时到传感器读取或者读取本地的缓存
    • 当设备收到平台下发的设置属性时,会自动调用setter方法。setter方法需要更新设备本地的值。如果属性不支持写操作,setter保留空实现。
    • setter和getter接口使用DevEco Studio右键的Generate的Getter and Setter自动生成,然后修改方法实现。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
      public set smokeAlarm(value: number) {
        this._smokeAlarm = value;
        if (value == 0) {
          LogUtil.info(TAG, "alarm is cleared by app");
        }
      }
    
      public get smokeAlarm(): number {
        return this._smokeAlarm;
      }
    
      public set concentration(value: number) {
        // 只读字段不需要实现set接口
      }
    
      public get concentration(): number {
        return Math.floor(Math.random() * 100);
      }
    
      public set humidity(value: number) {
        // 只读字段不需要实现set接口
      }
    
      public get humidity(): number {
        return Math.floor(Math.random() * 100);
      }
    
      public set temperature(value: number) {
        // 只读字段不需要实现set接口
      }
    
      public get temperature(): number {
        return Math.floor(Math.random() * 100);
      }  
    
  5. 实现构造函数,完成属性和命令的初始化。
      constructor() {
        super();
        const fields = Object.getOwnPropertyNames(this);
        this.init(fields);
      }
  6. 创建设备,注册烟感服务。
    1
    2
    3
    //创建设备服务
    const smokeDetector = new SmokeDetector();
    this.device.addService("smokeDetector", smokeDetector);
    
  7. 开启周期上报。
    1
    2
    //启动自动周期上报
    this.device.getService("smokeDetector")?.enableAutoReport(10000);
    
  8. 执行上述代码,查看日志上报属性。

  9. 在平台侧查看设备影子中属性alarm为1,修改alarm为0后,查看设备日志收到属性设置
    图13 设备影子-查看alarm属性
    图14 查看设备日志属性设置成功
  10. 在平台下发ringAlarm命令,查看设备日志看到ringAlarm命令被调用,并且成功上报响应。

版本更新说明

表1 ArkTS语言SDK版本更新说明

版本号

变更类型

功能描述说明

0.0.1

新增功能

提供对接华为云物联网平台能力,方便用户实现接入、设备管理、命令下发等业务场景

相关文档