UNPKG

zigbee-shepherd

Version:

An open source ZigBee gateway solution with node.js.

237 lines (201 loc) 9 kB
/* jshint node: true */ 'use strict'; const Q = require('q'); const Areq = require('areq'); const znp = require('cc-znp'); const ZSC = require('zstack-constants'); const zdoHelper = require('./zdo_helper'); class Zdo { constructor(controller) { this._areq = new Areq(controller, 10000); } request(apiName, valObj, callback) { const requestType = zdoHelper.getRequestType(apiName); if (requestType === 'rspless') return this._rsplessRequest(apiName, valObj, callback); else if (requestType === 'generic') return this._genericRequest(apiName, valObj, callback); else if (requestType === 'concat') return this._concatRequest(apiName, valObj, callback); else if (requestType === 'special') return this._specialRequest(apiName, valObj, callback); else callback(new Error('Unknown request type.')); } _sendZdoRequestViaZnp(apiName, valObj, callback) { const zdoRequest = znp.zdoRequest.bind(znp); // bind zdo._sendZdoRequestViaZnp() to znp.zdoRequest() return zdoRequest(apiName, valObj, (err, rsp) => { let error = null; if (err) error = err; else if (apiName !== 'startupFromApp' && rsp.status !== 0) error = new Error(`request unsuccess: ${rsp.status}`); callback(error, rsp); }); } _rsplessRequest(apiName, valObj, callback) { return this._sendZdoRequestViaZnp(apiName, valObj, callback); } _genericRequest(apiName, valObj, callback) { const deferred = Q.defer(); const areq = this._areq; const areqEvtKey = zdoHelper.generateEventOfRequest(apiName, valObj); if (areqEvtKey) areq.register(areqEvtKey, deferred, payload => { areq.resolve(areqEvtKey, payload); }); this._sendZdoRequestViaZnp(apiName, valObj, (err, rsp) => { if (err) areq.reject(areqEvtKey, err); }); return deferred.promise.nodeify(callback); } _specialRequest(apiName, valObj, callback) { if (apiName === 'serverDiscReq') { // broadcast, remote device may not response when no bits match in mask // listener at controller.on('ZDO:serverDiscRsp') return this._rsplessRequest('serverDiscReq', valObj, callback); } else if (apiName === 'bindReq') { if (valObj.dstaddrmode === ZSC.AF.addressMode.ADDR_16BIT) callback(new Error('TI not support address 16bit mode.')); else return this._genericRequest('bindReq', valObj, callback); } else if (apiName === 'mgmtPermitJoinReq') { if (valObj.dstaddr === 0xFFFC) // broadcast to all routers (and coord), no waiting for AREQ rsp return this._rsplessRequest('mgmtPermitJoinReq', valObj, callback); else return this._genericRequest('mgmtPermitJoinReq', valObj, callback); } else { callback(new Error('No such request.')); } } _concatRequest(apiName, valObj, callback) { if (apiName === 'nwkAddrReq' || apiName === 'ieeeAddrReq') return this._concatAddrRequest(apiName, valObj, callback); else if (apiName === 'mgmtNwkDiscReq') return this._concatListRequest(apiName, valObj, { entries: 'networkcount', listcount: 'networklistcount', list: 'networklist' }, callback); else if (apiName === 'mgmtLqiReq') return this._concatListRequest(apiName, valObj, { entries: 'neighbortableentries', listcount: 'neighborlqilistcount', list: 'neighborlqilist' }, callback); else if (apiName === 'mgmtRtgReq') return this._concatListRequest(apiName, valObj, { entries: 'routingtableentries', listcount: 'routingtablelistcount', list: 'routingtablelist' }, callback); else if (apiName === 'mgmtBindRsp') return this._concatListRequest(apiName, valObj, { entries: 'bindingtableentries', listcount: 'bindingtablelistcount', list: 'bindingtablelist' }, callback); else callback(new Error('No such request.')); } _concatAddrRequest(apiName, valObj, callback) { const self = this; let totalToGet = null; let accum = 0; let nextIndex = valObj.startindex; const reqObj = { reqtype: valObj.reqtype, startindex: valObj.startindex // start from 0 }; const finalRsp = { status: null, ieeeaddr: null, nwkaddr: null, startindex: valObj.startindex, numassocdev: null, assocdevlist: [] }; if (apiName === 'nwkAddrReq') reqObj.ieeeaddr = valObj.ieeeaddr; else reqObj.shortaddr = valObj.shortaddr; const recursiveRequest = function () { self._genericRequest(apiName, reqObj, (err, rsp) => { if (err) { callback(err, finalRsp); } else if (rsp.status !== 0) { callback(new Error(`request unsuccess: ${rsp.status}`), finalRsp); } else { finalRsp.status = rsp.status; finalRsp.ieeeaddr = finalRsp.ieeeaddr || rsp.ieeeaddr; finalRsp.nwkaddr = finalRsp.nwkaddr || rsp.nwkaddr; finalRsp.numassocdev = finalRsp.numassocdev || rsp.numassocdev; finalRsp.assocdevlist = finalRsp.assocdevlist.concat(rsp.assocdevlist); totalToGet = totalToGet || (finalRsp.numassocdev - finalRsp.startindex); // compute at 1st rsp back accum = accum + rsp.assocdevlist.length; if (valObj.reqtype === 1 && accum < totalToGet) { // extended, include associated devices nextIndex = nextIndex + rsp.assocdevlist.length; reqObj.startindex = nextIndex; recursiveRequest(); } else { callback(null, finalRsp); } } }); }; recursiveRequest(); } _concatListRequest(apiName, valObj, listKeys, callback) { // valObj = { dstaddr[, scanchannels, scanduration], startindex } // listKeys = { entries: 'networkcount', listcount: 'networklistcount', list: 'networklist' }; const self = this; let totalToGet = null; let accum = 0; let nextIndex = valObj.startindex; const reqObj = { dstaddr: valObj.dstaddr, scanchannels: valObj.scanchannels, scanduration: valObj.scanduration, startindex: valObj.startindex // starts from 0 }; const finalRsp = { srcaddr: null, status: null, startindex: valObj.startindex }; finalRsp[listKeys.entries] = null; // finalRsp.networkcount = null finalRsp[listKeys.listcount] = null; // finalRsp.networklistcount = null finalRsp[listKeys.list] = []; // finalRsp.networklist = [] if (apiName === 'mgmtNwkDiscReq') { reqObj.scanchannels = valObj.scanchannels; reqObj.scanduration = valObj.scanduration; } const recursiveRequest = function () { self._genericRequest(apiName, reqObj, (err, rsp) => { if (err) { callback(err, finalRsp); } else if (rsp.status !== 0) { callback(new Error(`request unsuccess: ${rsp.status}`), finalRsp); } else { finalRsp.status = rsp.status; finalRsp.srcaddr = finalRsp.srcaddr || rsp.srcaddr; finalRsp[listKeys.entries] = finalRsp[listKeys.entries] || rsp[listKeys.entries]; finalRsp[listKeys.listcount] = rsp[listKeys.listcount]; finalRsp[listKeys.list] = finalRsp[listKeys.list].concat(rsp[listKeys.list]); totalToGet = totalToGet || (finalRsp[listKeys.entries] - finalRsp.startindex); accum = accum + rsp[listKeys.list].length; if (accum < totalToGet) { nextIndex = nextIndex + rsp[listKeys.list].length; reqObj.startindex = nextIndex; recursiveRequest(); } else { callback(null, finalRsp); } } }); }; recursiveRequest(); } } module.exports = Zdo;