'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;
|