
# Node.js Demo使用说明
#### 概述
本文以Node.js为例，介绍通过MQTTS/MQTT协议接入平台，基于[平台接口](https://support.huaweicloud.com/api-iothub/iot_06_v5_3002.html)实现"属性上报"、"订阅接收命令"等功能。
![](https://support.huaweicloud.com/devg-iothub/public_sys-resources/note_3.0-zh-cn.png)
本文中使用的代码为样例代码，仅用于体验平台通信功能，如需进行商用，可以参考[资源获取](https://support.huaweicloud.com/devg-iothub/iot_02_1004.html)获取对应语言的IoT Device SDK进行集成。
#### 前提条件
- 已安装Node.js，若未安装请参考[安装Node.js]。
- 已在[管理控制台](https://console.huaweicloud.com/iotdm/#/dm-portal/home)获取设备接入地址。获取地址的操作步骤，请参考[平台对接信息](https://support.huaweicloud.com/devg-iothub/iot_02_1004.html#ZH-CN_TOPIC_0187644975__section436212919379)。
- 已在[管理控制台](https://console.huaweicloud.com/iotdm/#/dm-portal/home)创建产品和设备。创建产品和设备的具体操作细节，请参考[创建产品](https://support.huaweicloud.com/usermanual-iothub/iot_01_0054.html)、[注册单个设备](https://support.huaweicloud.com/usermanual-iothub/iot_01_0031.html)或[批量注册设备](https://support.huaweicloud.com/usermanual-iothub/iot_01_0032.html)。
 
 #### 准备工作
1. 安装Node.js访问[Node.js官网](https://nodejs.org/en/download/)，选择合适系统的版本下载。（本文以windows 64-bit系统，Node.js版本v12.18.0（npm 6.14.4）为例）。
   ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0252780339.png "点击放大")
   
2. 下载完成后，运行安装文件，根据界面提示安装。
3. 检查Node.js是否安装成功。 Win键 + r --\>输入 cmd--\>回车，进入命令行窗口。
   输入node --v，回车后显示Node.js版本，输入npm -v显示版本信息，即表示安装成功。
   
 
#### 导入代码样例
1. 下载[quickStart(Node.js)](https://obs-pipeline.obs.cn-north-4.myhuaweicloud.com/sdkDeploy/quickStart/quickStart(nodejs).zip)样例，并解压。
2. 运行Win键 + r --\>输入 cmd--\>回车，进入命令行窗口，安装全局模块，执行如下命令：
   
   npm install mqtt -g：mqtt协议模块；
   npm install crypto-js -g：设备密钥加密算法模块；
   npm install fs -g：用于加载平台证书；
   
   
3. 找到文件解压的对应目录，如下图所示。 
   ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0252791350.png "点击放大")
   代码目录介绍如下：
   - **DigiCertGlobalRootCA.crt.pem**：平台证书文件;
   
   - **MqttDemo.js**：Node.js源码，包含MQTT/MQTTS连接到平台，并进行属性上报，命令下发；
    
   
   
4. Demo里的关键工程配置参数。其中MqttDemo.js需要配置Server地址、设备ID和设备密钥，用于启动Demo时，连接控制台上注册的设备。
   
   - Server地址：即域名，参考[平台对接信息](https://support.huaweicloud.com/devg-iothub/iot_02_1004.html#ZH-CN_TOPIC_0187644975__section436212919379)获取，SSL加密接入时该Server地址要与对应的[证书文件匹配使用](https://support.huaweicloud.com/devg-iothub/iot_02_1004.html#ZH-CN_TOPIC_0187644975__section197481637133318)；
   
   - 设备ID和设备密钥：在物联网平台[注册设备](https://support.huaweicloud.com/usermanual-iothub/iot_01_0031.html)或调用[创建设备](https://support.huaweicloud.com/api-iothub/iot_06_v5_0046.html)接口后获取。
   
   
   ```
   var TRUSTED_CA = fs.readFileSync("DigiCertGlobalRootCA.crt.pem");//获取证书
   //IoT平台mqtt对接地址（要替换为设备所在的平台域名地址）
   var serverUrl = "xxx.myhuaweicloud.com"; //请填写设备所在平台的接入地址
   //注册设备时获得的deviceId,密钥（要替换为自己注册的设备ID与密钥）
   var deviceId = "722cb****************";
   var secret = "****";
   var timestamp = dateFormat("YYYYmmddHH", new Date());
   var propertiesReportJson = {'services':[{'properties':{'alarm':1,'temperature':12.670784,'humidity':18.37673,'smokeConcentration':19.97906},'service_id':'smokeDetector','event_time':null}]};
   var responseReqJson = {'result_code': 0,'response_name': 'COMMAND_RESPONSE','paras': {'result': 'success'}};
   ```
   
   
5. 用户可通过mqtt.connect(options)中选择不同的options，确定设备侧建链时是否进行SSL加密，建议使用默认的MQTTS安全连接。 
   ```
   //MQTTS安全连接
   var options = {
       host: serverUrl,
       port: 8883,
       clientId: getClientId(deviceId),
       username: deviceId,
       password:HmacSHA256(secret, timestamp).toString(),
       ca: TRUSTED_CA,
       protocol: 'mqtts',
       rejectUnauthorized: false,
       keepalive: 120,
       reconnectPeriod: 10000,
       connectTimeout: 30000
   }
   //MQTT非安全连接，不建议使用
   var option = {
       host: serverUrl,
       port: 1883,
       clientId: getClientId(deviceId),
       username: deviceId,
       password: HmacSHA256(secret, timestamp).toString(),
       keepalive: 120,
       reconnectPeriod: 10000,
       connectTimeout: 30000
       //protocol: 'mqtts'
       //rejectUnauthorized: false
   }
   //此处默认使用options为安全连接
   var client = mqtt.connect(options);
   ```
   
   
 
#### 程序启动
设备或网关在接入物联网平台时首先需要和平台建立连接，从而将设备或网关与平台进行关联。开发者通过传入设备信息，将设备或网关连接到物联网平台。
1. 此Demo主要提供建立MQTT/MQTTS连接等方法，MQTT使用1883端口，MQTTS使用8883端口（需要加载设备校验平台身份的证书，用于设备侧接入物联网平台登录鉴权使用），并提供了Mqtt客户端建立连接的方法mqtt.connect(options)。
   ```
   var client = mqtt.connect(options);
   client.on('connect', function () {
       log("connect to mqtt server success, deviceId is " + deviceId);
       //订阅Topic
       subScribeTopic();
       //发布消息
       publishMessage();
   })
   //命令下发响应
   client.on('message', function (topic, message) {
       log('received message is ' + message.toString());
       var jsonMsg = responseReq;
       client.publish(getResponseTopic(topic.toString().split("=")[1]), jsonMsg);
       log('response message is ' + jsonMsg);
   })
   ```
   找到Node.js Demo源码目录，修改[关键工程配置参数]后启动程序，如下图：
   ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0253874593.png "点击放大")
   启动程序前，设备状态是离线。
   图1设备列表-设备离线   
   ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0000001981569133.png "点击放大")
   启动程序后，设备状态变为在线
   图2设备列表-设备在线   
   ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0000001950367902.png "点击放大")
   **注：如果连接失败，在重连回调函数中已实现退避重连，代码样例如下：**
   ```
   client.on('reconnect', () => {
       log("reconnect is starting");
   
       //退避重连
       var lowBound = Number(defaultBackoff)*Number(0.8);
       var highBound = Number(defaultBackoff)*Number(1.2);
   
       var randomBackOff = parseInt(Math.random()*(highBound-lowBound+1),10);
   
       var backOffWithJitter = (Math.pow(2.0, retryTimes)) * (randomBackOff + lowBound);
   
       var waitTImeUtilNextRetry = (minBackoff + backOffWithJitter) > maxBackoff ? maxBackoff : (minBackoff + backOffWithJitter);
   
       client.options.reconnectPeriod = waitTImeUtilNextRetry;
   
       log("next retry time: " + waitTImeUtilNextRetry);
   
       retryTimes++;
   })
   ```
   
2. 订阅某Topic的设备才能接收broker发布的关于该Topic的消息，关于平台预置Topic可参考[Topic定义](https://support.huaweicloud.com/api-iothub/iot_06_v5_3004.html)。此Demo调用subScribeTopic方法进行订阅Topic，订阅成功后等待平台命令下发：
   ```
   //订阅接收命令topic
   function subScribeTopic() {
       client.subscribe(getCmdRequestTopic(), function (err) {
           if (err) {
               log("subscribe error:" + err);
           } else {
               log("topic : " + getCmdRequestTopic() + " is subscribed success");
           }
       })
   }
   ```
   
3. 发布Topic是指设备主动向平台上报自己的属性或消息，详细见[设备属性上报接口](https://support.huaweicloud.com/api-iothub/iot_06_v5_3010.html)文档。连接成功后，调用publishMessage方法进行属性上报：
   ```
   //上报json数据，注意serviceId要与Profile中的定义对应
   function publishMessage() {
       var jsonMsg = propertiesReport;
       log("publish message topic is " + getReportTopic());
       log("publish message is " + jsonMsg);
       client.publish(getReportTopic(), jsonMsg);
       log("publish message successful");
   }
   ```
   上报属性的json：
   ```
   var propertiesReportJson = {'services':[{'properties':{'alarm':1,'temperature':12.670784,'humidity':18.37673,'smokeConcentration':19.97906},'service_id':'smokeDetector','event_time':null}]};
   ```
   命令行主界面如下：
   ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0253875623.png "点击放大")
   属性上报成功，平台界面如下：
   图3查看上报数据-Demo_smokeDetector   
   ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0000001981636489.png "点击放大")
   ![](https://support.huaweicloud.com/devg-iothub/public_sys-resources/note_3.0-zh-cn.png)
   如果在"设备详情"页面没有最新上报数据，请确认设备上报的服务/属性和产品模型中的服务/属性一致。
   
 
#### 接收下发命令
在Demo中提供了接收平台下发命令的功能，在MQTT建链完成并成功订阅Topic后，可以在[管理控制台](https://console.huaweicloud.com/iotdm/#/dm-portal/home)设备详情中命令下发或[使用应用侧Demo](https://support.huaweicloud.com/devg-iothub/iot_02_9994.html)对该设备ID进行命令下发。下发成功后，在Demo中接收到平台下发给设备的命令。
例如下发参数名为smokeDetector: SILENCE，参数值为50的命令。
图4命令下发-SILENCE   
![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0000001981734473.png "点击放大")
命令下发成功后，Demo收到的消息是50，命令运行主界面显示如下：
![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0253877872.png "点击放大")
![](https://support.huaweicloud.com/devg-iothub/public_sys-resources/note_3.0-zh-cn.png)
由于是同步命令需要端侧回复响应可[参考接口](https://support.huaweicloud.com/api-iothub/iot_06_v5_3014.html#section4)。
