'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var mqtt = require('mqtt'); var EventEmitter = require('events'); var util = require('util'); var _require = require('./utils'), createGuid = _require.createGuid, createDebug = _require.createDebug, getIP = _require.getIP, isJsonString = _require.isJsonString; var debug = createDebug('device'); var guid = createGuid(); var packagejson = require('../package.json'); var Model = require('./model'); var Device = function (_EventEmitter) { _inherits(Device, _EventEmitter); function Device() { var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, Device); //init var _this = _possibleConstructorReturn(this, (Device.__proto__ || Object.getPrototypeOf(Device)).call(this)); _this.model = new Model(config); _this._createClient(_this.model); //订阅topic _this._subscribePresetTopic(); //上报客户端sdk信息 _this._reportSDKInfo(); _this.serveCB = []; _this._onShadowCB; //兼容旧版本 _this._compatibleoverdue(); return _this; } // 发布消息到topic _createClass(Device, [{ key: 'publish', value: function publish() { var _mqttClient; (_mqttClient = this._mqttClient).publish.apply(_mqttClient, arguments); } // 订阅消息 }, { key: 'subscribe', value: function subscribe() { var _mqttClient2; (_mqttClient2 = this._mqttClient).subscribe.apply(_mqttClient2, arguments); } // 取消订阅消息 }, { key: 'unsubscribe', value: function unsubscribe() { var _mqttClient3; (_mqttClient3 = this._mqttClient).unsubscribe.apply(_mqttClient3, arguments); } }, { key: 'end', value: function end() { var _mqttClient4; (_mqttClient4 = this._mqttClient).end.apply(_mqttClient4, arguments); } // 属性快捷获取 }, { key: 'postProps', /* 高级版设备属性上报:详细文档地址:https://help.aliyun.com/document_detail/89301.html?spm=a2c4g.11186623.6.660.3ad223dcF1jjSU "params": {"key": "key","value": "value"} */ value: function postProps() { var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var cb = arguments[1]; this._publishAlinkMessage({ method: this.model.POST_PROPERY_METHOD, pubTopic: this.model.POST_PROPS_TOPIC, params: props }, cb); } /* 高级版设备事件上报:详细文档地址:https://help.aliyun.com/document_detail/89301.html?spm=a2c4g.11186623.6.660.3ad223dcF1jjSU "params": {"key": "key","value": "value"} */ }, { key: 'postEvent', value: function postEvent(eventName) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var cb = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function () {}; //event需要动态订阅topic返回值 if (this._addEventListenerFn == undefined) { this._addEventListenerFn = this._wrapEventSubscribe(eventName); } this._addEventListenerFn(); this._publishAlinkMessage({ method: this.model.getPostEventMethod(eventName), pubTopic: this.model.getPostEvenTopic(eventName), params: params }, cb); } //修改onService方法 }, { key: 'onService', value: function onService(serviceName, cb) { if (this._addServiceListenerFn == undefined) { this._addServiceListenerFn = this._wrapServiceSubscribe(serviceName, cb); } this._addServiceListenerFn(); this._pushReceiveServiceCallback(serviceName, cb); } /* 设备标签上报:详细文档地址:https://help.aliyun.com/document_detail/89304.html?spm=a2c4g.11174283.6.662.7e9d16685aIS1k "params": [{"attrKey": "key","attrValue": "value"},{},{}] 每次上报不会清除上一次的tag */ }, { key: 'postTags', value: function postTags() { var tags = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; var cb = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {}; this._publishAlinkMessage({ method: this.model.POST_TAGS_METHOD, pubTopic: this.model.TAG_TOPIC, params: tags }, cb); } // 删除设备标签 }, { key: 'deleteTags', value: function deleteTags() { var tags = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; var cb = arguments[1]; var params = tags.map(function (tag) { return { attrKey: tag }; }); this._publishAlinkMessage({ method: this.model.DELETE_TAGS_METHOD, pubTopic: this.model.TAG_DELETE_TOPIC, params: params }, cb); } /* * 获取远程配置 * 技术文档地址:https://help.aliyun.com/document_detail/89308.html?spm=a2c4g.11186623.6.666.53e168d0joDPCn * 返回数据格式 example { code: 200, data: { configId: '3cfda5091d5b4f53b51621cf4bbf86ec', configSize: 16, getType: 'file', sign: '0e89816213d1e462482311a16310424a2cbc5aeea79f7213934d087d97f8b763', signMethod: 'Sha256', url: 'https://otx-devicecenter-thing-config-cn-shanghai-online.oss-cn-shanghai.aliyuncs.com/a1aq9sQk2JE/1548394288572_h6z5LVjh?Expires=1548396135&OSSAccessKeyId=LTAIRY3rx5dg2JBm&Signature=27SgWTHTGRZ6k8UYk5uIELopJFk%3D' }, id: '3', method: 'thing.config.get', version: '1.0' } */ }, { key: 'getConfig', value: function getConfig() { var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {}; // 目前只支持产品维度和文件类型 var params = { "configScope": "product", "getType": "file" }; this._publishAlinkMessage({ method: this.model.GET_CONFIG_METHOD, pubTopic: this.model.CONFIG_TOPIC, params: params }, cb); } //当远程配置下发到设备触发 todo // onConfig(cb = () => {}) { // this._onConfig = cb; // // console.log(this.model.CONFIG_SUBSCRIBE_TOPIC); // this.subscribe(this.model.CONFIG_SUBSCRIBE_TOPIC, (error, res) => { // console.log('onConfig,sub error:', error, res); // }); // } // 获取设备影子数据 }, { key: 'getShadow', value: function getShadow() { var msg = { "method": "get" }; this._publishMessage(this.model.SHADOW_GET_TOPIC, msg); } // 设备上报实际值 }, { key: 'postShadow', value: function postShadow(reported) { var msg = { "method": "update", "state": { "reported": reported }, "version": Date.now() }; this._publishMessage(this.model.SHADOW_POST_TOPIC, msg); } // 删除影子设备 }, { key: 'deleteShadow', value: function deleteShadow(keys) { var reported = {}; if (typeof keys == 'string') { var key = keys; reported[key] = "null"; } if ((typeof keys === 'undefined' ? 'undefined' : _typeof(keys)) == "object") { keys.forEach(function (key) { reported[key] = "null"; }); } if (keys == undefined) { reported = "null"; } var msg = { "method": "delete", "state": { reported: reported }, "version": Date.now() }; this._publishMessage(this.model.SHADOW_POST_TOPIC, msg); } // 注册影子设备监听 }, { key: 'onShadow', value: function onShadow(cb) { this._onShadowCB = cb; } // responseService(serviceName,msg){ // const payload = JSON.stringify(msg); // this._mqttClient.publish(this.model.getServiceRespTopic(serviceName), payload) // } // 发送普通消息 }, { key: '_publishMessage', value: function _publishMessage(pubTopic) { var msg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var qos = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var payload = JSON.stringify(msg); this.publish(pubTopic, payload, { qos: qos }, function (err, res) { if (err) { // console.log('publish error', pubTopic, msg.id, err, res) } }); } // 发送alink协议消息 }, { key: '_publishAlinkMessage', value: function _publishAlinkMessage(_ref, cb) { var _ref$method = _ref.method, method = _ref$method === undefined ? "" : _ref$method, pubTopic = _ref.pubTopic, params = _ref.params, timeout = _ref.timeout; var id = guid(); //暂存回调函数 this._pushCallback(id, cb); var msg = this.model.genAlinkContent(method, params, id); var payload = JSON.stringify(msg); // console.log("_publishAlinkMessage:",payload); this.publish(pubTopic, payload, function (err, res) { debug('pub callback', pubTopic, msg.id, err, res); // console.log('pub callback', pubTopic, msg.id, err, res); if (err) { debug('publish error', pubTopic, msg.id, err, res); } }); } }, { key: '_createClient', value: function _createClient() { var client = mqtt.connect(this.model.brokerUrl, this.model.genConnectPrarms()); this._mqttClient = client; } }, { key: '_subscribePresetTopic', value: function _subscribePresetTopic() { var _this2 = this; ['connect', 'error', 'close', 'reconnect', 'offline', 'message'].forEach(function (evtName) { _this2._mqttClient.on(evtName, function () { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } debug('mqtt client ' + evtName); if (evtName === 'connect') { debug('mqtt connected'); //初始化只需要订阅 属性返回的topic和标签删除返回的topic,事件topic需要跟进event动态订阅,所以初始化不需要订阅 [ // devices _this2.model.POST_PROPS_REPLY_TOPIC, _this2.model.TAG_REPLY_TOPIC, _this2.model.TAG_DELETE_REPLY_TOPIC, _this2.model.CONFIG_REPLY_TOPIC, _this2.model.SHADOW_SUBSCRIBE_TOPIC, // gateway _this2.model.ADD_TOPO_REPLY_TOPIC, _this2.model.DELETE_TOPO_REPLY_TOPIC, _this2.model.GET_TOPO_REPLY_TOPIC, _this2.model.LOGIN_REPLY_TOPIC, _this2.model.LOGOUT_REPLY_TOPIC, _this2.model.SUBDEVICE_REGISTER_REPLY_TOPIC].forEach(function (replyTopic) { _this2.subscribe(replyTopic, { "qos": 1 }, function (error, res) { if (error) { debug('sub error:', error.toString); } }); }); } // 事件流到设备端开发者lib中的方式有2中,通过subscribe和通过callback if (evtName === 'message') { // 1:处理subscribe通知 _this2.emit.apply(_this2, [evtName].concat(args)); // 2:处理callback通知 _this2._mqttCallbackHandler.apply(_this2, args); return; } // 其他事件 'connect', 'error', 'close', 'reconnect', 'offline'处理 _this2.emit(evtName, args); }); }); } // 处理内部message以及各种方法的回调 }, { key: '_mqttCallbackHandler', value: function _mqttCallbackHandler(topic, message) { // 几种不处理的情况 // 情况1:回调函数为空 if (this._cb == [] && this._serviceCB == [] && this._onShadowCB == []) { return; } // 情况2:返回值为非结构化数据(非结构化可能是:基础版产品或是用户自定义topic) if (isJsonString(message.toString()) == false) { return; } // 开始处理返回值 var res = void 0; try { res = JSON.parse(message.toString()); var _res = res, method = _res.method; //处理物模型服务订阅返回数据 if (method.indexOf("thing.service") != -1) { this._onReceiveService(res); return; } // 影子设备reply和云端或应用下发影子配置通知,很久之前cmp定义的方法名称,所以格式与其他名称不太相同 if (method == "reply" || method == "control") { this._onShadowCB(res); return; } } catch (e) {} // 其他rrpc方法 finally { var _res2 = res, cbID = _res2.id; var callback = this._popCallback(cbID); if (callback) { callback(res); } } } // 查找回调函数,找到后使 }, { key: '_popCallback', value: function _popCallback(cbID) { var cb = this._cb[cbID] || function () {}; delete this._cb[cbID]; return cb; } }, { key: '_wrapEventSubscribe', value: function _wrapEventSubscribe(eventName) { var _this3 = this; var subscription = void 0; var fn = function fn() { //初始化 if (subscription == undefined) { subscription = {}; }; // 查找是否存在 if (subscription.eventName == undefined) { _this3.subscribe(_this3.model.getPostEvenReplyTopic(eventName), function (error, res) { if (error) { debug('sub error:', subTopic); } }); subscription.eventName = true; } }; return fn; } }, { key: '_wrapServiceSubscribe', value: function _wrapServiceSubscribe(serviceName, cb) { var _this4 = this; var subscription = void 0; var fn = function fn() { //初始化 if (subscription == undefined) { subscription = {}; }; // 查找是否存在 if (subscription.serviceName == undefined) { _this4.subscribe(_this4.model.getServiceTopic(serviceName), function (error, res) { if (error) { debug('sub error:', res); } }); subscription.serviceName = true; } }; return fn; } //处理接收云端下发服务调用 }, { key: '_onReceiveService', value: function _onReceiveService(res) { var method = res.method; var serviceName = method.split('.').pop(); var cb = this._serviceCB[serviceName] || function () {}; cb(res); } }, { key: '_pushCallback', value: function _pushCallback(msgid, fn) { // 初始化回调函数数组 if (this._cb == undefined) { this._cb = []; }; this._cb[msgid] = fn; } }, { key: '_pushReceiveServiceCallback', value: function _pushReceiveServiceCallback(serviceName, fn) { // 初始化回调函数数组 if (this._serviceCB == undefined) { this._serviceCB = []; }; this._serviceCB[serviceName] = fn; } }, { key: '_reportSDKInfo', value: function _reportSDKInfo() { //上报sdk版本信息 var networkInfo = 'WiFi|' + getIP(); var sdkInfo = []; var KVS = { 'SYS_SDK_LANGUAGE': 'NodeJS', 'SYS_LP_SDK_VERSION': packagejson.version, 'SYS_SDK_IF_INFO': networkInfo }; Object.keys(KVS).forEach(function (key) { sdkInfo.push({ "attrKey": key, "attrValue": KVS[key], "domain": "SYSTEM" }); }); this.postTags(sdkInfo); // console.log('publish sdk info',sdkInfo); } }, { key: '_compatibleoverdue', value: function _compatibleoverdue() { //兼容老版本serve 方法 0.5版本去掉 // this.serve = this.onService; } }, { key: 'connected', get: function get() { return this._mqttClient.connected; } }, { key: 'mqttClient', get: function get() { return this._mqttClient; } }]); return Device; }(EventEmitter); module.exports = Device;