UNPKG

zigbee-herdsman

Version:

An open source Zigbee gateway solution with node.js.

700 lines 29 kB
"use strict"; /* v8 ignore start */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.frameParserEvents = void 0; const node_events_1 = require("node:events"); const buffalo_1 = require("../../../buffalo"); const logger_1 = require("../../../utils/logger"); const Zdo = __importStar(require("../../../zspec/zdo")); const constants_1 = require("./constants"); const driver_1 = require("./driver"); const NS = "zh:deconz:frameparser"; const littleEndian = true; const lastReceivedGpInd = { srcId: 0, commandId: 0, frameCounter: 0 }; exports.frameParserEvents = new node_events_1.EventEmitter(); function parseReadParameterResponse(view) { const seqNumber = view.getUint8(1); const status = view.getUint8(2); const parameterId = view.getUint8(7); let pos = 8; let result = null; if (status !== constants_1.CommandStatus.Success) { if (parameterId in constants_1.ParamId) { logger_1.logger.debug(`Received read parameter response for ${constants_1.ParamId[parameterId]}, seq: ${seqNumber}, status: ${status}`, NS); } return result; } switch (parameterId) { case constants_1.ParamId.MAC_ADDRESS: { result = view.getBigUint64(pos, littleEndian); break; } case constants_1.ParamId.APS_TRUST_CENTER_ADDRESS: { result = view.getBigUint64(pos, littleEndian); break; } case constants_1.ParamId.NWK_PANID: { result = view.getUint16(pos, littleEndian); break; } case constants_1.ParamId.STK_PROTOCOL_VERSION: { result = view.getUint16(pos, littleEndian); break; } case constants_1.ParamId.NWK_NETWORK_ADDRESS: { result = view.getUint16(pos, littleEndian); break; } case constants_1.ParamId.NWK_EXTENDED_PANID: { result = view.getBigUint64(pos, littleEndian); break; } case constants_1.ParamId.APS_USE_EXTENDED_PANID: { result = view.getBigUint64(pos, littleEndian); break; } case constants_1.ParamId.STK_NETWORK_KEY: { result = Buffer.alloc(16); pos += 1; // key index for (let i = 0; i < 16; i++) { result[i] = view.getUint8(pos); pos += 1; } break; } case constants_1.ParamId.STK_CURRENT_CHANNEL: { result = view.getUint8(pos); break; } case constants_1.ParamId.STK_ENDPOINT: { result = Buffer.alloc(view.byteLength - pos); for (let i = 0; pos < view.byteLength; i++, pos++) { result[i] = view.getUint8(pos); } break; } case constants_1.ParamId.APS_CHANNEL_MASK: { result = view.getUint32(pos, littleEndian); break; } case constants_1.ParamId.STK_FRAME_COUNTER: { result = view.getUint32(pos, littleEndian); break; } case constants_1.ParamId.STK_PERMIT_JOIN: { result = view.getUint8(pos); break; } case constants_1.ParamId.DEV_WATCHDOG_TTL: { result = view.getUint32(pos, littleEndian); break; } case constants_1.ParamId.STK_NWK_UPDATE_ID: { result = view.getUint8(pos); break; } default: //throw new Error(`unknown parameter id ${parameterId}`); logger_1.logger.debug(`unknown parameter id ${parameterId}`, NS); break; } if (parameterId in constants_1.ParamId) { let p = result; if (parameterId === constants_1.ParamId.STK_NETWORK_KEY) { // don't show in logs p = "<hidden>"; } else if (typeof result === "bigint") { p = `0x${result.toString(16).padStart(16, "0")}`; } logger_1.logger.debug(`Received read parameter response for ${constants_1.ParamId[parameterId]}, seq: ${seqNumber}, status: ${status}, parameter: ${p}`, NS); } return result; } function parseReadFirmwareResponse(view) { const fw = view.getUint32(5, littleEndian); logger_1.logger.debug(`read firmware version response - version: 0x${fw.toString(16)}`, NS); return fw; } function parseDeviceStateResponse(view) { const deviceState = view.getUint8(5); exports.frameParserEvents.emit("deviceStateUpdated", deviceState); return deviceState; } function parseChangeNetworkStateResponse(view) { const status = view.getUint8(2); const state = view.getUint8(5); logger_1.logger.debug(`change network state - status: ${status} new state: ${state}`, NS); return state; } function parseApsConfirmResponse(view) { const buf = new buffalo_1.Buffalo(Buffer.from(view.buffer)); const commandId = buf.readUInt8(); const seqNr = buf.readUInt8(); const status = buf.readUInt8(); if (status !== constants_1.CommandStatus.Success) { logger_1.logger.debug(`Response APS-DATA.confirm seq: ${seqNr} status: ${constants_1.CommandStatus[status]} (error)`, NS); return null; } const frameLength = buf.readUInt16(); const payloadLength = buf.readUInt16(); // payload const deviceState = buf.readUInt8(); const requestId = buf.readUInt8(); const destAddrMode = buf.readUInt8(); let destAddr64; let destAddr16; let destEndpoint; let destAddr = ""; if (destAddrMode === constants_1.ApsAddressMode.Nwk) { destAddr16 = buf.readUInt16(); destAddr = destAddr16.toString(16).padStart(4, "0"); destEndpoint = buf.readUInt8(); } else if (destAddrMode === constants_1.ApsAddressMode.Group) { destAddr16 = buf.readUInt16(); destAddr = destAddr16.toString(16).padStart(4, "0"); } else if (destAddrMode === constants_1.ApsAddressMode.Ieee) { destAddr64 = buf.readUInt64().toString(16).padStart(16, "0"); destAddr = destAddr64; destEndpoint = buf.readUInt8(); } else { logger_1.logger.debug(`Response APS-DATA.confirm seq: ${seqNr} unsupported address mode: ${destAddrMode}`, NS); } const srcEndpoint = buf.readUInt8(); const confirmStatus = buf.readUInt8(); // resolve APS-DATA.request promise const i = driver_1.apsBusyQueue.findIndex((r) => r.request && r.request.requestId === requestId); if (i < 0) { logger_1.logger.debug(`Response APS-DATA.confirm seq: ${seqNr} but no matching request`, NS); return null; } const req = driver_1.apsBusyQueue[i]; //remove from busyqueue driver_1.apsBusyQueue.splice(i, 1); let strstatus = "unknown"; const hexstatus = `0x${confirmStatus.toString(16).padStart(2, "0")}`; if (confirmStatus in constants_1.ApsStatusCode) { strstatus = constants_1.ApsStatusCode[confirmStatus]; } // TODO(mpi): This function should not change the queue and resolve/reject the request promise. // It should only do what the name says and remaining parts handled in upper callframe. if (confirmStatus === constants_1.ApsStatusCode.Success) { req.resolve(confirmStatus); } else { // if the request failed check if we can resend with APS ACK enabled const hasTimeLeft = Date.now() - req.ts < req.request.timeout; if (req.request.txOptions === 0 && hasTimeLeft && req.request.destAddrMode === constants_1.ApsAddressMode.Nwk) { req.request.txOptions = 0x04; logger_1.logger.debug(`Resend request with APS ACK enabled seq: ${seqNr}`, NS); driver_1.apsQueue.push(req); } else { req.reject(new Error(`Failed APS-DATA.request with confirm status: ${strstatus} (${hexstatus})`)); } } logger_1.logger.debug(`APS-DATA.confirm destAddr: 0x${destAddr} APS request id: ${requestId} confirm status: ${strstatus} ${hexstatus}`, NS); exports.frameParserEvents.emit("deviceStateUpdated", deviceState); return { commandId, seqNr, status, frameLength, payloadLength, deviceState, requestId, destAddrMode, destAddr16, destAddr64, destEndpoint, srcEndpoint, confirmStatus, }; } // TODO(mpi): The ../buffalo/buffalo.ts already provides this, so we should reuse it instead of a own implementation?! class UDataView { littleEndian = true; pos = 0; view; constructor(view, littleEndian) { this.view = view; this.littleEndian = littleEndian; } getI8() { if (this.pos + 1 <= this.view.byteLength) { this.pos += 1; return this.view.getInt8(this.pos - 1); } throw new RangeError(); } getU8() { if (this.pos + 1 <= this.view.byteLength) { this.pos += 1; return this.view.getUint8(this.pos - 1); } throw new RangeError(); } getU16() { if (this.pos + 2 <= this.view.byteLength) { this.pos += 2; return this.view.getUint16(this.pos - 2, this.littleEndian); } throw new RangeError(); } getU32() { if (this.pos + 4 <= this.view.byteLength) { this.pos += 4; return this.view.getUint16(this.pos - 4, this.littleEndian); } throw new RangeError(); } getU64() { if (this.pos + 8 <= this.view.byteLength) { this.pos += 8; return this.view.getBigUint64(this.pos - 8, this.littleEndian); } throw new RangeError(); } } function parseApsDataIndicationResponse(inview) { // min 28 bytelength try { const uview = new UDataView(inview, true); const commandId = uview.getU8(); const seqNr = uview.getU8(); const status = uview.getU8(); if (status !== constants_1.CommandStatus.Success) { logger_1.logger.debug(`Response APS-DATA.indication seq: ${seqNr} status: ${constants_1.CommandStatus[status]}`, NS); return null; } const frameLength = uview.getU16(); const payloadLength = uview.getU16(); //------ start of payload ---------------------------------- const deviceState = uview.getU8(); const destAddrMode = uview.getU8(); let destAddr64; let destAddr16; let destAddr; if (destAddrMode === constants_1.ApsAddressMode.Ieee) { destAddr64 = uview.getU64().toString(16).padStart(16, "0"); destAddr16 = 0xfffe; destAddr = destAddr64; } else if (destAddrMode === constants_1.ApsAddressMode.Nwk || destAddrMode === constants_1.ApsAddressMode.Group) { destAddr16 = uview.getU16(); destAddr = destAddr16.toString(16); } else { throw new Error(`unsupported destination address mode: ${destAddrMode}`); } const destEndpoint = uview.getU8(); const srcAddrMode = uview.getU8(); let srcAddr64; let srcAddr16 = 0xfffe; let srcAddr; if (srcAddrMode === constants_1.ApsAddressMode.Nwk || srcAddrMode === constants_1.ApsAddressMode.NwkAndIeee) { srcAddr16 = uview.getU16(); srcAddr = srcAddr16.toString(16); if (srcAddrMode === constants_1.ApsAddressMode.NwkAndIeee) { srcAddr64 = uview.getU64().toString(16).padStart(16, "0"); } } else { throw new Error(`unsupported source address mode: ${srcAddrMode}`); } // else if (srcAddrMode === PARAM.PARAM.addressMode.IEEE_ADDR) { // srcAddr64 = uview.getU64().toString(16).padStart(16, '0'); // srcAddr = srcAddr64; // } const srcEndpoint = uview.getU8(); const profileId = uview.getU16(); const clusterId = uview.getU16(); const asduLength = uview.getU16(); const asdu = new Uint8Array(asduLength); for (let i = 0; i < asduLength; i++) { asdu[i] = uview.getU8(); } // The following two bytes depends on protocol version 2 or 3 // for now just discard uview.getU16(); const lqi = uview.getU8(); // version >= 2 let rssi = 0; try { rssi = uview.getI8(); } catch (_) { } logger_1.logger.debug(`Response APS-DATA.indication seq: ${seqNr} srcAddr: 0x${srcAddr} destAddr: 0x${destAddr} profile id: 0x${profileId.toString(16).padStart(4, "0")} cluster id: 0x${clusterId.toString(16).padStart(4, "0")} lqi: ${lqi}`, NS); //logger.debug(`Response payload: [${Array.from(asdu).map(x =>x.toString(16).padStart(2, '0')).join(' ')}]`, NS); exports.frameParserEvents.emit("deviceStateUpdated", deviceState); const asduPayload = Buffer.from(asdu); const response = { commandId, seqNr, status, frameLength, payloadLength, deviceState, destAddrMode, destAddr16, destAddr64, destEndpoint, srcAddrMode, srcAddr16, srcAddr64, srcEndpoint, profileId, clusterId, asduLength, asduPayload, lqi, rssi, zdo: profileId === Zdo.ZDO_PROFILE_ID ? Zdo.Buffalo.readResponse(true, clusterId, asduPayload) : undefined, }; exports.frameParserEvents.emit("receivedDataPayload", response); return response; } catch (error) { logger_1.logger.debug(`Response APS-DATA.indication error: ${error}`, NS); return null; } } function parseApsDataRequestResponse(view) { try { const status = view.getUint8(2); const requestId = view.getUint8(8); const deviceState = view.getUint8(7); logger_1.logger.debug(`Response APS-DATA.request APS request id: ${requestId} status: ${constants_1.CommandStatus[status]}`, NS); exports.frameParserEvents.emit("deviceStateUpdated", deviceState); return deviceState; } catch (error) { logger_1.logger.debug(`parseEnqueueSendDataResponse - ${error}`, NS); return null; } } function parseWriteParameterResponse(view) { try { const status = view.getUint8(2); const parameterId = view.getUint8(7); if (parameterId in constants_1.ParamId) { // should always be true logger_1.logger.debug(`Write parameter response parameter: ${constants_1.ParamId[parameterId]}, status: ${constants_1.CommandStatus[status]}`, NS); } return parameterId; } catch (error) { logger_1.logger.debug(`parseWriteParameterResponse - ${error}`, NS); return null; } } function parseDeviceStateChangedNotification(view) { try { const deviceState = view.getUint8(5); exports.frameParserEvents.emit("deviceStateUpdated", deviceState); return deviceState; } catch (error) { logger_1.logger.debug(`parsedeviceStateUpdated - ${error}`, NS); return null; } } // The ApplicationID sub-field contains the information about the application used by the GPD. // ApplicationID = 0b000 indicates the GPD_ID field has the length of 4B and contains the GPD SrcID. // ApplicationID = 0b010 indicates the GPD_ID field has the length of 8B and contains the GPD IEEE address. var GpApplicationId; (function (GpApplicationId) { GpApplicationId[GpApplicationId["SrcId4B"] = 0] = "SrcId4B"; GpApplicationId[GpApplicationId["Lped"] = 1] = "Lped"; GpApplicationId[GpApplicationId["Ieee8B"] = 2] = "Ieee8B"; })(GpApplicationId || (GpApplicationId = {})); var GpSecurityLevel; (function (GpSecurityLevel) { GpSecurityLevel[GpSecurityLevel["NoSecurity"] = 0] = "NoSecurity"; // TODO(mpi): "Reserved" is noted in the available spec but the code defined it as follows: GpSecurityLevel[GpSecurityLevel["Reserved"] = 1] = "Reserved"; GpSecurityLevel[GpSecurityLevel["FrameCounter4BMic4B"] = 2] = "FrameCounter4BMic4B"; GpSecurityLevel[GpSecurityLevel["EncryptionFrameCounter4BMic4B"] = 3] = "EncryptionFrameCounter4BMic4B"; })(GpSecurityLevel || (GpSecurityLevel = {})); var ZgpConstants; (function (ZgpConstants) { ZgpConstants[ZgpConstants["GpNwkProtocolVersion"] = 3] = "GpNwkProtocolVersion"; ZgpConstants[ZgpConstants["GpNwkDataFrame"] = 0] = "GpNwkDataFrame"; ZgpConstants[ZgpConstants["GpNwkMaintenanceFrame"] = 1] = "GpNwkMaintenanceFrame"; ZgpConstants[ZgpConstants["GpMinMsduSize"] = 1] = "GpMinMsduSize"; ZgpConstants[ZgpConstants["GpAutoCommissioningFlag"] = 64] = "GpAutoCommissioningFlag"; ZgpConstants[ZgpConstants["GpNwkFrameControlExtensionFlag"] = 128] = "GpNwkFrameControlExtensionFlag"; })(ZgpConstants || (ZgpConstants = {})); function parseGreenPowerDataIndication(view) { try { let srcId = 0; let frameCounter = 0; let commandId = 0; let commandFrameSize = 0; let commandFrame; const buf = new buffalo_1.Buffalo(Buffer.from(view.buffer)); const _fwCommandId = buf.readUInt8(); const seqNr = buf.readUInt8(); const fwStatus = buf.readUInt8(); if (fwStatus !== constants_1.CommandStatus.Success) { return null; } const _frameLength = buf.readUInt16(); const _payloadLength = buf.readUInt16(); // payload // implementation ported from deCONZ GP code const nwkFrameControl = buf.readUInt8(); // check frame type const frameType = nwkFrameControl & 0x03; if (frameType !== ZgpConstants.GpNwkDataFrame && frameType !== ZgpConstants.GpNwkMaintenanceFrame) { return null; } // check green power protocol version if (((nwkFrameControl >> 2) & 0x03) !== ZgpConstants.GpNwkProtocolVersion) { return null; } // extended frame control let nwkExtFrameControl = 0; const hasExtensionFlag = nwkFrameControl & ZgpConstants.GpNwkFrameControlExtensionFlag; if (hasExtensionFlag) { nwkExtFrameControl = buf.readUInt8(); } const options = nwkExtFrameControl | (nwkFrameControl << 8); const applicationId = nwkExtFrameControl & 7; const securityLevel = (nwkExtFrameControl >> 3) & 3; if (applicationId !== GpApplicationId.SrcId4B && applicationId !== GpApplicationId.Ieee8B) { return null; // NOTE: GpApplicationId.Lped (1) should be dropped as per spec } // The GPDSrcID field is present if the Frame Type sub-field is set to 0b00 and the ApplicationID sub- // field of the Extended NWK Frame Control field is set to 0b000 (or not present). if (applicationId === GpApplicationId.SrcId4B && frameType === ZgpConstants.GpNwkDataFrame /*|| (frameType === ZgpConstants.GpNwkMaintenanceFrame && hasExtensionFlag) */) { srcId = buf.readUInt32(); } // TODO(mpi): for applicationId == GpApplicationId.Ieee8B: // currently Ieee addresses aren't supported, do they actually appear?! // these need be extracted from MAC header which we don't have here (this is only the NWK payload). // frame counter filed frameCounter = 0; let micSize = 0; if (hasExtensionFlag && frameType === ZgpConstants.GpNwkDataFrame) { if (applicationId === GpApplicationId.Ieee8B) { const _endpoint = buf.readUInt8(); } // If the SecurityLevel is set to 0b00, the SecurityKey sub-field is ignored on reception, and the // fields Security frame counter and MIC are not present. if (securityLevel === GpSecurityLevel.Reserved) { micSize = 2; // TODO(mpi) does this actually exists? Check recent specs! } else if (securityLevel === GpSecurityLevel.FrameCounter4BMic4B || securityLevel === GpSecurityLevel.EncryptionFrameCounter4BMic4B) { frameCounter = buf.readUInt32(); micSize = 4; } } if (!buf.isMore()) { return null; } if (applicationId === GpApplicationId.SrcId4B || applicationId === GpApplicationId.Ieee8B) { commandId = buf.readUInt8(); commandFrameSize = buf.getBuffer().length - buf.getPosition() - micSize; //logger.debug(`GPD payload length: ${commandFrameSize}, mic size: ${micSize}`, NS); if (commandFrameSize < 0) { logger_1.logger.error(`GPD payload length < 0: ${commandFrameSize}`, NS); return null; } commandFrame = Buffer.from(buf.readBuffer(commandFrameSize)); // copy } // NOTE(mpi): The old adapter treated (view.byteLength < 30) as notification, larger as commissioning?! // The controller thus rejected commissioning frames. const id = 0; // 0 = notification, 4 = commissioning if (commandFrame === undefined) { logger_1.logger.debug("GPD discard frame since commandFrame is null?!", NS); return null; } const ind = { seqNr, id, options, srcId, frameCounter, commandId, commandFrameSize, commandFrame, }; // TODO(mpi): This only tracks one frame, might be a bit optimistic if (!(lastReceivedGpInd.srcId === srcId && lastReceivedGpInd.commandId === commandId && lastReceivedGpInd.frameCounter === frameCounter)) { lastReceivedGpInd.srcId = srcId; lastReceivedGpInd.commandId = commandId; lastReceivedGpInd.frameCounter = frameCounter; //logger.debug(`GP_DATA_INDICATION - src id: ${srcId} cmd id: ${commandId} frameCounter: ${frameCounter}`, NS); logger_1.logger.debug(`GP_DATA_INDICATION - src id: 0x${srcId.toString(16)} cmd id: 0x${commandId.toString(16)} frameCounter: 0x${frameCounter.toString(16)}`, NS); exports.frameParserEvents.emit("receivedGreenPowerIndication", ind); } return ind; } catch (error) { logger_1.logger.debug(`GREEN_POWER INDICATION - ${error}`, NS); return null; } } function parseMacPollCommand(_view) { //logger.debug("Received command MAC_POLL", NS); return constants_1.FirmwareCommand.MacPollIndication; } function parseBeaconRequest(_view) { logger_1.logger.debug("Received Beacon Request", NS); return constants_1.FirmwareCommand.Beacon; } function parseDebugLog(view) { let dbg = ""; const buf = new buffalo_1.Buffalo(Buffer.from(view.buffer)); /* const commandId = */ buf.readUInt8(); /* const seqNr = */ buf.readUInt8(); const status = buf.readUInt8(); if (status !== constants_1.CommandStatus.Success) { // unlikely return null; } /* const frameLength = */ buf.readUInt16(); const payloadLength = buf.readUInt16(); for (let i = 0; i < payloadLength && buf.isMore(); i++) { const ch = buf.readUInt8(); if (ch >= 32 && ch <= 127) { dbg += String.fromCharCode(ch); } } if (dbg.length !== 0) { logger_1.logger.debug(`firmware log: ${dbg}`, NS); } return null; } function parseUnknownCommand(view) { const id = view.getUint8(0); if (id in constants_1.FirmwareCommand) { logger_1.logger.debug(`received unsupported command: ${constants_1.FirmwareCommand[id]} id: 0x${id.toString(16).padStart(2, "0")}`, NS); } else { logger_1.logger.debug(`received unknown command: id: 0x${id.toString(16).padStart(2, "0")}`, NS); } return id; } function getParserForCommandId(id) { switch (id) { case constants_1.FirmwareCommand.ReadParameter: return parseReadParameterResponse; case constants_1.FirmwareCommand.WriteParameter: return parseWriteParameterResponse; case constants_1.FirmwareCommand.FirmwareVersion: return parseReadFirmwareResponse; case constants_1.FirmwareCommand.Status: return parseDeviceStateResponse; case constants_1.FirmwareCommand.ApsDataIndication: return parseApsDataIndicationResponse; case constants_1.FirmwareCommand.ApsDataRequest: return parseApsDataRequestResponse; case constants_1.FirmwareCommand.ApsDataConfirm: return parseApsConfirmResponse; case constants_1.FirmwareCommand.StatusChangeIndication: return parseDeviceStateChangedNotification; case constants_1.FirmwareCommand.ChangeNetworkState: return parseChangeNetworkStateResponse; case constants_1.FirmwareCommand.ZgpDataIndication: return parseGreenPowerDataIndication; case constants_1.FirmwareCommand.MacPollIndication: return parseMacPollCommand; case constants_1.FirmwareCommand.Beacon: return parseBeaconRequest; case constants_1.FirmwareCommand.DebugLog: return parseDebugLog; default: return parseUnknownCommand; //throw new Error(`unknown command id ${id}`); } } function processFrame(frame) { const [seqNumber, status, command, commandId] = parseFrame(frame); // logger.debug(`Process frame with cmd: 0x${commandId.toString(16).padStart(2,'0')}, seq: ${seqNumber} status: ${status}`, NS); let queue = driver_1.busyQueue; if (commandId === constants_1.FirmwareCommand.ApsDataRequest) { queue = driver_1.apsBusyQueue; } const i = queue.findIndex((r) => r.seqNumber === seqNumber && r.commandId === commandId); if (i < 0) { return; } const req = queue[i]; if (commandId === constants_1.FirmwareCommand.ApsDataRequest) { if (status === constants_1.CommandStatus.Success) { // wait for APS-DATA.confirm to arrive return; } // TODO(mpi): Within the timeout we should reschedule the APS-DATA.request (given that network state = connected) // continue to reject as there will be no APS-DATA.confirm } //remove from busyqueue queue.splice(i, 1); if (status === constants_1.CommandStatus.Success) { req.resolve(command); } else if (status === constants_1.CommandStatus.Unsupported && commandId === constants_1.FirmwareCommand.ReadParameter) { // resolve anyway to let higher layer handle unsupported req.resolve(command); } else { let cmdName; if (commandId in constants_1.FirmwareCommand) { cmdName = constants_1.FirmwareCommand[commandId]; } else { cmdName = `0x${commandId.toString(16).padStart(2, "0")}`; } req.reject(new Error(`Command ${cmdName} failed with status: ${constants_1.CommandStatus[status]}`)); } } function parseFrame(frame) { // at this point frame.buffer.length is at least 5 bytes long const view = new DataView(frame.buffer); const commandId = view.getUint8(0); const seqNumber = view.getUint8(1); const status = view.getUint8(2); const parser = getParserForCommandId(commandId); return [seqNumber, status, parser(view), commandId]; } exports.default = processFrame; //# sourceMappingURL=frameParser.js.map