# Aliyun IoT Device SDK for Javascript Aliyun IoT Device SDK由阿里云提供给开发者然设备接入到阿里云IoT物联网平台(LinkPlatform )的工具。如果有使用问题可以反馈到linkplatform@alibaba-inc.com,关于IoT物联网平台更多功能和功能详细说明,参考官网文档 https://help.aliyun.com/product/30520.html?spm=a2c4g.11186623.6.540.3cb468d0Y2JGB2 ## 安装 > 安装 Node.js 运行环境,版本 `>=4.0.0` 。 通过 npm 包管理工具安装: ```bash npm install aliyun-iot-device-sdk --save ``` ## 设备接入云端 ```javascript var aliyunIot = require('aliyun-iot-device-sdk'); var device = aliyunIot.device({ productKey: '', deviceName: '', deviceSecret: '' }); device.on('connect', () => { console.log('connect successfully!'); }); ``` ## IoT LinkPlatform基础版 API ## 设备上报数据 ```javascript device.publish('///update', 'hello world!'); ``` ## 云端下行消息监听 ```javascript device.subscribe('///get'); device.on('message', (topic, payload) => { console.log(topic, payload.toString()); }); ``` ## IoT LinkPlatform高级版 API IoT 套件高级版封装了物模型定义与 Alink 异步协议,SDK 封装使得设备与云端通信时不需要关心 MQTT topic,只需要调用属性上报(aliyunIot.device#postProps())、服务监听(aliyunIot.device#onService())、事件上报(aliyunIot.device#postEvent())等相关 API。 ### 设备属性上报 ```javascript device.postProps({ 'LightSwitch': 0 }, (res) => { console.log("上报数据成功") }); ``` 调用 `device.postProps()`  等同于执行以下代码: ```javascript // 发布属性上报 topic device.publish('/sys///thing/event/property/post', JSON.stringify({ { id: msgId, version: '1.0', params: { 'LightSwitch': 25, }, method: 'thing.event.property.post' } })); // 监听属性上报响应 topic device.subscribe('/sys///thing/event/property/post_reply'); device.on('message', function(topic, message){ var res = message.toString(); if (res.id === msgId) { // 在这里处理服务端响应 console.log(res.data); } }); ``` ###  监听云端下发的服务调用消息 ```javascript // 监听云端设置属性wakeup_async服务消息 device.onService('wakeup_async', function (res) { // 处理服务参数 console.log('1^:device.serve',res); }); ``` 其他更多功能,请查api列表,值得注意的是 - device继承自mqtt.js的EventEmitter,可以使用其全部方法 - gateway可以使用device的全部api - subDevice也可以使用device的全部api ## API 设备相关 * aliyunIot.device() * aliyunIot.device#publish() * aliyunIot.device#subscribe() * aliyunIot.device#unsubscribe() * aliyunIot.device#postProps() * aliyunIot.device#postEvent() * aliyunIot.device#onService() * aliyunIot.device#end() * aliyunIot.postTags#postTags() * aliyunIot.deleteTags#deleteTags() * aliyunIot.getConfig#getConfig() * aliyunIot.onShadow#onShadow() * aliyunIot.getShadow#getShadow() * aliyunIot.postShadow#postShadow() * aliyunIot.deleteShadow#deleteShadow() 网关相关(网关也可以使用设备相关的api) * aliyunIot.gateway() * aliyunIot.gateway#addTopo() * aliyunIot.gateway#getTopo() * aliyunIot.gateway#removeTopo() * aliyunIot.gateway#login() * aliyunIot.gateway#logout() 子设备 * 与设备api相同,通过网关的login()方法返回子设备实例 动态注册 * aliyunIot(register() * aliyunIot.gateway#regiestSubDevice() ### aliyunIot.device(options) 和云端建立连接,返回一个 `Device` 连接实例,入参: * `options` * `productKey` (`String`) * `deviceName` (`String`) * `deviceSecret` (`String`) * `region` (`String`) 阿里云 region,默认值:cn-shanghai * `tls` (`Bool`) 是否开启 TLS 加密,Node.js 中如果开启将使用 TLS 协议进行连接,浏览器如果开启将上使用 WSS 协议 * `keepalive` (`int`) 心跳报文时间间隔,默认值60秒 * `clean` (`bool`) cleansession,是否清除连接session设置,默认值false ````js var device = aliyunIot.device({ productKey: '', deviceName: '', deviceSecret: '' }); ```` ### Event `'connect'` `function(connack) {}` 当连接到云端成功时触发。 ````javascript var aliyunIot = require('aliyun-iot-device-sdk'); var device = aliyunIot.device({ productKey: '', deviceName: '', deviceSecret: '' }); device.on('connect', () => { console.log('connected!'); }); ```` ### Event `'message'` `function(topic, message) {}` 当接受到云端消息时触发,回调函数参数: * `topic`  消息主题 * `message` 消息 payload ````js device.on('message', (res) => { console.log('msg:',res); }); ```` ### Event `'error'` 注意:由于nodejs的event机制,如果未监听error事件,当出现错误时会throw一个error,未try catch会导致程序终止,建议error事件需要监听
node对于这个error处理的解释: When an error occurs within an EventEmitter instance, the typical action is for an 'error' event to be emitted. These are treated as special cases within Node.js. If an EventEmitter does not have at least one listener registered for the 'error' event, and an 'error' event is emitted, the error is thrown, a stack trace is printed, and the Node.js process exits. 原文地址:https://nodejs.org/api/events.html ````js device.on('error', (err) => { console.log('error:',err); }); ```` 当连接出错触发 ### aliyunIot.device#publish(topic, message, [options], [callback]) 向topic发送消息,等同于 [mqtt.Client#publish()](https://github.com/mqttjs/MQTT.js/blob/master/README.md#publish) 方法。 ### aliyunIot.device#subscribe(topic, [callback]) 订阅消息,等同于 [mqtt.Client#subscribe()](https://github.com/mqttjs/MQTT.js/blob/master/README.md#subscribe) 方法。 ### aliyunIot.device#unsubscribe(topic, [callback]) 取消订阅消息等同于 [mqtt.Client#unsubscribe()](https://github.com/mqttjs/MQTT.js/blob/master/README.md#unsubscribe) 方法。 ### aliyunIot.device#postProps(params, [callback]) 上报物模型属性: * `params` 属性参数,`Object` 类型 * `callback` * `res` 服务端 reply 消息内容 ````js // 上报设备属性 device.postProps({ LightSwitch: 0 }, (res) => { console.log(res); }); ```` ### aliyunIot.device#postEvent(eventIdentifier, params, [callback]) 上报物模型事件: * `eventName` 事件名称 `String` 类型 * `params` 事件参数,`Object` 类型 * `callback` * `err` 错误,比如超时 * `res` 服务端 reply 消息内容 ````js // 上报设备事件 device.postEvent("lowpower", { power: 10, }, (res) => { console.log(`postEvent:${res}`); }) ```` ### aliyunIot.device#onService(seviceIdentifier, [callback]) 监听物模型服务: * `seviceName` 服务名称,`String` 类型 * `callback` * `params` 服务参数 ```javascript device.onService('turnOn', function(res) { // 收到设备服务调用消息 }); ``` ### aliyunIot.device#end([force], [options], [callback]) 设备或网关断开连接,等同于 [mqtt.Client#end()](https://github.com/mqttjs/MQTT.js/blob/master/README.md#end) 方法。 ### aliyunIot.device#postTags(params, [callback]) 上报设备标签: * `params` 属性对象数组,`array` 类型,内容格式示例 [ {attrKey:'xxx',attrValue:'xxx'},{}...] * `attrKey` 错误,比如超时或者 `res.code !== 200` * `attrValue` 服务端 reply 消息内容 * `callback` * `res` 服务端 reply 消息内容 - 每次重新上报设备标签相同key会覆盖内容,不同key会增加标签 - params 示例: ````js const tags = [ { "attrKey": "Temperature", "attrValue": "36.8" } ] device.postTags( tags, (res) => { console.log(`add tag ok res:${res.id}`); done() } ); ```` ### aliyunIot.device#deleteTags(tags) 删除设备标签: * `tags` 属性参数,`array` 类型,内容格式 [ 'string','string',....] * `string` 内外为tag的标签名称 - 示例: ```javascript device.deleteTags(['tagA','tagB']); ``` ### aliyunIot.device#getConfig() 获取设备标签: * `callback` 回调函数 * res获取到的当前设备标签 - 示例: ````javascript device.getConfig((res) => { console.log(`getConfig:${res.data.toString()}`); }); ```` ### aliyunIot.device#onShadow(callback) 订阅设备影子回调函数方法: * `callback` 回调函数 * res当影子设备变化或获取影子设备消息,上报影子消息时回调 - 示例: ````javascript device.onShadow((res) => { console.log('获取最新设备影子,%o', res); }) ```` ### aliyunIot.device#getShadow() 获取设备影子最新: - 示例: ````javascript // 设备主动获取影子,回调函数会触发onShadow方法,返回设备影子信息 device.getShadow(); ```` ### aliyunIot.device#postShadow(params) 上报设备影子数据 * `params` 上报影子设备的实际值 - 示例: ````javascript // 上报成功或错误都会触发onShadow方法,返回设备影子信息 device.postShadow({ a: "avalue" }); ```` ### aliyunIot.device#deleteShadow(keys) 删除影子设备的属性值 * `keys` 需要删除设备影子的属性的key数组 * 除了数组外,如果传入单个key,可以删除单个属性,传入空会删除全部属性 - 示例: ````javascript // 删除单个影子设备属性 device.deleteShadow("a"); // 删除多个影子设备属性 device.deleteShadow(["a","b"]); // 删除所有影子设备属性 device.deleteShadow() ```` ### aliyunIot.gateway(options) 和云端建立连接,返回一个网关 `Gateway` 类连接实例,继承自 `Device`  类。网关可以使用设备的所有方法 * `options` * `productKey` (`String`) * `deviceName` (`String`) * `deviceSecret` (`String`) * `region` (`String`) 阿里云 region,默认值:cn-shanghai * `tls` (`Bool`) 是否开启 TLS 加密,Node.js 中如果开启将使用 TLS 协议进行连接,浏览器如果开启将上使用 WSS 协议 * `keepalive` (`int`) 心跳报文时间间隔,默认值60秒 * `clean` (`bool`) cleansession,是否清除连接session设置,默认值false ````js var device = aliyunIot.gateway({ productKey: '', deviceName: '', deviceSecret: '' }); ```` ### aliyunIot.gateway#addTopo(deviceSign, [callback]) 添加子设备到拓扑 * `params` 子设备三元组数组,[{productKey,deviceName,deviceSecret},{productKey,deviceName,deviceSecret},] * `callback` * `res` 服务端 reply 消息内容 ````js gateway.addTopo( [sub_device1,sub_device2], (res)=>{console.log('>>>>>getTopo',res.message,res.data);} ); ```` ### aliyunIot.gateway#getTopo(callback) 添加子设备到拓扑关系 * `callback` * `res` 服务端 reply 消息内容 ````js gateway.getTopo( (res)=>{ console.log(res) } ); ```` ### aliyunIot.gateway#removeTopo(params, [callback]) 从拓扑关系里移除子设备 * `params` 移除设备参数的数组,示例:[{"deviceName": "xx","productKey": "xx"},{"deviceName": "xx","productKey": "xx"},....] * `productKey` * `deviceName` * `callback` * `res` 服务端 reply 消息内容 ````js gateway.removeTopo( [{"deviceName": "xx","productKey": "xx"},{"deviceName": "xx","productKey": "xx"}], (res)=>{ console.log('>>>>>getTopo') console.log(res.message) console.log(res.data) } ); ```` ### aliyunIot.gateway#login(params, [callback]) 子设备上线 * `params` 登录的设备信息示例:{"deviceName": "xx","productKey": "xx"} * `productKey` (`String`) * `deviceName` (`String`) * `deviceSecret` (`String`) * `callback` * `res` 服务端 reply 消息内容 * `返回值` 返回一个子设备,子设备可以使用设备的api,做相关的操作 ````js gateway.on('connect', () => { //子设备登录ok sub1 = gateway.login( {"deviceName":"xx","productKey":"xx","deviceSecret":"xxx"}, (res) => { console.log('>>>>>login', res); } ); // 子设备连接状态 sub1.on('connect', () => { console.log(">>>>sub connected!"); // do something sub1.postProps({ LightSwitch: 0 },(res)=>{ console.log(">>>>sub postProps!"); console.log(res); }); }); }); ```` ### aliyunIot.gateway#logout(params, [callback]) 子设备下线 * `params` 子设备身份 * `productKey` (`String`) * `deviceName` (`String`) * `callback` * `res` 服务端 reply 消息内容 ````js gateway.logout( {"productKey": "xxxxx","deviceName": "xxxxx"}, (res) => { console.log('>>>>>logout', res); } ```` ### aliyunIot#register(params, [callback]) 直连设备动态注册 * `params` 子设备身份 object 实例 productKey:"a15YDgQGhU0", * `productSecret` * `productKey` * `deviceName` * `callback` * `res` 服务端 reply 消息内容,包含设备三元组 ````js const params = { productKey:"xxxxxx", productSecret:"xxxxxx", deviceName:"xxxxxx" } aliyunIot.register(params,(res)=>{ console.log("register:",res); if(res.code == '200'){ // 注册成功请保存设备三元组,只会返回一次 } }) ```` ### aliyunIot.gateway#regiestSubDevice(params, [callback]) 通过网关注册子设备 * `params` 子设备身份信息,可以是单个`{productKey,deviceName}`或者是一组`[{productKey,deviceName},{productKey2,deviceName2}]`进行批量注册 * `productKey` * `deviceName` * `callback` * `res` 服务端 reply 消息内容 ````js const gateway = aliyunIot.gateway({ productKey: '', deviceName: '', deviceSecret: ''} ); gateway.on('connect', () => { gateway.regiestSubDevice([{"deviceName": "xxx","productKey": "xxx"}],(res)=>{ if(res.code == '200'){ // 注册成功请保存设备三元组,只会返回一次 } }); }); ```` ## 平台返回res统一格式 * `id` 发送请求的消息id,sdk中会自动生成 * `message` 返回的消息 * `data` 返回的数据 * `code` 服务端返回消息的状态码,常见code如下 * `200` 成功 * `400` 内部服务错误, 处理时发生内部错误 * `429` 请求过于频繁,设备端处理不过来时可以使用 * `460` 请求参数错误 * `520` 子设备会话不存在 ## 版本更新说明 #### 1.0.0版本更新 - 对核心代码进行了重构 - 增加设备标签上报功能 - 增加删除标签功能 - 增加了设备动态注册功能 - 增加设备影子相关功能 - 增加获取设备配置功能 - [非兼容性升级]网关设备之前的删除topo方法名和文档不一致,去掉了deleteTopo方法,统一使用removeTopo - [非兼容性升级]网关类方法,入参需要signUtil的逻辑去除,直接传入设备信息 - [非兼容性升级]去掉signUtil方法导出 - [非兼容性升级]初始化device和gateway时候签名方法参数从signAlgorithm改为signmethod - [非兼容性升级]设备的serve方法改成onService - [非兼容性升级]修改返回函数的res格式,取消err作为第一个参数,code,mssage都会放在res里面 - [非兼容性升级]去掉网关设备的方法 postSubDeviceProps,postSubDeviceEvent,serveSubDeviceService,改成使用子设备调用 ```` // aliyunIot.gateway#postSubDeviceProps() // aliyunIot.gateway#postSubDeviceEvent() // aliyunIot.gateway#serveSubDeviceService() const sub = gateway.login( sub_device1, (res)=>{console.log('>>>>>login',res);} ); sub.on('connect', () => { console.log(">>>>sub connected!"); sub.postProps({ LightSwitch: 0 },(res)=>{ console.log(">>>>sub postProps!"); console.log(res); }); }); setTimeout(()=>{ gateway.logout( sub_device1, (res)=>{console.log('>>>>>logout',res);} ); },5000) ```` #### 0.3.1版本更新 - productKey,deviceName,deviceSecret大小写经常容易忽略,新版本支持忽略大小写,也可以连接上 #### 0.3.0版本更新 1:初始化连接option选择增加支持keepalive和clean(cleansession)配置 2:修改regionId为region,并兼容之前的regionId参数