zigbee-herdsman
Version:
An open source Zigbee gateway solution with node.js.
7,119 lines • 368 kB
JavaScript
"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;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Ezsp = void 0;
const node_events_1 = __importDefault(require("node:events"));
const async_mutex_1 = require("../../../utils/async-mutex");
const logger_1 = require("../../../utils/logger");
const ZSpec = __importStar(require("../../../zspec"));
const Zcl = __importStar(require("../../../zspec/zcl"));
const cluster_1 = require("../../../zspec/zcl/definition/cluster");
const Zdo = __importStar(require("../../../zspec/zdo"));
const endpoints_1 = require("../adapter/endpoints");
const consts_1 = require("../consts");
const enums_1 = require("../enums");
const ezspError_1 = require("../ezspError");
const ash_1 = require("../uart/ash");
const initters_1 = require("../utils/initters");
const math_1 = require("../utils/math");
const buffalo_1 = require("./buffalo");
const consts_2 = require("./consts");
const enums_2 = require("./enums");
const NS = "zh:ember:ezsp";
/** no multi-network atm, so just use const */
const DEFAULT_NETWORK_INDEX = endpoints_1.FIXED_ENDPOINTS[0].networkIndex;
/** other values not supported atm */
const DEFAULT_SLEEP_MODE = enums_2.EzspSleepMode.IDLE;
/** Maximum number of times we attempt to reset the NCP and start the ASH protocol. */
const MAX_INIT_ATTEMPTS = 5;
/**
* This is the max hops that the network can support - used to determine the max source route overhead
* and broadcast radius if we havent defined MAX_HOPS then define based on profile ID
*/
// #ifdef HAS_SECURITY_PROFILE_SE
// export const ZA_MAX_HOPS = 6;
// #else
const ZA_MAX_HOPS = 12;
// #endif
/**
* The mask applied to generated message tags used by the framework when sending messages via EZSP.
* Customers who call ezspSend functions directly must use message tags outside this mask.
*/
const MESSAGE_TAG_MASK = 0x7f;
/**
* Host EZSP layer.
*
* Provides functions that allow the Host application to send every EZSP command to the NCP.
*
* Commands to send to the serial>ASH layers all are named `ezsp${CommandName}`.
* They do nothing but build the command, send it and return the value(s).
* Callers are expected to handle errors appropriately.
* - They will throw `EzspStatus` if `sendCommand` fails or the returned value(s) by NCP are invalid (wrong length, etc).
* - Most will return `EmberStatus` given by NCP (some `EzspStatus`, some `SLStatus`...).
*/
class Ezsp extends node_events_1.default {
version;
ash;
buffalo;
/** The contents of the current EZSP frame. CAREFUL using this guy, it's pre-allocated. */
frameContents;
/** The total Length of the incoming frame */
frameLength;
callbackBuffalo;
/** The contents of the current EZSP frame. CAREFUL using this guy, it's pre-allocated. */
callbackFrameContents;
/** The total Length of the incoming frame */
callbackFrameLength;
initialVersionSent;
/** EZSP frame sequence number. Used in EZSP_SEQUENCE_INDEX byte. */
frameSequence;
/** Sequence used for EZSP send() tagging. static uint8_t */
sendSequence;
queue;
/** Awaiting response resolve/timer struct. undefined if not waiting for response. */
responseWaiter;
/** Counter for Queue Full errors */
counterErrQueueFull;
constructor(options) {
super();
this.frameContents = Buffer.alloc(consts_2.EZSP_MAX_FRAME_LENGTH);
this.buffalo = new buffalo_1.EzspBuffalo(this.frameContents, 0);
this.callbackFrameContents = Buffer.alloc(consts_2.EZSP_MAX_FRAME_LENGTH);
this.callbackBuffalo = new buffalo_1.EzspBuffalo(this.callbackFrameContents, 0);
this.queue = new async_mutex_1.AsyncMutex();
this.ash = new ash_1.UartAsh(options);
this.version = 0;
this.frameLength = 0;
this.callbackFrameLength = 0;
this.initialVersionSent = false;
this.frameSequence = -1; // start at 0
this.sendSequence = 0; // start at 1
this.counterErrQueueFull = 0;
}
/**
* Returns the number of EZSP responses that have been received by the serial
* protocol and are ready to be collected by the EZSP layer via
* responseReceived().
*/
get pendingResponseCount() {
return this.ash.rxQueue.length;
}
/**
* Create a string representation of the last received frame in storage.
*/
get frameToString() {
const id = this.buffalo.getFrameId();
return `[FRAME: ID=${id}:"${enums_2.EzspFrameID[id]}" Seq=${this.frameContents[consts_2.EZSP_SEQUENCE_INDEX]} Len=${this.frameLength}]`;
}
/**
* Create a string representation of the last received callback frame in storage.
*/
get callbackFrameToString() {
const id = this.callbackBuffalo.getFrameId();
return `[CBFRAME: ID=${id}:"${enums_2.EzspFrameID[id]}" Seq=${this.callbackFrameContents[consts_2.EZSP_SEQUENCE_INDEX]} Len=${this.callbackFrameLength}]`;
}
async start() {
logger_1.logger.info("======== EZSP starting ========", NS);
let status = enums_1.EzspStatus.HOST_FATAL_ERROR;
for (let i = 0; i < MAX_INIT_ATTEMPTS; i++) {
status = await this.ash.resetNcp();
// fail early if we couldn't even get the port set up
if (status !== enums_1.EzspStatus.SUCCESS) {
return status;
}
status = await this.ash.start();
if (status === enums_1.EzspStatus.SUCCESS) {
logger_1.logger.info("======== EZSP started ========", NS);
// registered after reset sequence
this.ash.on("frame", this.onAshFrame.bind(this));
this.ash.on("fatalError", this.onAshFatalError.bind(this));
return status;
}
}
return status;
}
/**
* Cleanly close down the serial protocol (UART).
* After this function has been called, init() must be called to resume communication with the NCP.
*/
async stop() {
await this.ash.stop();
this.ash.removeAllListeners();
logger_1.logger.info("======== EZSP stopped ========", NS);
}
/**
* Must be called immediately after `ezspVersion` to sync the Host protocol version.
* @param version Version for the Host to use.
*/
setProtocolVersion(version) {
this.version = version;
}
/**
* Check if connected.
* If not, attempt to restore the connection.
*
* @returns
*/
checkConnection() {
return this.ash.connected;
}
/**
* Triggered by ASH layer @see 'fatalError'
*/
onAshFatalError(status) {
logger_1.logger.error(`Fatal error, status=${enums_1.EzspStatus[status]}. Last Frame: ${this.frameToString}`, NS);
this.emit("ncpNeedsResetAndInit", status);
}
/**
* Triggered by ASH layer @see 'frame'
*/
onAshFrame() {
const buffer = this.ash.rxQueue.getPrecedingEntry();
if (buffer == null) {
// something is seriously wrong
logger_1.logger.error("Found no buffer in queue but ASH layer sent signal that one was available.", NS);
return;
}
this.ash.rxQueue.removeEntry(buffer);
// logger.debug(`<<<< ${buffer.data.subarray(0, buffer.len).toString('hex')}`, NS);
if (buffer.data[consts_2.EZSP_FRAME_CONTROL_INDEX] & consts_2.EZSP_FRAME_CONTROL_ASYNCH_CB) {
// take only what len tells us is actual content
buffer.data.copy(this.callbackFrameContents, 0, 0, buffer.len);
this.callbackFrameLength = buffer.len;
logger_1.logger.debug(`<=== ${this.callbackFrameToString}`, NS);
this.ash.rxFree.freeBuffer(buffer);
const status = this.validateReceivedFrame(this.callbackBuffalo);
if (status === enums_1.EzspStatus.SUCCESS) {
this.callbackDispatch();
}
else {
logger_1.logger.debug(`<=x= ${this.callbackFrameToString} Invalid, status=${enums_1.EzspStatus[status]}.`, NS);
}
}
else {
// take only what len tells us is actual content
buffer.data.copy(this.frameContents, 0, 0, buffer.len);
this.frameLength = buffer.len;
logger_1.logger.debug(`<=== ${this.frameToString}`, NS);
this.ash.rxFree.freeBuffer(buffer); // always
if (this.responseWaiter !== undefined) {
const status = this.validateReceivedFrame(this.buffalo);
clearTimeout(this.responseWaiter.timer);
this.responseWaiter.resolve(status);
this.responseWaiter = undefined; // done, gc
}
else {
logger_1.logger.debug("Received response while not expecting one. Ignoring.", NS);
}
}
}
/**
* Event from the EZSP layer indicating that the transaction with the NCP could not be completed due to a
* serial protocol error or that the response received from the NCP reported an error.
* The status parameter provides more information about the error.
*
* @param status
*/
ezspErrorHandler(status) {
const lastFrameStr = `Last Frame: ${this.frameToString}.`;
if (status === enums_1.EzspStatus.ERROR_QUEUE_FULL) {
this.counterErrQueueFull += 1;
logger_1.logger.error(`Adapter queue full (counter: ${this.counterErrQueueFull}). ${lastFrameStr}`, NS);
}
else if (status === enums_1.EzspStatus.ERROR_OVERFLOW) {
logger_1.logger.error(`The adapter has run out of buffers, causing general malfunction. Remediate network congestion, if present. ${lastFrameStr}`, NS);
}
else {
logger_1.logger.error(`ERROR Transaction failure; status=${enums_1.EzspStatus[status]}. ${lastFrameStr}`, NS);
}
// Do not reset if improper frame control direction flag (XXX: certain devices appear to trigger this somehow, without reason?)
// Do not reset if this is a decryption failure, as we ignored the packet
// Do not reset for a callback overflow or error queue, as we don't want the device to reboot under stress;
// resetting under these conditions does not solve the problem as the problem is external to the NCP.
// Throttling the additional traffic and staggering things might make it better instead.
// For all other errors, we reset the NCP
if (status !== enums_1.EzspStatus.ERROR_SECURITY_PARAMETERS_INVALID &&
status !== enums_1.EzspStatus.ERROR_OVERFLOW &&
status !== enums_1.EzspStatus.ERROR_QUEUE_FULL &&
status !== enums_1.EzspStatus.ERROR_WRONG_DIRECTION) {
this.emit("ncpNeedsResetAndInit", status);
}
}
nextFrameSequence() {
this.frameSequence = ++this.frameSequence & 0xff;
return this.frameSequence;
}
startCommand(command) {
const sendBuffalo = new buffalo_1.EzspBuffalo(Buffer.alloc(consts_2.EZSP_MAX_FRAME_LENGTH));
// Send initial EZSP_VERSION command with old packet format for old Hosts/NCPs
if (command === enums_2.EzspFrameID.VERSION && !this.initialVersionSent) {
sendBuffalo.setPosition(consts_2.EZSP_PARAMETERS_INDEX);
sendBuffalo.setCommandByte(consts_2.EZSP_FRAME_ID_INDEX, (0, math_1.lowByte)(command));
}
else {
// convert to extended frame format
sendBuffalo.setPosition(consts_2.EZSP_EXTENDED_PARAMETERS_INDEX);
sendBuffalo.setCommandByte(consts_2.EZSP_EXTENDED_FRAME_ID_LB_INDEX, (0, math_1.lowByte)(command));
sendBuffalo.setCommandByte(consts_2.EZSP_EXTENDED_FRAME_ID_HB_INDEX, (0, math_1.highByte)(command));
}
return sendBuffalo;
}
/**
* Sends the current EZSP command frame. Returns EZSP_SUCCESS if the command was sent successfully.
* Any other return value means that an error has been detected by the serial protocol layer.
*
* if ezsp.sendCommand fails early, this will be:
* - EzspStatus.ERROR_INVALID_CALL
* - EzspStatus.NOT_CONNECTED
* - EzspStatus.ERROR_COMMAND_TOO_LONG
*
* if ezsp.sendCommand fails, this will be whatever ash.send returns:
* - EzspStatus.SUCCESS
* - EzspStatus.NO_TX_SPACE
* - EzspStatus.DATA_FRAME_TOO_SHORT
* - EzspStatus.DATA_FRAME_TOO_LONG
* - EzspStatus.NOT_CONNECTED
*
* if ezsp.sendCommand times out, this will be EzspStatus.ASH_ERROR_TIMEOUTS
*
* if ezsp.sendCommand resolves, this will be whatever ezsp.responseReceived returns:
* - EzspStatus.NO_RX_DATA (should not happen if command was sent (since we subscribe to frame event to trigger function))
* - status from EzspFrameID.INVALID_COMMAND status byte
* - EzspStatus.ERROR_UNSUPPORTED_CONTROL
* - EzspStatus.ERROR_WRONG_DIRECTION
* - EzspStatus.ERROR_TRUNCATED
* - EzspStatus.SUCCESS
*/
async sendCommand(sendBuffalo) {
if (!this.checkConnection()) {
logger_1.logger.debug("[SEND COMMAND] NOT CONNECTED", NS);
return enums_1.EzspStatus.NOT_CONNECTED;
}
const sequence = this.nextFrameSequence();
sendBuffalo.setCommandByte(consts_2.EZSP_SEQUENCE_INDEX, sequence);
// we always set the network index in the ezsp frame control.
sendBuffalo.setCommandByte(consts_2.EZSP_EXTENDED_FRAME_CONTROL_LB_INDEX, consts_2.EZSP_FRAME_CONTROL_COMMAND |
(DEFAULT_SLEEP_MODE & consts_2.EZSP_FRAME_CONTROL_SLEEP_MODE_MASK) |
((DEFAULT_NETWORK_INDEX << consts_2.EZSP_FRAME_CONTROL_NETWORK_INDEX_OFFSET) & consts_2.EZSP_FRAME_CONTROL_NETWORK_INDEX_MASK));
// Send initial EZSP_VERSION command with old packet format for old Hosts/NCPs
if (!this.initialVersionSent && sendBuffalo.getCommandByte(consts_2.EZSP_FRAME_ID_INDEX) === enums_2.EzspFrameID.VERSION) {
this.initialVersionSent = true;
}
else {
sendBuffalo.setCommandByte(consts_2.EZSP_EXTENDED_FRAME_CONTROL_HB_INDEX, consts_2.EZSP_EXTENDED_FRAME_FORMAT_VERSION);
}
// might have tried to write more than allocated EZSP_MAX_FRAME_LENGTH for frameContents
// use write index to detect broken frames cases (inc'ed every time a byte is supposed to have been written)
// since index is always inc'ed on setCommandByte, this should always end at 202 max
const length = sendBuffalo.getPosition();
if (length > consts_2.EZSP_MAX_FRAME_LENGTH) {
// this.ezspErrorHandler(EzspStatus.ERROR_COMMAND_TOO_LONG);// XXX: this forces a NCP reset??
return enums_1.EzspStatus.ERROR_COMMAND_TOO_LONG;
}
return await this.queue.run(async () => {
let status = enums_1.EzspStatus.ASH_ERROR_TIMEOUTS; // will be overwritten below as necessary
const frameId = sendBuffalo.getFrameId();
const frameString = `[FRAME: ID=${frameId}:"${enums_2.EzspFrameID[frameId]}" Seq=${sequence} Len=${length}]`;
logger_1.logger.debug(`===> ${frameString}`, NS);
try {
status = await new Promise((resolve, reject) => {
const sendStatus = this.ash.send(length, sendBuffalo.getBuffer());
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
reject(new ezspError_1.EzspError(sendStatus));
return;
}
this.responseWaiter = {
timer: setTimeout(() => reject(new ezspError_1.EzspError(enums_1.EzspStatus.ASH_ERROR_TIMEOUTS)), this.ash.responseTimeout),
resolve,
};
});
if (status !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(status);
}
}
catch (error) {
this.responseWaiter = undefined;
logger_1.logger.debug(`=x=> ${frameString} ${error}`, NS);
if (error instanceof ezspError_1.EzspError) {
status = error.code;
this.ezspErrorHandler(status);
}
}
return status;
});
}
/**
* Sets the stage for parsing if valid (indexes buffalo to params index).
* @returns
*/
validateReceivedFrame(buffalo) {
const [retStatus, frameControl, frameId, parametersIndex] = buffalo.getResponseMetadata();
let status = retStatus;
if (frameId === enums_2.EzspFrameID.INVALID_COMMAND) {
status = buffalo.getResponseByte(parametersIndex);
}
if ((frameControl & consts_2.EZSP_FRAME_CONTROL_DIRECTION_MASK) !== consts_2.EZSP_FRAME_CONTROL_RESPONSE) {
status = enums_1.EzspStatus.ERROR_WRONG_DIRECTION;
}
if ((frameControl & consts_2.EZSP_FRAME_CONTROL_TRUNCATED_MASK) === consts_2.EZSP_FRAME_CONTROL_TRUNCATED) {
status = enums_1.EzspStatus.ERROR_TRUNCATED;
}
if ((frameControl & consts_2.EZSP_FRAME_CONTROL_OVERFLOW_MASK) === consts_2.EZSP_FRAME_CONTROL_OVERFLOW) {
status = enums_1.EzspStatus.ERROR_OVERFLOW;
}
if ((frameControl & consts_2.EZSP_FRAME_CONTROL_PENDING_CB_MASK) === consts_2.EZSP_FRAME_CONTROL_PENDING_CB) {
this.ash.ncpHasCallbacks = true;
}
else {
this.ash.ncpHasCallbacks = false;
}
// Set the callback network
//this.callbackNetworkIndex = (frameControl & EZSP_FRAME_CONTROL_NETWORK_INDEX_MASK) >> EZSP_FRAME_CONTROL_NETWORK_INDEX_OFFSET;
if (status !== enums_1.EzspStatus.SUCCESS) {
this.ezspErrorHandler(status);
}
buffalo.setPosition(parametersIndex);
// An overflow does not indicate a comms failure;
// The system can still communicate but buffers are running critically low.
// This is almost always due to network congestion and goes away when the network becomes quieter.
if (status === enums_1.EzspStatus.ERROR_OVERFLOW) {
status = enums_1.EzspStatus.SUCCESS;
}
return status;
}
/**
* Dispatches callback frames handlers.
*/
callbackDispatch() {
switch (this.callbackBuffalo.getExtFrameId()) {
case enums_2.EzspFrameID.NO_CALLBACKS: {
this.ezspNoCallbacks();
break;
}
case enums_2.EzspFrameID.STACK_TOKEN_CHANGED_HANDLER: {
const tokenAddress = this.callbackBuffalo.readUInt16();
this.ezspStackTokenChangedHandler(tokenAddress);
break;
}
case enums_2.EzspFrameID.TIMER_HANDLER: {
const timerId = this.callbackBuffalo.readUInt8();
this.ezspTimerHandler(timerId);
break;
}
case enums_2.EzspFrameID.COUNTER_ROLLOVER_HANDLER: {
const type = this.callbackBuffalo.readUInt8();
this.ezspCounterRolloverHandler(type);
break;
}
case enums_2.EzspFrameID.MUX_INVALID_RX_HANDLER: {
if (this.version >= 0x11) {
const newRxChannel = this.callbackBuffalo.readUInt8();
const oldRxChannel = this.callbackBuffalo.readUInt8();
this.ezspMuxInvalidRxHandler(newRxChannel, oldRxChannel);
}
break;
}
case enums_2.EzspFrameID.CUSTOM_FRAME_HANDLER: {
const payload = this.callbackBuffalo.readPayload();
this.ezspCustomFrameHandler(payload);
break;
}
case enums_2.EzspFrameID.STACK_STATUS_HANDLER: {
const status = this.callbackBuffalo.readStatus(this.version);
this.ezspStackStatusHandler(status);
break;
}
case enums_2.EzspFrameID.ENERGY_SCAN_RESULT_HANDLER: {
const channel = this.callbackBuffalo.readUInt8();
const maxRssiValue = this.callbackBuffalo.readInt8();
this.ezspEnergyScanResultHandler(channel, maxRssiValue);
break;
}
case enums_2.EzspFrameID.NETWORK_FOUND_HANDLER: {
const networkFound = this.callbackBuffalo.readEmberZigbeeNetwork();
const lastHopLqi = this.callbackBuffalo.readUInt8();
const lastHopRssi = this.callbackBuffalo.readInt8();
this.ezspNetworkFoundHandler(networkFound, lastHopLqi, lastHopRssi);
break;
}
case enums_2.EzspFrameID.SCAN_COMPLETE_HANDLER: {
const channel = this.callbackBuffalo.readUInt8();
const status = this.callbackBuffalo.readStatus(this.version);
this.ezspScanCompleteHandler(channel, status);
break;
}
case enums_2.EzspFrameID.UNUSED_PAN_ID_FOUND_HANDLER: {
const panId = this.callbackBuffalo.readUInt16();
const channel = this.callbackBuffalo.readUInt8();
this.ezspUnusedPanIdFoundHandler(panId, channel);
break;
}
case enums_2.EzspFrameID.CHILD_JOIN_HANDLER: {
const index = this.callbackBuffalo.readUInt8();
const joining = this.callbackBuffalo.readUInt8() !== 0;
const childId = this.callbackBuffalo.readUInt16();
const childEui64 = this.callbackBuffalo.readIeeeAddr();
const childType = this.callbackBuffalo.readUInt8();
this.ezspChildJoinHandler(index, joining, childId, childEui64, childType);
break;
}
case enums_2.EzspFrameID.DUTY_CYCLE_HANDLER: {
const channelPage = this.callbackBuffalo.readUInt8();
const channel = this.callbackBuffalo.readUInt8();
const state = this.callbackBuffalo.readUInt8();
const totalDevices = this.callbackBuffalo.readUInt8();
const arrayOfDeviceDutyCycles = this.callbackBuffalo.readEmberPerDeviceDutyCycle();
this.ezspDutyCycleHandler(channelPage, channel, state, totalDevices, arrayOfDeviceDutyCycles);
break;
}
case enums_2.EzspFrameID.REMOTE_SET_BINDING_HANDLER: {
const entry = this.callbackBuffalo.readEmberBindingTableEntry();
const index = this.callbackBuffalo.readUInt8();
const policyDecision = this.callbackBuffalo.readStatus(this.version);
this.ezspRemoteSetBindingHandler(entry, index, policyDecision);
break;
}
case enums_2.EzspFrameID.REMOTE_DELETE_BINDING_HANDLER: {
const index = this.callbackBuffalo.readUInt8();
const policyDecision = this.callbackBuffalo.readStatus(this.version);
this.ezspRemoteDeleteBindingHandler(index, policyDecision);
break;
}
case enums_2.EzspFrameID.MESSAGE_SENT_HANDLER: {
if (this.version < 0x0e) {
const type = this.callbackBuffalo.readUInt8();
const indexOrDestination = this.callbackBuffalo.readUInt16();
const apsFrame = this.callbackBuffalo.readEmberApsFrame();
const messageTag = this.callbackBuffalo.readUInt8();
const status = this.callbackBuffalo.readStatus(this.version);
// EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY set to messageTag only, so skip parsing entirely
// const messageContents = this.callbackBuffalo.readPayload();
this.ezspMessageSentHandler(status, type, indexOrDestination, apsFrame, messageTag);
}
else {
const status = this.callbackBuffalo.readUInt32();
const type = this.callbackBuffalo.readUInt8();
const indexOrDestination = this.callbackBuffalo.readUInt16();
const apsFrame = this.callbackBuffalo.readEmberApsFrame();
const messageTag = this.callbackBuffalo.readUInt16();
// EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY set to messageTag only, so skip parsing entirely
// const messageContents = this.callbackBuffalo.readPayload();
this.ezspMessageSentHandler(status, type, indexOrDestination, apsFrame, messageTag);
}
break;
}
case enums_2.EzspFrameID.POLL_COMPLETE_HANDLER: {
const status = this.callbackBuffalo.readStatus(this.version);
this.ezspPollCompleteHandler(status);
break;
}
case enums_2.EzspFrameID.POLL_HANDLER: {
const childId = this.callbackBuffalo.readUInt16();
const transmitExpected = this.callbackBuffalo.readUInt8() !== 0;
this.ezspPollHandler(childId, transmitExpected);
break;
}
case enums_2.EzspFrameID.INCOMING_MESSAGE_HANDLER: {
if (this.version < 0x0e) {
const type = this.callbackBuffalo.readUInt8();
const apsFrame = this.callbackBuffalo.readEmberApsFrame();
const lastHopLqi = this.callbackBuffalo.readUInt8();
const lastHopRssi = this.callbackBuffalo.readInt8();
const senderShortId = this.callbackBuffalo.readUInt16();
const bindingIndex = this.callbackBuffalo.readUInt8();
const addressIndex = this.callbackBuffalo.readUInt8();
const packetInfo = {
senderShortId,
senderLongId: ZSpec.BLANK_EUI64,
bindingIndex,
addressIndex,
lastHopLqi,
lastHopRssi,
lastHopTimestamp: 0,
};
const messageContents = this.callbackBuffalo.readPayload();
this.ezspIncomingMessageHandler(type, apsFrame, packetInfo, messageContents);
}
else {
const type = this.callbackBuffalo.readUInt8();
const apsFrame = this.callbackBuffalo.readEmberApsFrame();
const packetInfo = this.callbackBuffalo.readEmberRxPacketInfo();
const messageContents = this.callbackBuffalo.readPayload();
this.ezspIncomingMessageHandler(type, apsFrame, packetInfo, messageContents);
}
break;
}
case enums_2.EzspFrameID.INCOMING_MANY_TO_ONE_ROUTE_REQUEST_HANDLER: {
const source = this.callbackBuffalo.readUInt16();
const longId = this.callbackBuffalo.readIeeeAddr();
const cost = this.callbackBuffalo.readUInt8();
this.ezspIncomingManyToOneRouteRequestHandler(source, longId, cost);
break;
}
case enums_2.EzspFrameID.INCOMING_ROUTE_ERROR_HANDLER: {
const status = this.callbackBuffalo.readStatus(this.version);
const target = this.callbackBuffalo.readUInt16();
this.ezspIncomingRouteErrorHandler(status, target);
break;
}
case enums_2.EzspFrameID.INCOMING_NETWORK_STATUS_HANDLER: {
const errorCode = this.callbackBuffalo.readUInt8();
const target = this.callbackBuffalo.readUInt16();
this.ezspIncomingNetworkStatusHandler(errorCode, target);
break;
}
case enums_2.EzspFrameID.INCOMING_ROUTE_RECORD_HANDLER: {
const source = this.callbackBuffalo.readUInt16();
const sourceEui = this.callbackBuffalo.readIeeeAddr();
const lastHopLqi = this.callbackBuffalo.readUInt8();
const lastHopRssi = this.callbackBuffalo.readInt8();
const relayCount = this.callbackBuffalo.readUInt8();
const relayList = this.callbackBuffalo.readListUInt16(relayCount); //this.callbackBuffalo.readListUInt8(relayCount * 2);
this.ezspIncomingRouteRecordHandler(source, sourceEui, lastHopLqi, lastHopRssi, relayCount, relayList);
break;
}
case enums_2.EzspFrameID.ID_CONFLICT_HANDLER: {
const id = this.callbackBuffalo.readUInt16();
this.ezspIdConflictHandler(id);
break;
}
case enums_2.EzspFrameID.MAC_PASSTHROUGH_MESSAGE_HANDLER: {
if (this.version < 0x0e) {
const messageType = this.callbackBuffalo.readUInt8();
const lastHopLqi = this.callbackBuffalo.readUInt8();
const lastHopRssi = this.callbackBuffalo.readInt8();
const packetInfo = {
senderShortId: ZSpec.NULL_NODE_ID,
senderLongId: ZSpec.BLANK_EUI64,
bindingIndex: ZSpec.NULL_BINDING,
addressIndex: 0xff,
lastHopLqi,
lastHopRssi,
lastHopTimestamp: 0,
};
const messageContents = this.callbackBuffalo.readPayload();
this.ezspMacPassthroughMessageHandler(messageType, packetInfo, messageContents);
}
else {
const messageType = this.callbackBuffalo.readUInt8();
const packetInfo = this.callbackBuffalo.readEmberRxPacketInfo();
const messageContents = this.callbackBuffalo.readPayload();
this.ezspMacPassthroughMessageHandler(messageType, packetInfo, messageContents);
}
break;
}
case enums_2.EzspFrameID.MAC_FILTER_MATCH_MESSAGE_HANDLER: {
if (this.version < 0x0e) {
const filterIndexMatch = this.callbackBuffalo.readUInt8();
const legacyPassthroughType = this.callbackBuffalo.readUInt8();
const lastHopLqi = this.callbackBuffalo.readUInt8();
const lastHopRssi = this.callbackBuffalo.readInt8();
const packetInfo = {
senderShortId: ZSpec.NULL_NODE_ID,
senderLongId: ZSpec.BLANK_EUI64,
bindingIndex: ZSpec.NULL_BINDING,
addressIndex: 0xff,
lastHopLqi,
lastHopRssi,
lastHopTimestamp: 0,
};
const messageContents = this.callbackBuffalo.readPayload();
this.ezspMacFilterMatchMessageHandler(filterIndexMatch, legacyPassthroughType, packetInfo, messageContents);
}
else if (this.version < 0x12) {
const filterIndexMatch = this.callbackBuffalo.readUInt8();
const legacyPassthroughType = this.callbackBuffalo.readUInt8();
const packetInfo = this.callbackBuffalo.readEmberRxPacketInfo();
const messageContents = this.callbackBuffalo.readPayload();
this.ezspMacFilterMatchMessageHandler(filterIndexMatch, legacyPassthroughType, packetInfo, messageContents);
}
else {
const filterValueMatch = this.callbackBuffalo.readUInt16();
const legacyPassthroughType = this.callbackBuffalo.readUInt8();
const packetInfo = this.callbackBuffalo.readEmberRxPacketInfo();
const messageContents = this.callbackBuffalo.readPayload();
this.ezspMacFilterMatchMessageHandler(filterValueMatch, legacyPassthroughType, packetInfo, messageContents);
}
break;
}
case enums_2.EzspFrameID.RAW_TRANSMIT_COMPLETE_HANDLER: {
if (this.version < 0x0e) {
const status = this.callbackBuffalo.readStatus(this.version);
this.ezspRawTransmitCompleteHandler(Buffer.alloc(0), status);
}
else {
const messageContents = this.callbackBuffalo.readPayload();
const status = this.callbackBuffalo.readUInt32();
this.ezspRawTransmitCompleteHandler(messageContents, status);
}
break;
}
case enums_2.EzspFrameID.SWITCH_NETWORK_KEY_HANDLER: {
const sequenceNumber = this.callbackBuffalo.readUInt8();
this.ezspSwitchNetworkKeyHandler(sequenceNumber);
break;
}
case enums_2.EzspFrameID.ZIGBEE_KEY_ESTABLISHMENT_HANDLER: {
const partner = this.callbackBuffalo.readIeeeAddr();
const status = this.callbackBuffalo.readUInt8();
this.ezspZigbeeKeyEstablishmentHandler(partner, status);
break;
}
case enums_2.EzspFrameID.TRUST_CENTER_JOIN_HANDLER: {
const newNodeId = this.callbackBuffalo.readUInt16();
const newNodeEui64 = this.callbackBuffalo.readIeeeAddr();
const status = this.callbackBuffalo.readUInt8();
const policyDecision = this.callbackBuffalo.readUInt8();
const parentOfNewNodeId = this.callbackBuffalo.readUInt16();
this.ezspTrustCenterJoinHandler(newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId);
break;
}
case enums_2.EzspFrameID.GENERATE_CBKE_KEYS_HANDLER: {
const status = this.callbackBuffalo.readStatus(this.version);
const ephemeralPublicKey = this.callbackBuffalo.readEmberPublicKeyData();
this.ezspGenerateCbkeKeysHandler(status, ephemeralPublicKey);
break;
}
case enums_2.EzspFrameID.CALCULATE_SMACS_HANDLER: {
const status = this.callbackBuffalo.readStatus(this.version);
const initiatorSmac = this.callbackBuffalo.readEmberSmacData();
const responderSmac = this.callbackBuffalo.readEmberSmacData();
this.ezspCalculateSmacsHandler(status, initiatorSmac, responderSmac);
break;
}
case enums_2.EzspFrameID.GENERATE_CBKE_KEYS_HANDLER283K1: {
const status = this.callbackBuffalo.readStatus(this.version);
const ephemeralPublicKey = this.callbackBuffalo.readEmberPublicKey283k1Data();
this.ezspGenerateCbkeKeysHandler283k1(status, ephemeralPublicKey);
break;
}
case enums_2.EzspFrameID.CALCULATE_SMACS_HANDLER283K1: {
const status = this.callbackBuffalo.readStatus(this.version);
const initiatorSmac = this.callbackBuffalo.readEmberSmacData();
const responderSmac = this.callbackBuffalo.readEmberSmacData();
this.ezspCalculateSmacsHandler283k1(status, initiatorSmac, responderSmac);
break;
}
case enums_2.EzspFrameID.DSA_SIGN_HANDLER: {
const status = this.callbackBuffalo.readStatus(this.version);
const messageContents = this.callbackBuffalo.readPayload();
this.ezspDsaSignHandler(status, messageContents);
break;
}
case enums_2.EzspFrameID.DSA_VERIFY_HANDLER: {
const status = this.callbackBuffalo.readStatus(this.version);
this.ezspDsaVerifyHandler(status);
break;
}
case enums_2.EzspFrameID.MFGLIB_RX_HANDLER: {
const linkQuality = this.callbackBuffalo.readUInt8();
const rssi = this.callbackBuffalo.readInt8();
const packetContents = this.callbackBuffalo.readPayload();
this.ezspMfglibRxHandler(linkQuality, rssi, packetContents);
break;
}
case enums_2.EzspFrameID.INCOMING_BOOTLOAD_MESSAGE_HANDLER: {
if (this.version < 0x0e) {
const longId = this.callbackBuffalo.readIeeeAddr();
const lastHopLqi = this.callbackBuffalo.readUInt8();
const lastHopRssi = this.callbackBuffalo.readInt8();
const packetInfo = {
senderShortId: ZSpec.NULL_NODE_ID,
senderLongId: ZSpec.BLANK_EUI64,
bindingIndex: ZSpec.NULL_BINDING,
addressIndex: 0xff,
lastHopLqi,
lastHopRssi,
lastHopTimestamp: 0,
};
const messageContents = this.callbackBuffalo.readPayload();
this.ezspIncomingBootloadMessageHandler(longId, packetInfo, messageContents);
}
else {
const longId = this.callbackBuffalo.readIeeeAddr();
const packetInfo = this.callbackBuffalo.readEmberRxPacketInfo();
const messageContents = this.callbackBuffalo.readPayload();
this.ezspIncomingBootloadMessageHandler(longId, packetInfo, messageContents);
}
break;
}
case enums_2.EzspFrameID.BOOTLOAD_TRANSMIT_COMPLETE_HANDLER: {
const status = this.callbackBuffalo.readStatus(this.version);
const messageContents = this.callbackBuffalo.readPayload();
this.ezspBootloadTransmitCompleteHandler(status, messageContents);
break;
}
case enums_2.EzspFrameID.INCOMING_MFG_TEST_MESSAGE_HANDLER: {
const messageType = this.callbackBuffalo.readUInt8();
const messageContents = this.callbackBuffalo.readPayload();
this.ezspIncomingMfgTestMessageHandler(messageType, messageContents);
break;
}
case enums_2.EzspFrameID.ZLL_NETWORK_FOUND_HANDLER: {
if (this.version < 0x0e) {
const networkInfo = this.callbackBuffalo.readEmberZllNetwork();
const isDeviceInfoNull = this.callbackBuffalo.readUInt8() !== 0;
const deviceInfo = this.callbackBuffalo.readEmberZllDeviceInfoRecord();
const lastHopLqi = this.callbackBuffalo.readUInt8();
const lastHopRssi = this.callbackBuffalo.readInt8();
const packetInfo = {
senderShortId: ZSpec.NULL_NODE_ID,
senderLongId: ZSpec.BLANK_EUI64,
bindingIndex: ZSpec.NULL_BINDING,
addressIndex: 0xff,
lastHopLqi,
lastHopRssi,
lastHopTimestamp: 0,
};
this.ezspZllNetworkFoundHandler(networkInfo, isDeviceInfoNull, deviceInfo, packetInfo);
}
else {
const networkInfo = this.callbackBuffalo.readEmberZllNetwork();
const isDeviceInfoNull = this.callbackBuffalo.readUInt8() !== 0;
const deviceInfo = this.callbackBuffalo.readEmberZllDeviceInfoRecord();
const packetInfo = this.callbackBuffalo.readEmberRxPacketInfo();
this.ezspZllNetworkFoundHandler(networkInfo, isDeviceInfoNull, deviceInfo, packetInfo);
}
break;
}
case enums_2.EzspFrameID.ZLL_SCAN_COMPLETE_HANDLER: {
const status = this.callbackBuffalo.readStatus(this.version);
this.ezspZllScanCompleteHandler(status);
break;
}
case enums_2.EzspFrameID.ZLL_ADDRESS_ASSIGNMENT_HANDLER: {
if (this.version < 0x0e) {
const addressInfo = this.callbackBuffalo.readEmberZllAddressAssignment();
const lastHopLqi = this.callbackBuffalo.readUInt8();
const lastHopRssi = this.callbackBuffalo.readInt8();
const packetInfo = {
senderShortId: ZSpec.NULL_NODE_ID,
senderLongId: ZSpec.BLANK_EUI64,
bindingIndex: ZSpec.NULL_BINDING,
addressIndex: 0xff,
lastHopLqi,
lastHopRssi,
lastHopTimestamp: 0,
};
this.ezspZllAddressAssignmentHandler(addressInfo, packetInfo);
}
else {
const addressInfo = this.callbackBuffalo.readEmberZllAddressAssignment();
const packetInfo = this.callbackBuffalo.readEmberRxPacketInfo();
this.ezspZllAddressAssignmentHandler(addressInfo, packetInfo);
}
break;
}
case enums_2.EzspFrameID.ZLL_TOUCH_LINK_TARGET_HANDLER: {
const networkInfo = this.callbackBuffalo.readEmberZllNetwork();
this.ezspZllTouchLinkTargetHandler(networkInfo);
break;
}
case enums_2.EzspFrameID.D_GP_SENT_HANDLER: {
const status = this.callbackBuffalo.readStatus(this.version);
const gpepHandle = this.callbackBuffalo.readUInt8();
this.ezspDGpSentHandler(status, gpepHandle);
break;
}
case enums_2.EzspFrameID.GPEP_INCOMING_MESSAGE_HANDLER: {
const gpStatus = this.callbackBuffalo.readUInt8();
const gpdLink = this.callbackBuffalo.readUInt8();
const sequenceNumber = this.callbackBuffalo.readUInt8();
const addr = this.callbackBuffalo.readEmberGpAddress();
const gpdfSecurityLevel = this.callbackBuffalo.readUInt8();
const gpdfSecurityKeyType = this.callbackBuffalo.readUInt8();
const autoCommissioning = this.callbackBuffalo.readUInt8() !== 0;
const bidirectionalInfo = this.callbackBuffalo.readUInt8();
const gpdSecurityFrameCounter = this.callbackBuffalo.readUInt32();
const gpdCommandId = this.callbackBuffalo.readUInt8();
const mic = this.callbackBuffalo.readUInt32();
const proxyTableIndex = this.callbackBuffalo.readUInt8();
const gpdCommandPayload = this.callbackBuffalo.readPayload();
let packetInfo;
if (this.version < 0x10) {
packetInfo = {
senderShortId: ZSpec.NULL_NODE_ID,
senderLongId: ZSpec.BLANK_EUI64,
bindingIndex: ZSpec.NULL_BINDING,
addressIndex: 0xff,
lastHopLqi: gpdLink, // technically not accurate (this is rssi + lqi[0..3] map)
lastHopRssi: 0,
lastHopTimestamp: 0,
};
}
else {
packetInfo = this.callbackBuffalo.readEmberRxPacketInfo();
}
this.ezspGpepIncomingMessageHandler(gpStatus, gpdLink, sequenceNumber, addr, gpdfSecurityLevel, gpdfSecurityKeyType, autoCommissioning, bidirectionalInfo, gpdSecurityFrameCounter, gpdCommandId, mic, proxyTableIndex, gpdCommandPayload, packetInfo);
break;
}
default:
logger_1.logger.debug(`<=x= Ignored unused/unknown ${this.callbackFrameToString}`, NS);
}
}
/**
*
* @returns uint8_t
*/
nextSendSequence() {
this.sendSequence = ++this.sendSequence & MESSAGE_TAG_MASK;
return this.sendSequence;
}
/**
* Calls ezspSend${x} based on type and takes care of tagging message.
*
* Alias types expect `alias` & `sequence` params, along with `apsFrame.radius`.
*
* @param type Specifies the outgoing message type.
* @param indexOrDestination uint16_t Depending on the type of addressing used, this is either the NodeId of the destination,
* an index into the address table, or an index into the binding table.
* Unused for multicast types.
* This must be one of the three Zigbee broadcast addresses for broadcast.
* @param apsFrame [IN/OUT] EmberApsFrame * The APS frame which is to be added to the message.
* Sequence set in OUT as returned by ezspSend${x} command
* @param message uint8_t * Content of the message.
* @param alias The alias source address
* @param sequence uint8_t The alias sequence number
* @returns Result of the ezspSend${x} call or EmberStatus.INVALID_PARAMETER if type not supported.
* @returns messageTag Tag used for ezspSend${x} command
*/
async send(type, indexOrDestination, apsFrame, message, alias, sequence) {
let status = enums_1.SLStatus.INVALID_PARAMETER;
let apsSequence;
const messageTag = this.nextSendSequence();
let nwkRadius = ZA_MAX_HOPS;
let nwkAlias = ZSpec.NULL_NODE_ID;
switch (type) {
case enums_1.EmberOutgoingMessageType.VIA_BINDING:
case enums_1.EmberOutgoingMessageType.VIA_ADDRESS_TABLE:
case enums_1.EmberOutgoingMessageType.DIRECT: {
[status, apsSequence] = await this.ezspSendUnicast(type, indexOrDestination, apsFrame, messageTag, message);
break;
}
case enums_1.EmberOutgoingMessageType.MULTICAST:
case enums_1.EmberOutgoingMessageType.MULTICAST_WITH_ALIAS: {
if (type === enums_1.EmberOutgoingMessageType.MULTICAST_WITH_ALIAS ||
(apsFrame.sourceEndpoint === ZSpec.GP_ENDPOINT &&
apsFrame.destinationEndpoint === ZSpec.GP_ENDPOINT &&
apsFrame.options & enums_1.EmberApsOption.USE_ALIAS_SEQUENCE_NUMBER)) {
nwkRadius = apsFrame.radius ?? nwkRadius;
nwkAlias = alias;
}
[status, apsSequence] = await this.ezspSendMulticast(apsFrame, nwkRadius, 0, // broadcast addr
nwkAlias, sequence, messageTag, message);
break;
}
case enums_1.EmberOutgoingMessageType.BROADCAST:
case enums_1.EmberOutgoingMessageType.BROADCAST_WITH_ALIAS: {
if (type === enums_1.EmberOutgoingMessageType.BROADCAST_WITH_ALIAS ||
(apsFrame.sourceEndpoint === ZSpec.GP_ENDPOINT &&
apsFrame.destinationEndpoint === ZSpec.GP_ENDPOINT &&
apsFrame.options & enums_1.EmberApsOption.USE_ALIAS_SEQUENCE_NUMBER)) {
nwkRadius = apsFrame.radius ?? nwkRadius;
nwkAlias = alias;
}
[status, apsSequence] = await this.ezspSendBroadcast(nwkAlias, indexOrDestination, sequence, apsFrame, nwkRadius, messageTag, message);
break;
}
}
apsFrame.sequence = apsSequence;
// NOTE: match `~~~>` from adapter since this is just a wrapper for it
logger_1.logger.debug(`~~~> [SENT type=${enums_1.EmberOutgoingMessageType[type]} apsSequence=${apsSequence} messageTag=${messageTag} status=${enums_1.SLStatus[status]}]`, NS);
return [status, messageTag];
}
/**
* Retrieving the new version info.
* Wrapper for `ezspGetValue`.
* @returns Send status
* @returns EmberVersion*, null if status not SUCCESS.
*/
async ezspGetVersionStruct() {
const [status, outValueLength, outValue] = await this.ezspGetValue(enums_2.EzspValueId.VERSION_INFO, 7); // sizeof(EmberVersion)
if (outValueLength !== 7) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_VALUE);
}
return [
status,
{
build: outValue[0] + (outValue[1] << 8),
major: outValue[2],
minor: outValue[3],
patch: outValue[4],
special: outValue[5],
type: outValue[6],
},
];
}
/**
* Function for manipulating the endpoints flags on the NCP.
* Wrapper for `ezspGetExtendedValue`
* @param endpoint uint8_t
* @param flags EzspEndpointFlags
* @returns EzspStatus
*/
async ezspSetEndpointFlags(endpoint, flags) {
return await this.ezspSetValue(enums_2.EzspValueId.ENDPOINT_FLAGS, 3, [endpoint, (0, math_1.lowByte)(flags), (0, math_1.highByte)(flags)]);
}
/**
* Function for manipulating the endpoints flags on the NCP.
* Wrapper for `ezspGetExtendedValue`.
* @param endpoint uint8_t
* @returns SLStatus
* @returns flags
*/
async ezspGetEndpointFlags(endpoint) {
const [status, outValLen, outVal] = await this.ezspGetExtendedValue(enums_2.EzspExtendedValueId.ENDPOINT_FLAGS, endpoint, 2);
if (outValLen < 2) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_VALUE);
}
const returnFlags = (0, math_1.highLowToInt)(outVal[1], outVal[0]);
return [status, returnFlags];
}
/**
* Wrapper for `ezspGetExtendedValue`.
* @param NodeId
* @param destination
* @returns SLStatus
* @returns overhead uint8_t
*/
async ezspGetSourceRouteOverhead(destination) {
const [status, outValLen, outVal] = await this.ezspGetExtendedValue(enums_2.EzspExtendedValueId.GET_SOURCE_ROUTE_OVERHEAD, destination, 1);
if (outValLen < 1) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_VALUE);
}
return [status, outVal[0]];
}
/**
* Wrapper for `ezspGetExtendedValue`.
* @returns SLStatus
* @returns reason
* @returns nodeId NodeId*
*/
async ezspGetLastLeaveReason() {
const [status, outValLen, outVal] = await this.ezspGetExtendedValue(enums_2.EzspExtendedValueId.LAST_LEAVE_REASON, 0, 3);
if (outValLen < 3) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_VALUE);
}
return [status, outVal[0], (0, math_1.highLowToInt)(outVal[2], outVal[1])];
}
/**
* Gets memory usage data from sl_memory_manager APIs.
* Wrapper for `ezspGetExtendedValue`.
* @param type Type of memory usage data to be acquired
* @returns SLStatus
* @returns Data (in bytes) reflecting current or "boot" memory usage
*/
async ezspGetMemoryUsageData(type) {
if (this.version < 0x11) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const [status, outValLen, outVal] = await this.ezspGetExtendedValue(enums_2.EzspExtendedValueId.MEMORY_USAGE_DATA, type, 4);
if (outValLen < 4) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_VALUE);
}
return [status, outVal[0] + (outVal[1] << 8) + (outVal[2] << 16) + (outVal[3] << 24)];
}
/**
* Wrapper for `ezspGetValue`.
* @returns EzspStatus
* @returns reason
*/
async ezspGetLastRejoinReason() {
const [status, outValLen, outVal] = await this.ezspGetValue(enums_2.EzspValueId.LAST_REJOIN_REASON, 1);
if (outValLen < 1) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_VALUE);
}
return [status, outVal[0]];
}
/**
* Wrapper for `ezspSetValue`.
* @param mask
* @returns
*/
async ezspSetExtendedSecurityBitmask(mask) {
return await this.ezspSetValue(enums_2.EzspValueId.EXTENDED_SECURITY_BITMASK, 2, [(0, math_1.lowByte)(mask), (0, math_1.highByte)(mask)]);
}
/**
* Wrapper for `ezspGetValue`.
* @returns
*/
async ezspGetExtendedSecurityBitmask() {
const [status, outValLen, outVal] = await this.ezspGetValue(enums_2.EzspValueId.EXTENDED_SECURITY_BITMASK, 2);
if (outValLen < 2) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_VALUE);
}
return [status, (0, math_1.highLowToInt)(outVal[1], outVal[0])];
}
/**
* Wrapper for `ezspSetValue`.
* @returns
*/
async ezspStartWritingStackTokens() {
return await this.ezspSetValue(enums_2.EzspValueId.STACK_TOKEN_WRITING, 1, [1]);
}
/**
* Wrapper for `ezspSetValue`.
* @returns
*/
async ezspStopWritingStackTokens() {
return await this.ezspSetValue(enums_2.EzspValueId.STACK_TOKEN_WRITING, 1, [0]);
}
/**
* Wrapper for `ezspSetValue`.
*
* Set NWK layer outgoing frame counter (intended for device restoration purposes).
* Caveats:
* - Can only be called before NetworkInit / FormNetwork / JoinNetwork, when sl_zigbee_network_state()==SL_ZIGBEE_NO_NETWORK.
* - This function should be called before ::sl_zigbee_set_initial_security_state, and the SL_ZIGBEE_NO_FRAME_COUNTER_RESET
* bitmask should be added to the initial security bitmask when ::emberSetInitialSecurityState is called.
* - If used in multi-network context, be sure to call ::sl_zigbee_set_current_network() prior to calling this function.
*
* @param desiredValue The desired outgoing NWK frame counter value.
* This should needs to be less than MAX_INT32U_VALUE to ensure that rollover does not occur on the next encrypted transmission.
* @returns
* - SL_STATUS_OK if calling context is valid (sl_zigbee_network_state() == SL_ZIGBEE_NO_NETWORK) and desiredValue < MAX_INT32U_VALUE.
* - SL_STATUS_INVALID_STATE.
*/
async ezspSetNWKFrameCounter(frameCounter) {
return await this.ezspSetValue(enums_2.EzspValueId.NWK_FRAME_COUNTER, 4, [
frameCounter & 0xff,
(frameCounter >> 8) & 0xff,
(frameCounter >> 16) & 0xff,
(frameCounter >> 24) & 0xff,
]);
}
/**
* Wrapper for `ezspSetValue`.
*
* Function to set APS layer outgoing frame counter for Trust Center Link Key (intended for device restoration purposes).
* Caveats:
* - Can only be called before NetworkInit / FormNetwork / JoinNetwork, when sl_zigbee_network_state()==SL_ZIGBEE_NO_NETWORK.
* - This function should be called before ::sl_zigbee_set_initial_security_state, and the SL_ZIGBEE_NO_FRAME_COUNTER_RESET
* bitmask should be added to the initial security bitmask when ::emberSetInitialSecurityState is called.
* - If used in multi-network context, be sure to call ::sl_zigbee_set_current_network() prior to calling this function.
*
* @param desiredValue The desired outgoing APS frame counter value.
* This should needs to be less than MAX_INT32U_VALUE to ensure that rollover does not occur on the next encrypted transmission.
* @returns
* - SL_STATUS_OK if calling context is valid (sl_zigbee_network_state() == SL_ZIGBEE_NO_NETWORK) and desiredValue < MAX_INT32U_VALUE.
* - SL_STATUS_INVALID_STATE.
*/
async ezspSetAPSFrameCounter(frameCounter) {
return await this.ezspSetValue(enums_2.EzspValueId.APS_FRAME_COUNTER, 4, [
frameCounter & 0xff,
(frameCounter >> 8) & 0xff,
(frameCounter >> 16) & 0xff,
(frameCounter >> 24) & 0xff,
]);
}
//-----------------------------------------------------------------------------//
//---------------------------- START EZSP COMMANDS ----------------------------//
//-----------------------------------------------------------------------------//
//-----------------------------------------------------------------------------
// Configuration Frames
//-----------------------------------------------------------------------------
/**
* The command allows the Host to specify the desired EZSP version and must be
* sent before any other command. The response provides information about the
* firmware running on the NCP.
*
* @param desiredProtocolVersion uint8_t The EZSP version the Host wishes to use.
* To successfully set the version and allow other commands, this must be same as EZSP_PROTOCOL_VERSION.
* @returns uint8_t The EZSP version the NCP is using.
* @returns uint8_t * The type of stack running on the NCP (2).
* @returns uint16_t * The version number of the stack.
*/
async ezspVersion(desiredProtocolVersion) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.VERSION);
sendBuffalo.writeUInt8(desiredProtocolVersion);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const protocolVersion = this.buffalo.readUInt8();
const stackType = this.buffalo.readUInt8();
const stackVersion = this.buffalo.readUInt16();
return [protocolVersion, stackType, stackVersion];
}
/**
* Reads a configuration value from the NCP.
*
* @param configId Identifies which configuration value to read.
* @returns
* - SLStatus.OK if the value was read successfully,
* - SLStatus.ZIGBEE_EZSP_ERROR (for SL_ZIGBEE_EZSP_ERROR_INVALID_ID) if the NCP does not recognize configId.
* @returns uint16_t * The configuration value.
*/
async ezspGetConfigurationValue(configId) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_CONFIGURATION_VALUE);
sendBuffalo.writeUInt8(configId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version, false);
const value = this.buffalo.readUInt16();
return [status, value];
}
/**
* Writes a configuration value to the NCP. Configuration values can be modified
* by the Host after the NCP has reset. Once the status of the stack changes to
* EMBER_NETWORK_UP, configuration values can no longer be modified and this
* command will respond with EzspStatus.ERROR_INVALID_CALL.
*
* @param configId Identifies which configuration value to change.
* @param value uint16_t The new configuration value.
* @returns EzspStatus
* - SLStatus.OK if the configuration value was changed,
* - SLStatus.ZIGBEE_EZSP_ERROR if the new value exceeded the available memory,
* if the new value was out of bounds,
* if the NCP does not recognize configId,
* if configuration values can no longer be modified.
*/
async ezspSetConfigurationValue(configId, value) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_CONFIGURATION_VALUE);
sendBuffalo.writeUInt8(configId);
sendBuffalo.writeUInt16(value);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version, false);
return status;
}
/**
* Read attribute data on NCP endpoints.
* @param endpoint uint8_t Endpoint
* @param cluster uint16_t Cluster.
* @param attributeId uint16_t Attribute ID.
* @param mask uint8_t Mask.
* @param manufacturerCode uint16_t Manufacturer code.
* @returns An sl_zigbee_af_status_t value indicating success or the reason for failure, handled by the EZSP layer as a uint8_t.
* @returns uint8_t * Attribute data type.
* @returns uint8_t * Length of attribute data.
* @returns uint8_t * Attribute data.
*/
async ezspReadAttribute(endpoint, cluster, attributeId, mask, manufacturerCode, readLength) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.READ_ATTRIBUTE);
sendBuffalo.writeUInt8(endpoint);
sendBuffalo.writeUInt16(cluster);
sendBuffalo.writeUInt16(attributeId);
sendBuffalo.writeUInt8(mask);
sendBuffalo.writeUInt16(manufacturerCode);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const maxReadLength = readLength;
const status = this.buffalo.readStatus(0); // XXX: not yet switched to uint32 in v14, trick with 0 version for proper mapping
const dataType = this.buffalo.readUInt8();
readLength = this.buffalo.readUInt8();
if (readLength > maxReadLength) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_VALUE);
}
const data = this.buffalo.readListUInt8(readLength);
return [status, dataType, readLength, data];
}
/**
* Write attribute data on NCP endpoints.
* @param endpoint uint8_t Endpoint
* @param cluster uint16_t Cluster.
* @param attributeId uint16_t Attribute ID.
* @param mask uint8_t Mask.
* @param manufacturerCode uint16_t Manufacturer code.
* @param overrideReadOnlyAndDataType Override read only and data type.
* @param justTest Override read only and data type.
* @param dataType uint8_t Attribute data type.
* @param data uint8_t * Attribute data.
* @returns An sl_zigbee_af_status_t value indicating success or the reason for failure.
*/
async ezspWriteAttribute(endpoint, cluster, attributeId, mask, manufacturerCode, overrideReadOnlyAndDataType, justTest, dataType, data) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.WRITE_ATTRIBUTE);
sendBuffalo.writeUInt8(endpoint);
sendBuffalo.writeUInt16(cluster);
sendBuffalo.writeUInt16(attributeId);
sendBuffalo.writeUInt8(mask);
sendBuffalo.writeUInt16(manufacturerCode);
sendBuffalo.writeUInt8(overrideReadOnlyAndDataType ? 1 : 0);
sendBuffalo.writeUInt8(justTest ? 1 : 0);
sendBuffalo.writeUInt8(dataType);
sendBuffalo.writePayload(data);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(0); // XXX: not yet switched to uint32 in v14, trick with 0 version for proper mapping
return status;
}
/**
* Configures endpoint information on the NCP. The NCP does not remember these
* settings after a reset. Endpoints can be added by the Host after the NCP has
* reset. Once the status of the stack changes to EMBER_NETWORK_UP, endpoints
* can no longer be added and this command will respond with EzspStatus.ERROR_INVALID_CALL.
* @param endpoint uint8_t The application endpoint to be added.
* @param profileId uint16_t The endpoint's application profile.
* @param deviceId uint16_t The endpoint's device ID within the application profile.
* @param deviceVersion uint8_t The endpoint's device version.
* @param inputClusterList uint16_t * Input cluster IDs the endpoint will accept.
* @param outputClusterList uint16_t * Output cluster IDs the endpoint may send.
* @returns
* - SLStatus.OK if the endpoint was added,
* - SLStatus.ZIGBEE_EZSP_ERROR if there is not enough memory available to add the endpoint,
* if the endpoint already exists,
* if endpoints can no longer be added.
*/
async ezspAddEndpoint(endpoint, profileId, deviceId, deviceVersion, inputClusterList, outputClusterList) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ADD_ENDPOINT);
sendBuffalo.writeUInt8(endpoint);
sendBuffalo.writeUInt16(profileId);
sendBuffalo.writeUInt16(deviceId);
sendBuffalo.writeUInt8(deviceVersion);
sendBuffalo.writeUInt8(inputClusterList.length);
sendBuffalo.writeUInt8(outputClusterList.length);
sendBuffalo.writeListUInt16(inputClusterList);
sendBuffalo.writeListUInt16(outputClusterList);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version, false);
return status;
}
/**
* Allows the Host to change the policies used by the NCP to make fast
* decisions.
* @param policyId Identifies which policy to modify.
* @param decisionId The new decision for the specified policy.
* @returns
* - SLStatus.OK if the policy was changed,
* - SLStatus.ZIGBEE_EZSP_ERROR if the NCP does not recognize policyId.
*/
async ezspSetPolicy(policyId, decisionId) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_POLICY);
sendBuffalo.writeUInt8(policyId);
sendBuffalo.writeUInt8(decisionId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version, false);
return status;
}
/**
* Allows the Host to read the policies used by the NCP to make fast decisions.
* @param policyId Identifies which policy to read.
* @returns
* - SLStatus.OK if the policy was read successfully,
* - SLStatus.ZIGBEE_EZSP_ERROR if the NCP does not recognize policyId.
* @returns EzspDecisionId * The current decision for the specified policy.
*/
async ezspGetPolicy(policyId) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_POLICY);
sendBuffalo.writeUInt8(policyId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version, false);
const decisionId = this.buffalo.readUInt8();
return [status, decisionId];
}
/**
* Triggers a pan id update message.
* @param The new Pan Id
* @returns true if the request was successfully handed to the stack, false otherwise
*/
async ezspSendPanIdUpdate(newPan) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SEND_PAN_ID_UPDATE);
sendBuffalo.writeUInt16(newPan);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt8() !== 0;
return status;
}
/**
* Reads a value from the NCP.
* @param valueId Identifies which value to read.
* @returns
* - SLStatus.OK if the value was read successfully,
* - SLStatus.ZIGBEE_EZSP_ERROR if the NCP does not recognize valueId,
* if the length of the returned value exceeds the size of local storage allocated to receive it.
* @returns uint8_t * Both a command and response parameter.
* On command, the maximum in bytes of local storage allocated to receive the returned value.
* On response, the actual length in bytes of the returned value.
* @returns uint8_t * The value.
*/
async ezspGetValue(valueId, valueLength) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_VALUE);
sendBuffalo.writeUInt8(valueId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const maxValueLength = valueLength;
const status = this.buffalo.readStatus(this.version, false);
valueLength = this.buffalo.readUInt8();
if (valueLength > maxValueLength) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_VALUE);
}
const value = this.buffalo.readListUInt8(valueLength);
return [status, valueLength, value];
}
/**
* Reads a value from the NCP but passes an extra argument specific to the value
* being retrieved.
* @param valueId Identifies which extended value ID to read.
* @param characteristics uint32_t Identifies which characteristics of the extended value ID to read. These are specific to the value being read.
* @returns
* - SLStatus.OK if the value was read successfully,
* - SLStatus.ZIGBEE_EZSP_ERROR if the NCP does not recognize valueId,
* if the length of the returned value exceeds the size of local storage allocated to receive it.
* @returns uint8_t * Both a command and response parameter.
* On command, the maximum in bytes of local storage allocated to receive the returned value.
* On response, the actual length in bytes of the returned value.
* @returns uint8_t * The value.
*/
async ezspGetExtendedValue(valueId, characteristics, valueLength) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_EXTENDED_VALUE);
sendBuffalo.writeUInt8(valueId);
sendBuffalo.writeUInt32(characteristics);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
// let value: number[] = null;
const maxValueLength = valueLength;
const status = this.buffalo.readStatus(this.version, false);
valueLength = this.buffalo.readUInt8();
if (valueLength > maxValueLength) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_VALUE);
}
const value = this.buffalo.readListUInt8(valueLength);
return [status, valueLength, value];
}
/**
* Writes a value to the NCP.
* @param valueId Identifies which value to change.
* @param valueLength uint8_t The length of the value parameter in bytes.
* @param value uint8_t * The new value.
* @returns
* - SLStatus.OK if the value was changed,
* - SLStatus.ZIGBEE_EZSP_ERROR if the new value was out of bounds,
* if the NCP does not recognize valueId,
* if the value could not be modified.
*/
async ezspSetValue(valueId, valueLength, value) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_VALUE);
sendBuffalo.writeUInt8(valueId);
sendBuffalo.writeUInt8(valueLength);
sendBuffalo.writeListUInt8(value);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version, false);
return status;
}
/**
* Allows the Host to control the broadcast behaviour of a routing device used by the NCP.
* @param config uint8_t Passive ack config enum.
* @param minAcksNeeded uint8_t The minimum number of acknowledgments (re-broadcasts) to wait for until
* deeming the broadcast transmission complete.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspSetPassiveAckConfig(config, minAcksNeeded) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_PASSIVE_ACK_CONFIG);
sendBuffalo.writeUInt8(config);
sendBuffalo.writeUInt8(minAcksNeeded);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Set the PAN ID to be accepted by the device in a NLME Network Update command.
* If this is set to a different value than its default 0xFFFF, NLME network update messages will be ignored if they do not match this PAN ID.
* @param panId uint16_t PAN ID to be accepted in a network update.
*/
async ezspSetPendingNetworkUpdatePanId(panId) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_PENDING_NETWORK_UPDATE_PAN_ID);
sendBuffalo.writeUInt16(panId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Retrieve the endpoint number located at the specified index.
* @param index uint8_t Index to retrieve the endpoint number for.
* @returns uint8_t Endpoint number at the index.
*/
async ezspGetEndpoint(index) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_ENDPOINT);
sendBuffalo.writeUInt8(index);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const endpoint = this.buffalo.readUInt8();
return endpoint;
}
/**
* Get the number of configured endpoints.
* @returns uint8_t Number of configured endpoints.
*/
async ezspGetEndpointCount() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_ENDPOINT_COUNT);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const count = this.buffalo.readUInt8();
return count;
}
/**
* Retrieve the endpoint description for the given endpoint number.
* @param endpoint Endpoint number to get the description of.
* @returns Description of this endpoint.
*/
async ezspGetEndpointDescription(endpoint) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_ENDPOINT_DESCRIPTION);
sendBuffalo.writeUInt8(endpoint);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const endpointDescription = this.buffalo.readEmberEndpointDescription();
return endpointDescription;
}
/**
* Retrieve one of the cluster IDs associated with the given endpoint.
* @param endpoint Endpoint number to get a cluster ID for.
* @param listId Which list to get the cluster ID from. (0 for input, 1 for output).
* @param listIndex Index from requested list to look at the cluster ID of.
* @returns ID of the requested cluster.
*/
async ezspGetEndpointCluster(endpoint, listId, listIndex) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_ENDPOINT_CLUSTER);
sendBuffalo.writeUInt8(endpoint);
sendBuffalo.writeUInt8(listId);
sendBuffalo.writeUInt8(listIndex);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const endpointCluster = this.buffalo.readUInt16();
return endpointCluster;
}
//-----------------------------------------------------------------------------
// Utilities Frames
//-----------------------------------------------------------------------------
/**
* A command which does nothing. The Host can use this to set the sleep mode or to check the status of the NCP.
*/
async ezspNop() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.NOP);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Variable length data from the Host is echoed back by the NCP.
* This command has no other effects and is designed for testing the link between the Host and NCP.
* @param data uint8_t * The data to be echoed back.
* @returns uint8_t * The echo of the data.
*/
async ezspEcho(data) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ECHO);
sendBuffalo.writePayload(data);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const echo = this.buffalo.readPayload();
if (echo.length > data.length) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_VALUE);
}
return echo;
}
/**
* Allows the NCP to respond with a pending callback.
*/
async ezspCallback() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CALLBACK);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
this.callbackDispatch();
}
/**
* Callback
* Indicates that there are currently no pending callbacks.
*/
ezspNoCallbacks() {
logger_1.logger.debug("ezspNoCallbacks(): callback called", NS);
}
/**
* Sets a token (8 bytes of non-volatile storage) in the Simulated EEPROM of the NCP.
* @param tokenId uint8_t Which token to set
* @param tokenData uint8_t * The data to write to the token.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspSetToken(tokenId, tokenData) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_TOKEN);
sendBuffalo.writeUInt8(tokenId);
sendBuffalo.writeListUInt8(tokenData);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Retrieves a token (8 bytes of non-volatile storage) from the Simulated EEPROM of the NCP.
* @param tokenId uint8_t Which token to read
* @returns An SLStatus value indicating success or the reason for failure.
* @returns uint8_t * The contents of the token.
*/
async ezspGetToken(tokenId) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_TOKEN);
sendBuffalo.writeUInt8(tokenId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const tokenData = this.buffalo.readListUInt8(8);
return [status, tokenData];
}
/**
* Retrieves a manufacturing token from the Flash Information Area of the NCP
* (except for EZSP_STACK_CAL_DATA which is managed by the stack).
* @param tokenId Which manufacturing token to read.
* @returns uint8_t The length of the tokenData parameter in bytes.
* @returns uint8_t * The manufacturing token data.
*/
async ezspGetMfgToken(tokenId) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_MFG_TOKEN);
sendBuffalo.writeUInt8(tokenId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const tokenDataLength = this.buffalo.readUInt8();
let expectedTokenDataLength = 0;
// the size of corresponding the EZSP Mfg token, please refer to app/util/ezsp/ezsp-enum.h
switch (tokenId) {
// 2 bytes
case enums_2.EzspMfgTokenId.CUSTOM_VERSION:
case enums_2.EzspMfgTokenId.MANUF_ID:
case enums_2.EzspMfgTokenId.PHY_CONFIG:
case enums_2.EzspMfgTokenId.CTUNE:
expectedTokenDataLength = 2;
break;
// 8 bytes
case enums_2.EzspMfgTokenId.EZSP_STORAGE:
case enums_2.EzspMfgTokenId.CUSTOM_EUI_64:
expectedTokenDataLength = 8;
break;
// 16 bytes
case enums_2.EzspMfgTokenId.STRING:
case enums_2.EzspMfgTokenId.BOARD_NAME:
case enums_2.EzspMfgTokenId.BOOTLOAD_AES_KEY:
expectedTokenDataLength = 16;
break;
// 20 bytes
case enums_2.EzspMfgTokenId.INSTALLATION_CODE:
expectedTokenDataLength = 20;
break;
// 40 bytes
case enums_2.EzspMfgTokenId.ASH_CONFIG:
expectedTokenDataLength = 40;
break;
// 92 bytes
case enums_2.EzspMfgTokenId.CBKE_DATA:
expectedTokenDataLength = 92;
break;
default:
break;
}
if (tokenDataLength !== expectedTokenDataLength) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_VALUE);
}
const tokenData = this.buffalo.readListUInt8(tokenDataLength);
return [tokenDataLength, tokenData];
}
/**
* Sets a manufacturing token in the Customer Information Block (CIB) area of
* the NCP if that token currently unset (fully erased). Cannot be used with
* EZSP_STACK_CAL_DATA, EZSP_STACK_CAL_FILTER, EZSP_MFG_ASH_CONFIG, or
* EZSP_MFG_CBKE_DATA token.
* @param tokenId Which manufacturing token to set.
* @param tokenData uint8_t * The manufacturing token data.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspSetMfgToken(tokenId, tokenData) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_MFG_TOKEN);
sendBuffalo.writeUInt8(tokenId);
sendBuffalo.writePayload(tokenData);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Callback
* A callback invoked to inform the application that a stack token has changed.
* @param tokenAddress uint16_t The address of the stack token that has changed.
*/
ezspStackTokenChangedHandler(tokenAddress) {
logger_1.logger.debug(`ezspStackTokenChangedHandler: tokenAddress=${tokenAddress}`, NS);
}
/**
* Returns a pseudorandom number.
* @returns Always returns SLStatus.OK.
* @returns uint16_t * A pseudorandom number.
*/
async ezspGetRandomNumber() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_RANDOM_NUMBER);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const value = this.buffalo.readUInt16();
return [status, value];
}
/**
* Sets a timer on the NCP. There are 2 independent timers available for use by the Host.
* A timer can be cancelled by setting time to 0 or units to EMBER_EVENT_INACTIVE.
* @param timerId uint8_t Which timer to set (0 or 1).
* @param time uint16_t The delay before the timerHandler callback will be generated.
* Note that the timer clock is free running and is not synchronized with this command.
* This means that the actual delay will be between time and (time - 1). The maximum delay is 32767.
* @param units The units for time.
* @param repeat If true, a timerHandler callback will be generated repeatedly. If false, only a single timerHandler callback will be generated.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspSetTimer(timerId, time, units, repeat) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_TIMER);
sendBuffalo.writeUInt8(timerId);
sendBuffalo.writeUInt16(time);
sendBuffalo.writeUInt8(units);
sendBuffalo.writeUInt8(repeat ? 1 : 0);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Gets information about a timer. The Host can use this command to find out how
* much longer it will be before a previously set timer will generate a
* callback.
* @param timerId uint8_t Which timer to get information about (0 or 1).
* @returns uint16_t The delay before the timerHandler callback will be generated.
* @returns EmberEventUnits * The units for time.
* @returns bool * True if a timerHandler callback will be generated repeatedly. False if only a single timerHandler callback will be generated.
*/
async ezspGetTimer(timerId) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_TIMER);
sendBuffalo.writeUInt8(timerId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const time = this.buffalo.readUInt16();
const units = this.buffalo.readUInt8();
const repeat = this.buffalo.readUInt8() !== 0;
return [time, units, repeat];
}
/**
* Callback
* A callback from the timer.
* @param timerId uint8_t Which timer generated the callback (0 or 1).
*/
ezspTimerHandler(timerId) {
logger_1.logger.debug(`ezspTimerHandler: timerId=${timerId}`, NS);
}
/**
* Sends a debug message from the Host to the Network Analyzer utility via the NCP.
* @param binaryMessage true if the message should be interpreted as binary data, false if the message should be interpreted as ASCII text.
* @param messageContents uint8_t * The binary message.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspDebugWrite(binaryMessage, messageContents) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.DEBUG_WRITE);
sendBuffalo.writeUInt8(binaryMessage ? 1 : 0);
sendBuffalo.writePayload(messageContents);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Retrieves and clears Ember counters. See the EmberCounterType enumeration for the counter types.
* @returns uint16_t * A list of all counter values ordered according to the EmberCounterType enumeration.
*/
async ezspReadAndClearCounters() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.READ_AND_CLEAR_COUNTERS);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const values = this.buffalo.readListUInt16(enums_1.EmberCounterType.COUNT);
return values;
}
/**
* Retrieves Ember counters. See the EmberCounterType enumeration for the counter types.
* @returns uint16_t * A list of all counter values ordered according to the EmberCounterType enumeration.
*/
async ezspReadCounters() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.READ_COUNTERS);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const values = this.buffalo.readListUInt16(enums_1.EmberCounterType.COUNT);
return values;
}
/**
* Callback
* This call is fired when a counter exceeds its threshold
* @param type Type of Counter
*/
ezspCounterRolloverHandler(type) {
logger_1.logger.debug(`ezspCounterRolloverHandler: type=${enums_1.EmberCounterType[type]}`, NS);
logger_1.logger.info(`NCP Counter ${enums_1.EmberCounterType[type]} rolled over.`, NS);
}
/**
* Callback
* This call is fired when mux detects an invalid rx case, which would be different rx channels for different protocol contexts, when fast channel switching is not enabled
* @param newRxChannel uint8_t
* @param oldRxChannel uint8_t
*/
ezspMuxInvalidRxHandler(newRxChannel, oldRxChannel) {
logger_1.logger.debug(`ezspMuxInvalidRxHandler: newRxChannel=${newRxChannel} oldRxChannel=${oldRxChannel}`, NS);
}
/**
* Used to test that UART flow control is working correctly.
* @param delay uint16_t Data will not be read from the host for this many milliseconds.
*/
async ezspDelayTest(delay) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.DELAY_TEST);
sendBuffalo.writeUInt16(delay);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* This retrieves the status of the passed library ID to determine if it is compiled into the stack.
* @param libraryId The ID of the library being queried.
* @returns The status of the library being queried.
*/
async ezspGetLibraryStatus(libraryId) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_LIBRARY_STATUS);
sendBuffalo.writeUInt8(libraryId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt8();
return status;
}
/**
* Allows the HOST to know whether the NCP is running the XNCP library. If so,
* the response contains also the manufacturer ID and the version number of the
* XNCP application that is running on the NCP.
* @returns
* - SLStatus.OK if the NCP is running the XNCP library.
* - SLStatus.INVALID_STATE otherwise.
* @returns manufacturerId uint16_t * The manufactured ID the user has defined in the XNCP application.
* @returns versionNumber uint16_t * The version number of the XNCP application.
*/
async ezspGetXncpInfo() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_XNCP_INFO);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const manufacturerId = this.buffalo.readUInt16();
const versionNumber = this.buffalo.readUInt16();
return [status, manufacturerId, versionNumber];
}
/**
* Provides the customer a custom EZSP frame. On the NCP, these frames are only
* handled if the XNCP library is included. On the NCP side these frames are
* handled in the emberXNcpIncomingCustomEzspMessageCallback() callback
* function.
* @param uint8_t * The payload of the custom frame (maximum 119 bytes).
* @param uint8_t The expected length of the response.
* @returns The status returned by the custom command.
* @returns uint8_t *The response.
*/
async ezspCustomFrame(payload, replyLength) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CUSTOM_FRAME);
sendBuffalo.writePayload(payload);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const reply = this.buffalo.readPayload();
if (reply.length > replyLength) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_VALUE);
}
return [status, reply];
}
/**
* Callback
* A callback indicating a custom EZSP message has been received.
* @param payload uint8_t * The payload of the custom frame.
*/
ezspCustomFrameHandler(payload) {
logger_1.logger.debug(`ezspCustomFrameHandler: payload=${payload.toString("hex")}`, NS);
}
/**
* Returns the EUI64 ID of the local node.
* @returns The 64-bit ID.
*/
async ezspGetEui64() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_EUI64);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const eui64 = this.buffalo.readIeeeAddr();
return eui64;
}
/**
* Returns the 16-bit node ID of the local node.
* @returns The 16-bit ID.
*/
async ezspGetNodeId() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_NODE_ID);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const nodeId = this.buffalo.readUInt16();
return nodeId;
}
/**
* Returns number of phy interfaces present.
* @returns uint8_t Value indicate how many phy interfaces present.
*/
async ezspGetPhyInterfaceCount() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_PHY_INTERFACE_COUNT);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const interfaceCount = this.buffalo.readUInt8();
return interfaceCount;
}
/**
* Returns the entropy source used for true random number generation.
* @returns Value indicates the used entropy source.
*/
async ezspGetTrueRandomEntropySource() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_TRUE_RANDOM_ENTROPY_SOURCE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const entropySource = this.buffalo.readUInt8();
return entropySource;
}
/**
* Extend a joiner's timeout to wait for the network key on the joiner default key timeout is 3 sec,
* and only values greater equal to 3 sec are accepted.
* @param networkKeyTimeoutS Network key timeout
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspSetupDelayedJoin(networkKeyTimeoutS) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SETUP_DELAYED_JOIN);
sendBuffalo.writeUInt8(networkKeyTimeoutS);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Get the current scheduler priorities for multiprotocol apps.
* @returns The current priorities.
*/
async ezspRadioGetSchedulerPriorities() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.RADIO_GET_SCHEDULER_PRIORITIES);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const priorities = this.version < 0x10 ? this.buffalo.readEmberMultiprotocolPriorities() : this.buffalo.readEmber802154RadioPriorities();
return priorities;
}
/**
* Set the current scheduler priorities for multiprotocol apps.
* @param priorities The current priorities.
*/
async ezspRadioSetSchedulerPriorities(priorities) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.RADIO_SET_SCHEDULER_PRIORITIES);
if (this.version < 0x10) {
sendBuffalo.writeEmberMultiprotocolPriorities(priorities);
}
else {
sendBuffalo.writeEmber802154RadioPriorities(priorities);
}
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Get the current multiprotocol sliptime
* @returns Value of the current slip time.
*/
async ezspRadioGetSchedulerSliptime() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.RADIO_GET_SCHEDULER_SLIPTIME);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const slipTime = this.buffalo.readUInt32();
return slipTime;
}
/**
* Set the current multiprotocol sliptime
* @param slipTime Value of the current slip time.
*/
async ezspRadioSetSchedulerSliptime(slipTime) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.RADIO_SET_SCHEDULER_SLIPTIME);
sendBuffalo.writeUInt32(slipTime);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Check if a particular counter is one that could report from either a 2.4GHz or sub-GHz interface.
* @param counter The counter to be checked.
* @returns Whether this counter requires a PHY index when operating on a dual-PHY system.
*/
async ezspCounterRequiresPhyIndex(counter) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.COUNTER_REQUIRES_PHY_INDEX);
sendBuffalo.writeUInt8(counter);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const requires = this.buffalo.readUInt8() !== 0;
return requires;
}
/**
* Check if a particular counter can report on the destination node ID they have been triggered from.
* @param counter The counter to be checked.
* @returns Whether this counter requires the destination node ID.
*/
async ezspCounterRequiresDestinationNodeId(counter) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.COUNTER_REQUIRES_DESTINATION_NODE_ID);
sendBuffalo.writeUInt8(counter);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const requires = this.buffalo.readUInt8() !== 0;
return requires;
}
//-----------------------------------------------------------------------------
// Networking Frames
//-----------------------------------------------------------------------------
/**
* Sets the manufacturer code to the specified value.
* The manufacturer code is one of the fields of the node descriptor.
* @param code uint16_t The manufacturer code for the local node.
*/
async ezspSetManufacturerCode(code) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_MANUFACTURER_CODE);
sendBuffalo.writeUInt16(code);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Gets the manufacturer code to the specified value.
* The manufacturer code is one of the fields of the node descriptor.
* @returns The manufacturer code for the local node.
*/
async ezspGetManufacturerCode() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_MANUFACTURER_CODE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const code = this.buffalo.readUInt16();
return code;
}
/**
* Sets the power descriptor to the specified value. The power descriptor is a
* dynamic value. Therefore, you should call this function whenever the value
* changes.
* @param descriptor uint16_t The new power descriptor for the local node.
* @returns An SLStatus value indicating success or the reason for failure. Always `OK` in v13-.
*/
async ezspSetPowerDescriptor(descriptor) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_POWER_DESCRIPTOR);
sendBuffalo.writeUInt16(descriptor);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
if (this.version < 0x0e) {
return enums_1.SLStatus.OK;
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Resume network operation after a reboot. The node retains its original type.
* This should be called on startup whether or not the node was previously part
* of a network. EMBER_NOT_JOINED is returned if the node is not part of a
* network. This command accepts options to control the network initialization.
* @param networkInitStruct EmberNetworkInitStruct * An EmberNetworkInitStruct containing the options for initialization.
* @returns
* - SLStatus.OK if successful initialization,
* - SLStatus.NOT_JOINED if the node is not part of a network
* - or the reason for failure.
*/
async ezspNetworkInit(networkInitStruct) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.NETWORK_INIT);
sendBuffalo.writeEmberNetworkInitStruct(networkInitStruct);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Returns a value indicating whether the node is joining, joined to, or leaving a network.
* @returns Command send status.
* @returns An EmberNetworkStatus value indicating the current join status.
*/
async ezspNetworkState() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.NETWORK_STATE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt8();
return status;
}
/**
* Callback
* A callback invoked when the status of the stack changes. If the status
* parameter equals EMBER_NETWORK_UP, then the getNetworkParameters command can
* be called to obtain the new network parameters. If any of the parameters are
* being stored in nonvolatile memory by the Host, the stored values should be
* updated.
* @param status Stack status
*/
ezspStackStatusHandler(status) {
logger_1.logger.debug(`ezspStackStatusHandler: status=${enums_1.SLStatus[status]}`, NS);
this.emit("stackStatus", status);
}
/**
* This function will start a scan.
* @param scanType Indicates the type of scan to be performed. Possible values are: EZSP_ENERGY_SCAN and EZSP_ACTIVE_SCAN.
* For each type, the respective callback for reporting results is: energyScanResultHandler and networkFoundHandler.
* The energy scan and active scan report errors and completion via the scanCompleteHandler.
* @param channelMask uint32_t Bits set as 1 indicate that this particular channel should be scanned.
* Bits set to 0 indicate that this particular channel should not be scanned. For example, a channelMask value of 0x00000001
* would indicate that only channel 0 should be scanned. Valid channels range from 11 to 26 inclusive.
* This translates to a channel mask value of 0x07FFF800.
* As a convenience, a value of 0 is reinterpreted as the mask for the current channel.
* @param duration uint8_t Sets the exponent of the number of scan periods, where a scan period is 960 symbols.
* The scan will occur for ((2^duration) + 1) scan periods.
* @returns
* - SLStatus.OK signals that the scan successfully started. Possible error responses and their meanings:
* - SLStatus.MAC_SCANNING, we are already scanning;
* - SLStatus.BAD_SCAN_DURATION, we have set a duration value that is not 0..14 inclusive;
* - SLStatus.MAC_INCORRECT_SCAN_TYPE, we have requested an undefined scanning type;
* - SLStatus.INVALID_CHANNEL_MASK, our channel mask did not specify any valid channels.
*/
async ezspStartScan(scanType, channelMask, duration) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.START_SCAN);
sendBuffalo.writeUInt8(scanType);
sendBuffalo.writeUInt32(channelMask);
sendBuffalo.writeUInt8(duration);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Callback
* Reports the result of an energy scan for a single channel. The scan is not
* complete until the scanCompleteHandler callback is called.
* @param channel uint8_t The 802.15.4 channel number that was scanned.
* @param maxRssiValue int8_t The maximum RSSI value found on the channel.
*/
ezspEnergyScanResultHandler(channel, maxRssiValue) {
logger_1.logger.debug(`ezspEnergyScanResultHandler: channel=${channel} maxRssiValue=${maxRssiValue}`, NS);
logger_1.logger.info(`Energy scan for channel ${channel} reports max RSSI value at ${maxRssiValue} dBm.`, NS);
}
/**
* Callback
* Reports that a network was found as a result of a prior call to startScan.
* Gives the network parameters useful for deciding which network to join.
* @param networkFound EmberZigbeeNetwork * The parameters associated with the network found.
* @param lastHopLqi uint8_t The link quality from the node that generated this beacon.
* @param lastHopRssi int8_t The energy level (in units of dBm) observed during the reception.
*/
ezspNetworkFoundHandler(networkFound, lastHopLqi, lastHopRssi) {
logger_1.logger.debug(() => `ezspNetworkFoundHandler: networkFound=${JSON.stringify(networkFound)} lastHopLqi=${lastHopLqi} lastHopRssi=${lastHopRssi}`, NS);
}
/**
* Callback
* @param channel uint8_t The channel on which the current error occurred. Undefined for the case of EMBER_SUCCESS.
* @param status The error condition that occurred on the current channel. Value will be SLStatus.OK when the scan has completed.
* Other error conditions signify a failure to scan on the channel specified.
*/
ezspScanCompleteHandler(channel, status) {
logger_1.logger.debug(`ezspScanCompleteHandler: channel=${channel} status=${enums_1.SLStatus[status]}`, NS);
}
/**
* Callback
* This function returns an unused panID and channel pair found via the find
* unused panId scan procedure.
* @param panId The unused panID which has been found.
* @param channel uint8_t The channel that the unused panID was found on.
*/
ezspUnusedPanIdFoundHandler(panId, channel) {
logger_1.logger.debug(`ezspUnusedPanIdFoundHandler: panId=${panId} channel=${channel}`, NS);
}
/**
* This function starts a series of scans which will return an available panId.
* @param channelMask uint32_t The channels that will be scanned for available panIds.
* @param duration uint8_t The duration of the procedure.
* @returns The error condition that occurred during the scan. Value will be SLStatus.OK if there are no errors.
*/
async ezspFindUnusedPanId(channelMask, duration) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.FIND_UNUSED_PAN_ID);
sendBuffalo.writeUInt32(channelMask);
sendBuffalo.writeUInt8(duration);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Terminates a scan in progress.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspStopScan() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.STOP_SCAN);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Forms a new network by becoming the coordinator.
* @param parameters EmberNetworkParameters * Specification of the new network.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspFormNetwork(parameters) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.FORM_NETWORK);
sendBuffalo.writeEmberNetworkParameters(parameters);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Causes the stack to associate with the network using the specified network
* parameters. It can take several seconds for the stack to associate with the
* local network. Do not send messages until the stackStatusHandler callback
* informs you that the stack is up.
* @param nodeType Specification of the role that this node will have in the network.
* This role must not be EMBER_COORDINATOR. To be a coordinator, use the formNetwork command.
* @param parameters EmberNetworkParameters * Specification of the network with which the node should associate.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspJoinNetwork(nodeType, parameters) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.JOIN_NETWORK);
sendBuffalo.writeUInt8(nodeType);
sendBuffalo.writeEmberNetworkParameters(parameters);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Causes the stack to associate with the network using the specified network
* parameters in the beacon parameter. It can take several seconds for the stack
* to associate with the local network. Do not send messages until the
* stackStatusHandler callback informs you that the stack is up. Unlike
* ::emberJoinNetwork(), this function does not issue an active scan before
* joining. Instead, it will cause the local node to issue a MAC Association
* Request directly to the specified target node. It is assumed that the beacon
* parameter is an artifact after issuing an active scan. (For more information,
* see emberGetBestBeacon and emberGetNextBeacon.)
* @param localNodeType Specifies the role that this node will have in the network. This role must not be EMBER_COORDINATOR.
* To be a coordinator, use the formNetwork command.
* @param beacon EmberBeaconData * Specifies the network with which the node should associate.
* @param radioTxPower int8_t The radio transmit power to use, specified in dBm.
* @param clearBeaconsAfterNetworkUp If true, clear beacons in cache upon join success. If join fail, do nothing.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspJoinNetworkDirectly(localNodeType, beacon, radioTxPower, clearBeaconsAfterNetworkUp) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.JOIN_NETWORK_DIRECTLY);
sendBuffalo.writeUInt8(localNodeType);
sendBuffalo.writeEmberBeaconData(beacon);
sendBuffalo.writeInt8(radioTxPower);
sendBuffalo.writeUInt8(clearBeaconsAfterNetworkUp ? 1 : 0);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Causes the stack to leave the current network. This generates a
* stackStatusHandler callback to indicate that the network is down. The radio
* will not be used until after sending a formNetwork or joinNetwork command.
* @param options This parameter gives options when leave network
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspLeaveNetwork(options = enums_1.EmberLeaveNetworkOption.WITH_NO_OPTION) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.LEAVE_NETWORK);
if (this.version >= 0x0e) {
sendBuffalo.writeUInt8(options);
}
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* The application may call this function when contact with the network has been
* lost. The most common usage case is when an end device can no longer
* communicate with its parent and wishes to find a new one. Another case is
* when a device has missed a Network Key update and no longer has the current
* Network Key. The stack will call ezspStackStatusHandler to indicate that the
* network is down, then try to re-establish contact with the network by
* performing an active scan, choosing a network with matching extended pan id,
* and sending a Zigbee network rejoin request. A second call to the
* ezspStackStatusHandler callback indicates either the success or the failure
* of the attempt. The process takes approximately 150 milliseconds per channel
* to complete.
* @param haveCurrentNetworkKey This parameter tells the stack whether to try to use the current network key.
* If it has the current network key it will perform a secure rejoin (encrypted). If this fails the device should try an unsecure rejoin.
* If the Trust Center allows the rejoin then the current Network Key will be sent encrypted using the device's Link Key.
* @param channelMask uint32_t A mask indicating the channels to be scanned. See emberStartScan for format details.
* A value of 0 is reinterpreted as the mask for the current channel.
* @param reason uint8_t A sl_zigbee_rejoin_reason_t variable which could be passed in if there is actually a reason for rejoin,
* or could be left at 0xFF
* @param nodeType uint8_t The rejoin could be triggered with a different nodeType.
* This value could be set to 0 or SL_ZIGBEE_DEVICE_TYPE_UNCHANGED if not needed.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspFindAndRejoinNetwork(haveCurrentNetworkKey, channelMask, reason = 0xff, nodeType = 0) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.FIND_AND_REJOIN_NETWORK);
sendBuffalo.writeUInt8(haveCurrentNetworkKey ? 1 : 0);
sendBuffalo.writeUInt32(channelMask);
if (this.version >= 0x0e) {
sendBuffalo.writeUInt8(reason);
sendBuffalo.writeUInt8(nodeType);
}
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Tells the stack to allow other nodes to join the network with this node as
* their parent. Joining is initially disabled by default.
* @param duration uint8_t A value of 0x00 disables joining. A value of 0xFF enables joining.
* Any other value enables joining for that number of seconds.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspPermitJoining(duration) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.PERMIT_JOINING);
sendBuffalo.writeUInt8(duration);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Callback
* Indicates that a child has joined or left.
* @param index uint8_t The index of the child of interest.
* @param joining True if the child is joining. False the child is leaving.
* @param childId The node ID of the child.
* @param childEui64 The EUI64 of the child.
* @param childType The node type of the child.
*/
ezspChildJoinHandler(index, joining, childId, childEui64, childType) {
logger_1.logger.debug(`ezspChildJoinHandler: index=${index} joining=${joining} childId=${childId} childEui64=${childEui64} childType=${childType}`, NS);
}
/**
* Sends a ZDO energy scan request. This request may only be sent by the current
* network manager and must be unicast, not broadcast. See ezsp-utils.h for
* related macros emberSetNetworkManagerRequest() and
* emberChangeChannelRequest().
* @param target The network address of the node to perform the scan.
* @param scanChannels uint32_t A mask of the channels to be scanned
* @param scanDuration uint8_t How long to scan on each channel.
* Allowed values are 0..5, with the scan times as specified by 802.15.4 (0 = 31ms, 1 = 46ms, 2 = 77ms, 3 = 138ms, 4 = 261ms, 5 = 507ms).
* @param scanCount uint16_t The number of scans to be performed on each channel (1..8).
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspEnergyScanRequest(target, scanChannels, scanDuration, scanCount) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ENERGY_SCAN_REQUEST);
sendBuffalo.writeUInt16(target);
sendBuffalo.writeUInt32(scanChannels);
sendBuffalo.writeUInt8(scanDuration);
sendBuffalo.writeUInt16(scanCount);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Returns the current network parameters.
* @returns An SLStatus value indicating success or the reason for failure.
* @returns EmberNodeType * An EmberNodeType value indicating the current node type.
* @returns EmberNetworkParameters * The current network parameters.
*/
async ezspGetNetworkParameters() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_NETWORK_PARAMETERS);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const nodeType = this.buffalo.readUInt8();
const parameters = this.buffalo.readEmberNetworkParameters();
return [status, nodeType, parameters];
}
/**
* Returns the current radio parameters based on phy index.
* @param phyIndex uint8_t Desired index of phy interface for radio parameters.
* @returns An SLStatus value indicating success or the reason for failure.
* @returns EmberMultiPhyRadioParameters * The current radio parameters based on provided phy index.
*/
async ezspGetRadioParameters(phyIndex) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_RADIO_PARAMETERS);
sendBuffalo.writeUInt8(phyIndex);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const parameters = this.buffalo.readEmberMultiPhyRadioParameters();
return [status, parameters];
}
/**
* Returns information about the children of the local node and the parent of
* the local node.
* @returns uint8_t The number of children the node currently has.
* @returns The parent's EUI64. The value is undefined for nodes without parents (coordinators and nodes that are not joined to a network).
* @returns NodeId * The parent's node ID. The value is undefined for nodes without parents
* (coordinators and nodes that are not joined to a network).
*/
async ezspGetParentChildParameters() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_PARENT_CHILD_PARAMETERS);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const childCount = this.buffalo.readUInt8();
const parentEui64 = this.buffalo.readIeeeAddr();
const parentNodeId = this.buffalo.readUInt16();
return [childCount, parentEui64, parentNodeId];
}
/**
* Return the number of router children that the node currently has.
* @returns The number of router children.
*/
async ezspRouterChildCount() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ROUTER_CHILD_COUNT);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const routerChildCount = this.buffalo.readUInt8();
return routerChildCount;
}
/**
* Return the maximum number of children for this node.
* The return value is undefined for nodes that are not joined to a network.
* @returns The maximum number of children.
*/
async ezspMaxChildCount() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MAX_CHILD_COUNT);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const maxChildCount = this.buffalo.readUInt8();
return maxChildCount;
}
/**
* Return the maximum number of router children for this node.
* The return value is undefined for nodes that are not joined to a network.
* @returns The maximum number of router children.
*/
async ezspMaxRouterChildCount() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MAX_ROUTER_CHILD_COUNT);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const maxRouterChildCount = this.buffalo.readUInt8();
return maxRouterChildCount;
}
/**
*
* @returns
*/
async ezspGetParentIncomingNwkFrameCounter() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_PARENT_INCOMING_NWK_FRAME_COUNTER);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const parentIncomingNwkFrameCounter = this.buffalo.readUInt32();
return parentIncomingNwkFrameCounter;
}
/**
*
* @param value uint32_t
* @returns
*/
async ezspSetParentIncomingNwkFrameCounter(value) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_PARENT_INCOMING_NWK_FRAME_COUNTER);
sendBuffalo.writeUInt32(value);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Return a bitmask indicating the stack's current tasks.
* The mask ::SL_ZIGBEE_HIGH_PRIORITY_TASKS defines which tasks are high priority.
* Devices should not sleep if any high priority tasks are active.
* Active tasks that are not high priority are waiting for messages to arrive from other devices.
* If there are active tasks, but no high priority ones, the device may sleep but should periodically wake up
* and call ::emberPollForData() in order to receive messages.
* Parents will hold messages for ::SL_ZIGBEE_INDIRECT_TRANSMISSION_TIMEOUT milliseconds before discarding them.
* @returns A bitmask of the stack's active tasks.
*/
async ezspCurrentStackTasks() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CURRENT_STACK_TASKS);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const activeTasks = this.buffalo.readUInt16();
return activeTasks;
}
/**
* Indicate whether the stack is currently in a state where there are no high-priority tasks, allowing the device to sleep.
* There may be tasks expecting incoming messages, in which case the device should periodically wake up
* and call ::emberPollForData() in order to receive messages.
* This function can only be called when the node type is ::SL_ZIGBEE_SLEEPY_END_DEVICE
* @returns True if the application may sleep but the stack may be expecting incoming messages.
*/
async ezspOkToNap() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.OK_TO_NAP);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const value = this.buffalo.readUInt8() !== 0;
return value;
}
/**
* Indicate whether the parent token has been set by association.
* @returns True if the parent token has been set.
*/
async ezspParentTokenSet() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.PARENT_TOKEN_SET);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const indicator = this.buffalo.readUInt8() !== 0;
return indicator;
}
/**
* Indicate whether the stack currently has any tasks pending.
* If no tasks are pending, ::emberTick() does not need to be called until the next time a stack API function is called.
* This function can only be called when the node type is ::SL_ZIGBEE_SLEEPY_END_DEVICE.
* @returns True if the application may sleep for as long as it wishes.
*/
async ezspOkToHibernate() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.OK_TO_HIBERNATE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const indicator = this.buffalo.readUInt8() !== 0;
return indicator;
}
/**
* Indicate whether the stack is currently in a state that does not require the application to periodically poll.
* @returns True if the device may poll less frequently.
*/
async ezspOkToLongPoll() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.OK_TO_LONG_POLL);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const indicator = this.buffalo.readUInt8() !== 0;
return indicator;
}
/**
* Calling this function will render all other stack functions except ezspStackPowerUp() non-functional until the radio is powered back on.
*/
async ezspStackPowerDown() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.STACK_POWER_DOWN);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Initialize the radio. Typically called coming out of deep sleep.
* For non-sleepy devices, also turns the radio on and leaves it in RX mode.
*/
async ezspStackPowerUp() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.STACK_POWER_UP);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Returns information about a child of the local node.
* @param uint8_t The index of the child of interest in the child table. Possible indexes range from zero to EMBER_CHILD_TABLE_SIZE.
* @returns
* - SLStatus.OK if there is a child at index.
* - SLStatus.NOT_JOINED if there is no child at index.
* @returns EmberChildData * The data of the child.
*/
async ezspGetChildData(index) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_CHILD_DATA);
sendBuffalo.writeUInt8(index);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const childData = this.buffalo.readEmberChildData();
return [status, childData];
}
/**
* Sets child data to the child table token.
* @param index uint8_t The index of the child of interest in the child table. Possible indexes range from zero to (EMBER_CHILD_TABLE_SIZE - 1).
* @param childData EmberChildData * The data of the child.
* @returns
* - SLStatus.OK if the child data is set successfully at index.
* - SLStatus.INVALID_INDEX if provided index is out of range.
*/
async ezspSetChildData(index, childData) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_CHILD_DATA);
sendBuffalo.writeUInt8(index);
sendBuffalo.writeEmberChildData(childData);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Convert a child index to a node ID
* @param childIndex uint8_t The index of the child of interest in the child table. Possible indexes range from zero to EMBER_CHILD_TABLE_SIZE.
* @returns The node ID of the child or EMBER_NULL_NODE_ID if there isn't a child at the childIndex specified
*/
async ezspChildId(childIndex) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CHILD_ID);
sendBuffalo.writeUInt8(childIndex);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const childId = this.buffalo.readUInt16();
return childId;
}
/**
* Return radio power value of the child from the given childIndex
* @param childIndex uint8_t The index of the child of interest in the child table.
* Possible indexes range from zero to SL_ZIGBEE_CHILD_TABLE_SIZE.
* @returns The power of the child or maximum radio power, which is the power value provided by the user
* while forming/joining a network if there isn't a child at the childIndex specified
*/
async ezspChilPower(childIndex) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CHILD_POWER);
sendBuffalo.writeUInt8(childIndex);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const childPower = this.buffalo.readInt8();
return childPower;
}
/**
* Set the radio power value for a given child index.
* @param childIndex uint8_t
* @param newPower int8_t
*/
async ezspSetChildPower(childIndex, newPower) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_CHILD_POWER);
sendBuffalo.writeUInt8(childIndex);
sendBuffalo.writeInt8(newPower);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Convert a node ID to a child index
* @param childId The node ID of the child
* @returns uint8_t The child index or 0xFF if the node ID doesn't belong to a child
*/
async ezspChildIndex(childId) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CHILD_INDEX);
sendBuffalo.writeUInt16(childId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const childIndex = this.buffalo.readUInt8();
return childIndex;
}
/**
* Returns the source route table total size.
* @returns uint8_t Total size of source route table.
*/
async ezspGetSourceRouteTableTotalSize() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_SOURCE_ROUTE_TABLE_TOTAL_SIZE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const sourceRouteTableTotalSize = this.buffalo.readUInt8();
return sourceRouteTableTotalSize;
}
/**
* Returns the number of filled entries in source route table.
* @returns uint8_t The number of filled entries in source route table.
*/
async ezspGetSourceRouteTableFilledSize() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_SOURCE_ROUTE_TABLE_FILLED_SIZE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const sourceRouteTableFilledSize = this.buffalo.readUInt8();
return sourceRouteTableFilledSize;
}
/**
* Returns information about a source route table entry
* @param index uint8_t The index of the entry of interest in the source route table.
* Possible indexes range from zero to SOURCE_ROUTE_TABLE_FILLED_SIZE.
* @returns
* - SLStatus.OK if there is source route entry at index.
* - SLStatus.NOT_FOUND if there is no source route at index.
* @returns NodeId * The node ID of the destination in that entry.
* @returns uint8_t * The closer node index for this source route table entry
*/
async ezspGetSourceRouteTableEntry(index) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_SOURCE_ROUTE_TABLE_ENTRY);
sendBuffalo.writeUInt8(index);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const destination = this.buffalo.readUInt16();
const closerIndex = this.buffalo.readUInt8();
return [status, destination, closerIndex];
}
/**
* Returns the neighbor table entry at the given index. The number of active
* neighbors can be obtained using the neighborCount command.
* @param index uint8_t The index of the neighbor of interest. Neighbors are stored in ascending order by node id,
* with all unused entries at the end of the table.
* @returns
* - SLStatus.FAIL if the index is greater or equal to the number of active neighbors, or if the device is an end device.
* - SLStatus.OK otherwise.
* @returns EmberNeighborTableEntry * The contents of the neighbor table entry.
*/
async ezspGetNeighbor(index) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_NEIGHBOR);
sendBuffalo.writeUInt8(index);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const value = this.buffalo.readEmberNeighborTableEntry();
return [status, value];
}
/**
* Return EmberStatus depending on whether the frame counter of the node is
* found in the neighbor or child table. This function gets the last received
* frame counter as found in the Network Auxiliary header for the specified
* neighbor or child
* @param eui64 eui64 of the node
* @returns
* - SLStatus.NOT_FOUND if the node is not found in the neighbor or child table.
* - SLStatus.OK otherwise
* @returns uint32_t * Return the frame counter of the node from the neighbor or child table
*/
async ezspGetNeighborFrameCounter(eui64) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_NEIGHBOR_FRAME_COUNTER);
sendBuffalo.writeIeeeAddr(eui64);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const returnFrameCounter = this.buffalo.readUInt32();
return [status, returnFrameCounter];
}
/**
* Sets the frame counter for the neighbour or child.
* @param eui64 eui64 of the node
* @param frameCounter uint32_t Return the frame counter of the node from the neighbor or child table
* @returns
* - SLStatus.NOT_FOUND if the node is not found in the neighbor or child table.
* - SLStatus.OK otherwise
*/
async ezspSetNeighborFrameCounter(eui64, frameCounter) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_NEIGHBOR_FRAME_COUNTER);
sendBuffalo.writeIeeeAddr(eui64);
sendBuffalo.writeUInt32(frameCounter);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Sets the routing shortcut threshold to directly use a neighbor instead of
* performing routing.
* @param costThresh uint8_t The routing shortcut threshold to configure.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspSetRoutingShortcutThreshold(costThresh) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_ROUTING_SHORTCUT_THRESHOLD);
sendBuffalo.writeUInt8(costThresh);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Gets the routing shortcut threshold used to differentiate between directly
* using a neighbor vs. performing routing.
* @returns uint8_t The routing shortcut threshold
*/
async ezspGetRoutingShortcutThreshold() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_ROUTING_SHORTCUT_THRESHOLD);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const routingShortcutThresh = this.buffalo.readUInt8();
return routingShortcutThresh;
}
/**
* Returns the number of active entries in the neighbor table.
* @returns uint8_t The number of active entries in the neighbor table.
*/
async ezspNeighborCount() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.NEIGHBOR_COUNT);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const value = this.buffalo.readUInt8();
return value;
}
/**
* Returns the route table entry at the given index. The route table size can be
* obtained using the getConfigurationValue command.
* @param index uint8_t The index of the route table entry of interest.
* @returns
* - SLStatus.FAIL if the index is out of range or the device is an end
* - SLStatus.OK otherwise.
* @returns EmberRouteTableEntry * The contents of the route table entry.
*/
async ezspGetRouteTableEntry(index) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_ROUTE_TABLE_ENTRY);
sendBuffalo.writeUInt8(index);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const value = this.buffalo.readEmberRouteTableEntry();
return [status, value];
}
/**
* Sets the radio output power at which a node is operating. Ember radios have
* discrete power settings. For a list of available power settings, see the
* technical specification for the RF communication module in your Developer
* Kit. Note: Care should be taken when using this API on a running network, as
* it will directly impact the established link qualities neighboring nodes have
* with the node on which it is called. This can lead to disruption of existing
* routes and erratic network behavior.
* @param power int8_t Desired radio output power, in dBm.
* @returns An SLStatus value indicating the success or failure of the command.
*/
async ezspSetRadioPower(power) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_RADIO_POWER);
sendBuffalo.writeInt8(power);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Sets the channel to use for sending and receiving messages. For a list of
* available radio channels, see the technical specification for the RF
* communication module in your Developer Kit. Note: Care should be taken when
* using this API, as all devices on a network must use the same channel.
* @param channel uint8_t Desired radio channel.
* @returns An SLStatus value indicating the success or failure of the command.
*/
async ezspSetRadioChannel(channel) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_RADIO_CHANNEL);
sendBuffalo.writeUInt8(channel);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Gets the channel in use for sending and receiving messages.
* @returns uint8_t Current radio channel.
*/
async ezspGetRadioChannel() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_RADIO_CHANNEL);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const channel = this.buffalo.readUInt8();
return channel;
}
/**
* Set the configured 802.15.4 CCA mode in the radio.
* @param ccaMode uint8_t A RAIL_IEEE802154_CcaMode_t value.
* @returns An SLStatus value indicating the success or failure of the command.
*/
async ezspSetRadioIeee802154CcaMode(ccaMode) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_RADIO_IEEE802154_CCA_MODE);
sendBuffalo.writeUInt8(ccaMode);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Enable/disable concentrator support.
* @param on If this bool is true the concentrator support is enabled. Otherwise is disabled.
* If this bool is false all the other arguments are ignored.
* @param concentratorType uint16_t Must be either EMBER_HIGH_RAM_CONCENTRATOR or EMBER_LOW_RAM_CONCENTRATOR.
* The former is used when the caller has enough memory to store source routes for the whole network.
* In that case, remote nodes stop sending route records once the concentrator has successfully received one.
* The latter is used when the concentrator has insufficient RAM to store all outbound source routes.
* In that case, route records are sent to the concentrator prior to every inbound APS unicast.
* @param minTime uint16_t The minimum amount of time that must pass between MTORR broadcasts.
* @param maxTime uint16_t The maximum amount of time that can pass between MTORR broadcasts.
* @param routeErrorThreshold uint8_t The number of route errors that will trigger a re-broadcast of the MTORR.
* @param deliveryFailureThreshold uint8_t The number of APS delivery failures that will trigger a re-broadcast of the MTORR.
* @param maxHops uint8_t The maximum number of hops that the MTORR broadcast will be allowed to have.
* A value of 0 will be converted to the EMBER_MAX_HOPS value set by the stack.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspSetConcentrator(on, concentratorType, minTime, maxTime, routeErrorThreshold, deliveryFailureThreshold, maxHops) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_CONCENTRATOR);
sendBuffalo.writeUInt8(on ? 1 : 0);
sendBuffalo.writeUInt16(concentratorType);
sendBuffalo.writeUInt16(minTime);
sendBuffalo.writeUInt16(maxTime);
sendBuffalo.writeUInt8(routeErrorThreshold);
sendBuffalo.writeUInt8(deliveryFailureThreshold);
sendBuffalo.writeUInt8(maxHops);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Starts periodic many-to-one route discovery.
* Periodic discovery is started by default on bootup, but this function may be used if discovery
* has been stopped by a call to ::ezspConcentratorStopDiscovery().
*/
async ezspConcentratorStartDiscovery() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CONCENTRATOR_START_DISCOVERY);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Stops periodic many-to-one route discovery.
*/
async ezspConcentratorStopDiscovery() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CONCENTRATOR_STOP_DISCOVERY);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Notes when a route error has occurred.
* @param status
* @param nodeId
*/
async ezspConcentratorNoteRouteError(status, nodeId) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CONCENTRATOR_NOTE_ROUTE_ERROR);
sendBuffalo.writeUInt32(status);
sendBuffalo.writeUInt16(nodeId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Sets the error code that is sent back from a router with a broken route.
* @param errorCode uint8_t Desired error code.
* @returns An SLStatus value indicating the success or failure of the command.
*/
async ezspSetBrokenRouteErrorCode(errorCode) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_BROKEN_ROUTE_ERROR_CODE);
sendBuffalo.writeUInt8(errorCode);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* This causes to initialize the desired radio interface other than native and
* form a new network by becoming the coordinator with same panId as native
* radio network.
* @param phyIndex uint8_t Index of phy interface. The native phy index would be always zero hence valid phy index starts from one.
* @param page uint8_t Desired radio channel page.
* @param channel uint8_t Desired radio channel.
* @param power int8_t Desired radio output power, in dBm.
* @param bitmask Network configuration bitmask.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspMultiPhyStart(phyIndex, page, channel, power, bitmask) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MULTI_PHY_START);
sendBuffalo.writeUInt8(phyIndex);
sendBuffalo.writeUInt8(page);
sendBuffalo.writeUInt8(channel);
sendBuffalo.writeInt8(power);
sendBuffalo.writeUInt8(bitmask);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* This causes to bring down the radio interface other than native.
* @param phyIndex uint8_t Index of phy interface. The native phy index would be always zero hence valid phy index starts from one.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspMultiPhyStop(phyIndex) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MULTI_PHY_STOP);
sendBuffalo.writeUInt8(phyIndex);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Sets the radio output power for desired phy interface at which a node is
* operating. Ember radios have discrete power settings. For a list of available
* power settings, see the technical specification for the RF communication
* module in your Developer Kit. Note: Care should be taken when using this api
* on a running network, as it will directly impact the established link
* qualities neighboring nodes have with the node on which it is called. This
* can lead to disruption of existing routes and erratic network behavior.
* @param phyIndex uint8_t Index of phy interface. The native phy index would be always zero hence valid phy index starts from one.
* @param power int8_t Desired radio output power, in dBm.
* @returns An SLStatus value indicating the success or failure of the command.
*/
async ezspMultiPhySetRadioPower(phyIndex, power) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MULTI_PHY_SET_RADIO_POWER);
sendBuffalo.writeUInt8(phyIndex);
sendBuffalo.writeInt8(power);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Send Link Power Delta Request from a child to its parent
* @returns An SLStatus value indicating the success or failure of the command.
*/
async ezspSendLinkPowerDeltaRequest() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SEND_LINK_POWER_DELTA_REQUEST);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Sets the channel for desired phy interface to use for sending and receiving
* messages. For a list of available radio pages and channels, see the technical
* specification for the RF communication module in your Developer Kit. Note:
* Care should be taken when using this API, as all devices on a network must
* use the same page and channel.
* @param phyIndex uint8_t Index of phy interface. The native phy index would be always zero hence valid phy index starts from one.
* @param page uint8_t Desired radio channel page.
* @param channel uint8_t Desired radio channel.
* @returns An SLStatus value indicating the success or failure of the command.
*/
async ezspMultiPhySetRadioChannel(phyIndex, page, channel) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MULTI_PHY_SET_RADIO_CHANNEL);
sendBuffalo.writeUInt8(phyIndex);
sendBuffalo.writeUInt8(page);
sendBuffalo.writeUInt8(channel);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Obtains the current duty cycle state.
* @returns An SLStatus value indicating the success or failure of the command.
* @returns EmberDutyCycleState * The current duty cycle state in effect.
*/
async ezspGetDutyCycleState() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_DUTY_CYCLE_STATE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const returnedState = this.buffalo.readUInt8();
return [status, returnedState];
}
/**
* Set the current duty cycle limits configuration. The Default limits set by
* stack if this call is not made.
* @param limits EmberDutyCycleLimits * The duty cycle limits configuration to utilize.
* @returns
* - SLStatus.OK if the duty cycle limit configurations set successfully,
* - SLStatus.INVALID_PARAMETER if set illegal value such as setting only one of the limits to default
* or violates constraints Susp > Crit > Limi,
* - SLStatus.INVALID_STATE if device is operating on 2.4Ghz
*/
async ezspSetDutyCycleLimitsInStack(limits) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_DUTY_CYCLE_LIMITS_IN_STACK);
sendBuffalo.writeEmberDutyCycleLimits(limits);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Obtains the current duty cycle limits that were previously set by a call to emberSetDutyCycleLimitsInStack(),
* or the defaults set by the stack if no set call was made.
* @returns An SLStatus value indicating the success or failure of the command.
* @returns EmberDutyCycleLimits * Return current duty cycle limits if returnedLimits is not NULL
*/
async ezspGetDutyCycleLimits() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_DUTY_CYCLE_LIMITS);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const returnedLimits = this.buffalo.readEmberDutyCycleLimits();
return [status, returnedLimits];
}
/**
* Returns the duty cycle of the stack's connected children that are being
* monitored, up to maxDevices. It indicates the amount of overall duty cycle
* they have consumed (up to the suspend limit). The first entry is always the
* local stack's nodeId, and thus the total aggregate duty cycle for the device.
* The passed pointer arrayOfDeviceDutyCycles MUST have space for maxDevices.
* @param maxDevices uint8_t Number of devices to retrieve consumed duty cycle.
* @returns
* - SLStatus.OK if the duty cycles were read successfully,
* - SLStatus.INVALID_PARAMETER maxDevices is greater than SL_ZIGBEE_MAX_END_DEVICE_CHILDREN + 1.
* @returns uint8_t * Consumed duty cycles up to maxDevices. When the number of children that are being monitored is less than maxDevices,
* the NodeId element in the EmberPerDeviceDutyCycle will be 0xFFFF.
*/
async ezspGetCurrentDutyCycle(maxDevices) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_CURRENT_DUTY_CYCLE);
sendBuffalo.writeUInt8(maxDevices);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const arrayOfDeviceDutyCycles = this.buffalo.readListUInt8(134);
return [status, arrayOfDeviceDutyCycles];
}
/**
* Callback
* Callback fires when the duty cycle state has changed
* @param channelPage uint8_t The channel page whose duty cycle state has changed.
* @param channel uint8_t The channel number whose duty cycle state has changed.
* @param state The current duty cycle state.
* @param totalDevices uint8_t The total number of connected end devices that are being monitored for duty cycle.
* @param arrayOfDeviceDutyCycles EmberPerDeviceDutyCycle * Consumed duty cycles of end devices that are being monitored.
* The first entry always be the local stack's nodeId, and thus the total aggregate duty cycle for the device.
*/
ezspDutyCycleHandler(channelPage, channel, state, totalDevices, arrayOfDeviceDutyCycles) {
logger_1.logger.debug(`ezspDutyCycleHandler: channelPage=${channelPage} channel=${channel} state=${state} totalDevices=${totalDevices} arrayOfDeviceDutyCycles=${arrayOfDeviceDutyCycles}`, NS);
}
/**
* Configure the number of beacons to store when issuing active scans for networks.
* @param numBeacons uint8_t The number of beacons to cache when scanning.
* @returns
* - SLStatus.INVALID_PARAMETER if numBeacons is greater than SL_ZIGBEE_MAX_BEACONS_TO_STORE
* - SLStatus.OK
*/
async ezspSetNumBeaconToStore(numBeacons) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_NUM_BEACONS_TO_STORE);
sendBuffalo.writeUInt8(numBeacons);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Fetches the specified beacon in the cache. Beacons are stored in cache after issuing an active scan.
* @param beaconNumber uint8_t The beacon index to fetch. Valid values range from 0 to ezspGetNumStoredBeacons-1.
* @returns An appropriate SLStatus status code.
* @returns EmberBeaconData * The beacon to populate upon success.
*/
async ezspGetStoredBeacon(beaconNumber) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_STORED_BEACON);
sendBuffalo.writeUInt8(beaconNumber);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
const beacon = this.buffalo.readEmberBeaconData();
return [status, beacon];
}
/**
* Returns the number of cached beacons that have been collected from a scan.
* @returns uint8_t The number of cached beacons that have been collected from a scan.
*/
async ezspGetNumStoredBeacons() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_NUM_STORED_BEACONS);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const numBeacons = this.buffalo.readUInt8();
return numBeacons;
}
/**
* Clears all cached beacons that have been collected from a scan.
* @returns An SLStatus value indicating success or the reason for failure. Always `OK` in v13-.
*/
async ezspClearStoredBeacons() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CLEAR_STORED_BEACONS);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
if (this.version < 0x0e) {
return enums_1.SLStatus.OK;
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* This call sets the radio channel in the stack and propagates the information
* to the hardware.
* @param radioChannel uint8_t The radio channel to be set.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspSetLogicalAndRadioChannel(radioChannel) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_LOGICAL_AND_RADIO_CHANNEL);
sendBuffalo.writeUInt8(radioChannel);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Form a new sleepy-to-sleepy network.
* If the network is using security, the device must call sli_zigbee_stack_set_initial_security_state() first.
* @param parameters Specification of the new network.
* @param initiator Whether this device is initiating or joining the network.
* @returns An SLStatus value indicating success or a reason for failure.
*/
async ezspSleepyToSleepyNetworkStart(parameters, initiator) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SLEEPY_TO_SLEEPY_NETWORK_START);
sendBuffalo.writeEmberNetworkParameters(parameters);
sendBuffalo.writeUInt8(initiator ? 1 : 0);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Send a Zigbee NWK Leave command to the destination.
* @param destination Node ID of the device being told to leave.
* @param flags Bitmask indicating additional considerations for the leave request.
* @returns Status indicating success or a reason for failure. Call is invalid if destination is on network or is the local node.
*/
async ezspSendZigbeeLeave(destination, flags) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SEND_ZIGBEE_LEAVE);
sendBuffalo.writeUInt16(destination);
sendBuffalo.writeUInt8(flags);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Indicate the state of permit joining in MAC.
* @returns Whether the current network permits joining.
*/
async ezspGetPermitJoining() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_PERMIT_JOINING);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const joiningPermitted = this.buffalo.readUInt8() !== 0;
return joiningPermitted;
}
/**
* Get the 8-byte extended PAN ID of this node.
* @returns Extended PAN ID of this node. Valid only if it is currently on a network.
*/
async ezspGetExtendedPanId() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_EXTENDED_PAN_ID);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const extendedPanId = this.buffalo.readListUInt8(ZSpec.EXTENDED_PAN_ID_SIZE);
return extendedPanId;
}
/**
* Get the current network.
* @returns Return the current network index.
*/
async ezspGetCurrentNetwork() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_CURRENT_NETWORK);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const index = this.buffalo.readUInt8();
return index;
}
/**
* Set initial outgoing link cost for neighbor.
* @param cost The new default cost. Valid values are 0, 1, 3, 5, and 7.
* @returns Whether or not initial cost was successfully set.
*/
async ezspSetInitialNeighborOutgoingCost(cost) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_INITIAL_NEIGHBOR_OUTGOING_COST);
sendBuffalo.writeUInt8(cost);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Get initial outgoing link cost for neighbor.
* @returns The default cost associated with new neighbor's outgoing links.
*/
async ezspGetInitialNeighborOutgoingCost() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_INITIAL_NEIGHBOR_OUTGOING_COST);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const cost = this.buffalo.readUInt8();
return cost;
}
/**
* Indicate whether a rejoining neighbor should have its incoming frame counter reset.
* @param reset
*/
async ezspResetRejoiningNeighborsFrameCounter(reset) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.RESET_REJOINING_NEIGHBORS_FRAME_COUNTER);
sendBuffalo.writeUInt8(reset ? 1 : 0);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Check whether a rejoining neighbor will have its incoming frame counter reset based on the currently set policy.
* @returns Whether or not a rejoining neighbor's incoming FC gets reset (true or false).
*/
async ezspIsResetRejoiningNeighborsFrameCounterEnabled() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.IS_RESET_REJOINING_NEIGHBORS_FRAME_COUNTER_ENABLED);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const getsReset = this.buffalo.readUInt8() !== 0;
return getsReset;
}
//-----------------------------------------------------------------------------
// Binding Frames
//-----------------------------------------------------------------------------
/**
* Deletes all binding table entries.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspClearBindingTable() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CLEAR_BINDING_TABLE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Sets an entry in the binding table.
* @param index uint8_t The index of a binding table entry.
* @param value EmberBindingTableEntry * The contents of the binding entry.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspSetBinding(index, value) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_BINDING);
sendBuffalo.writeUInt8(index);
sendBuffalo.writeEmberBindingTableEntry(value);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Gets an entry from the binding table.
* @param index uint8_t The index of a binding table entry.
* @returns An SLStatus value indicating success or the reason for failure.
* @returns EmberBindingTableEntry * The contents of the binding entry.
*/
async ezspGetBinding(index) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_BINDING);
sendBuffalo.writeUInt8(index);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const value = this.buffalo.readEmberBindingTableEntry();
return [status, value];
}
/**
* Deletes a binding table entry.
* @param index uint8_t The index of a binding table entry.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspDeleteBinding(index) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.DELETE_BINDING);
sendBuffalo.writeUInt8(index);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Indicates whether any messages are currently being sent using this binding
* table entry. Note that this command does not indicate whether a binding is
* clear. To determine whether a binding is clear, check whether the type field
* of the EmberBindingTableEntry has the value EMBER_UNUSED_BINDING.
* @param index uint8_t The index of a binding table entry.
* @returns True if the binding table entry is active, false otherwise.
*/
async ezspBindingIsActive(index) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.BINDING_IS_ACTIVE);
sendBuffalo.writeUInt8(index);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const active = this.buffalo.readUInt8() !== 0;
return active;
}
/**
* Returns the node ID for the binding's destination, if the ID is known. If a
* message is sent using the binding and the destination's ID is not known, the
* stack will discover the ID by broadcasting a ZDO address request. The
* application can avoid the need for this discovery by using
* setBindingRemoteNodeId when it knows the correct ID via some other means. The
* destination's node ID is forgotten when the binding is changed, when the
* local node reboots or, much more rarely, when the destination node changes
* its ID in response to an ID conflict.
* @param index uint8_t The index of a binding table entry.
* @returns The short ID of the destination node or EMBER_NULL_NODE_ID if no destination is known.
*/
async ezspGetBindingRemoteNodeId(index) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_BINDING_REMOTE_NODE_ID);
sendBuffalo.writeUInt8(index);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const nodeId = this.buffalo.readUInt16();
return nodeId;
}
/**
* Set the node ID for the binding's destination. See getBindingRemoteNodeId for
* a description.
* @param index uint8_t The index of a binding table entry.
* @param The short ID of the destination node.
*/
async ezspSetBindingRemoteNodeId(index, nodeId) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_BINDING_REMOTE_NODE_ID);
sendBuffalo.writeUInt8(index);
sendBuffalo.writeUInt16(nodeId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Callback
* The NCP used the external binding modification policy to decide how to handle
* a remote set binding request. The Host cannot change the current decision,
* but it can change the policy for future decisions using the setPolicy
* command.
* @param entry EmberBindingTableEntry * The requested binding.
* @param index uint8_t The index at which the binding was added.
* @param policyDecision SLStatus.OK if the binding was added to the table and any other status if not.
*/
ezspRemoteSetBindingHandler(entry, index, policyDecision) {
logger_1.logger.debug(() => `ezspRemoteSetBindingHandler: entry=${JSON.stringify(entry)} index=${index} policyDecision=${enums_1.SLStatus[policyDecision]}`, NS);
}
/**
* Callback
* The NCP used the external binding modification policy to decide how to handle
* a remote delete binding request. The Host cannot change the current decision,
* but it can change the policy for future decisions using the setPolicy
* command.
* @param index uint8_t The index of the binding whose deletion was requested.
* @param policyDecision SLStatus.OK if the binding was removed from the table and any other status if not.
*/
ezspRemoteDeleteBindingHandler(index, policyDecision) {
logger_1.logger.debug(`ezspRemoteDeleteBindingHandler: index=${index} policyDecision=${enums_1.SLStatus[policyDecision]}`, NS);
}
//-----------------------------------------------------------------------------
// Messaging Frames
//-----------------------------------------------------------------------------
/**
* Returns the maximum size of the payload. The size depends on the security level in use.
* @returns uint8_t The maximum APS payload length.
*/
async ezspMaximumPayloadLength() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MAXIMUM_PAYLOAD_LENGTH);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const apsLength = this.buffalo.readUInt8();
return apsLength;
}
/**
* Sends a unicast message as per the Zigbee specification.
* The message will arrive at its destination only if there is a known route to the destination node.
* Setting the ENABLE_ROUTE_DISCOVERY option will cause a route to be discovered if none is known.
* Setting the FORCE_ROUTE_DISCOVERY option will force route discovery.
* Routes to end-device children of the local node are always known.
* Setting the APS_RETRY option will cause the message to be retransmitted until either a matching acknowledgement is received
* or three transmissions have been made.
* Note: Using the FORCE_ROUTE_DISCOVERY option will cause the first transmission to be consumed by a route request as part of discovery,
* so the application payload of this packet will not reach its destination on the first attempt. If you want the packet to reach its destination,
* the APS_RETRY option must be set so that another attempt is made to transmit the message with its application payload
* after the route has been constructed.
* Note: When sending fragmented messages, the stack will only assign a new APS sequence number for the first fragment of the message
* (i.e., SL_ZIGBEE_APS_OPTION_FRAGMENT is set and the low-order byte of the groupId field in the APS frame is zero).
* For all subsequent fragments of the same message, the application must set the sequence number field in the APS frame
* to the sequence number assigned by the stack to the first fragment.
* @param type Specifies the outgoing message type.
* Must be one of EMBER_OUTGOING_DIRECT, EMBER_OUTGOING_VIA_ADDRESS_TABLE, or EMBER_OUTGOING_VIA_BINDING.
* @param indexOrDestination Depending on the type of addressing used, this is either the NodeId of the destination,
* an index into the address table, or an index into the binding table.
* @param apsFrame EmberApsFrame * The APS frame which is to be added to the message.
* @param messageTag uint8_t (v14+: uint16_t) A value chosen by the Host.
* This value is used in the ezspMessageSentHandler response to refer to this message.
* @param messageContents uint8_t * Content of the message.
* @returns An SLStatus value indicating success or the reason for failure.
* @returns uint8_t * The sequence number that will be used when this message is transmitted.
*/
async ezspSendUnicast(type, indexOrDestination, apsFrame, messageTag, messageContents) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SEND_UNICAST);
sendBuffalo.writeUInt8(type);
sendBuffalo.writeUInt16(indexOrDestination);
sendBuffalo.writeEmberApsFrame(apsFrame);
if (this.version < 0x0e) {
sendBuffalo.writeUInt8(messageTag);
}
else {
sendBuffalo.writeUInt16(messageTag);
}
sendBuffalo.writePayload(messageContents);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const apsSequence = this.buffalo.readUInt8();
return [status, apsSequence];
}
/**
* Sends a broadcast message as per the Zigbee specification.
* @param alias uint16_t (unused in v13-) The aliased source from which we send the broadcast.
* This must be SL_ZIGBEE_NULL_NODE_ID if we do not need an aliased source
* @param destination The destination to which to send the broadcast. This must be one of the three Zigbee broadcast addresses.
* @param nwkSequence uint8_t (unused in v13-) The alias nwk sequence number. This won't be used if there is no aliased source.
* @param apsFrame EmberApsFrame * The APS frame for the message.
* @param radius uint8_t The message will be delivered to all nodes within radius hops of the sender.
* A radius of zero is converted to EMBER_MAX_HOPS.
* @param messageTag uint8_t (v14+: uint16_t) A value chosen by the Host.
* This value is used in the ezspMessageSentHandler response to refer to this message.
* @param messageContents uint8_t * The broadcast message.
* @returns An SLStatus value indicating success or the reason for failure.
* @returns uint8_t * The sequence number that will be used when this message is transmitted.
*/
async ezspSendBroadcast(alias, destination, nwkSequence, apsFrame, radius, messageTag, messageContents) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SEND_BROADCAST);
if (this.version < 0x0e) {
sendBuffalo.writeUInt16(destination);
sendBuffalo.writeEmberApsFrame(apsFrame);
sendBuffalo.writeUInt8(radius);
sendBuffalo.writeUInt8(messageTag);
}
else {
sendBuffalo.writeUInt16(alias);
sendBuffalo.writeUInt16(destination);
sendBuffalo.writeUInt8(nwkSequence);
sendBuffalo.writeEmberApsFrame(apsFrame);
sendBuffalo.writeUInt8(radius);
sendBuffalo.writeUInt16(messageTag);
}
sendBuffalo.writePayload(messageContents);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const apsSequence = this.buffalo.readUInt8();
return [status, apsSequence];
}
/**
* Sends proxied broadcast message for another node in conjunction with sl_zigbee_proxy_broadcast
* where a long source is also specified in the NWK frame control.
* @param euiSource The long source from which to send the broadcast
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspProxyNextBroadcastFromLong(euiSource) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.PROXY_NEXT_BROADCAST_FROM_LONG);
sendBuffalo.writeIeeeAddr(euiSource);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Sends a multicast message to all endpoints that share a specific multicast ID and are within a specified number of hops of the sender.
* @param apsFrame EmberApsFrame * The APS frame for the message. The multicast will be sent to the groupId in this frame.
* @param hops uint8_t The message will be delivered to all nodes within this number of hops of the sender.
* A value of zero is converted to EMBER_MAX_HOPS.
* @param broadcastAddr uint16_t (unused in v13-) The number of hops that the message will be forwarded by devices
* that are not members of the group.
* A value of 7 or greater is treated as infinite.
* @param alias uint16_t (unused in v13-) The alias source address. This must be SL_ZIGBEE_NULL_NODE_ID if we do not need an aliased source
* @param nwkSequence uint8_t (unused in v13-) The alias sequence number. This won't be used if there is no aliased source.
* @param messageTag uint8_t (v14+: uint16_t) A value chosen by the Host.
* This value is used in the ezspMessageSentHandler response to refer to this message.
* @param messageContents uint8_t * The multicast message.
* @returns An SLStatus value. For any result other than SLStatus.OK, the message will not be sent.
* - SLStatus.OK - The message has been submitted for transmission.
* - SLStatus.INVALID_INDEX - The bindingTableIndex refers to a non-multicast binding.
* - SLStatus.NETWORK_DOWN - The node is not part of a network.
* - SLStatus.MESSAGE_TOO_LONG - The message is too large to fit in a MAC layer frame.
* - SLStatus.ALLOCATION_FAILED - The free packet buffer pool is empty.
* - SLStatus.BUSY - Insufficient resources available in Network or MAC layers to send message.
* @returns uint8_t * The sequence number that will be used when this message is transmitted.
*/
async ezspSendMulticast(apsFrame, hops, broadcastAddr, alias, nwkSequence, messageTag, messageContents) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SEND_MULTICAST);
sendBuffalo.writeEmberApsFrame(apsFrame);
sendBuffalo.writeUInt8(hops);
if (this.version < 0x0e) {
sendBuffalo.writeUInt8(ZA_MAX_HOPS); // nonMemberRadius
sendBuffalo.writeUInt8(messageTag);
}
else {
sendBuffalo.writeUInt16(broadcastAddr);
sendBuffalo.writeUInt16(alias);
sendBuffalo.writeUInt8(nwkSequence);
sendBuffalo.writeUInt16(messageTag);
}
sendBuffalo.writePayload(messageContents);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const apsSequence = this.buffalo.readUInt8();
return [status, apsSequence];
}
/**
* Sends a reply to a received unicast message. The incomingMessageHandler
* callback for the unicast being replied to supplies the values for all the
* parameters except the reply itself.
* @param sender Value supplied by incoming unicast.
* @param apsFrame EmberApsFrame * Value supplied by incoming unicast.
* @param uint8_t The length of the messageContents parameter in bytes.
* @param uint8_t * The reply message.
* @returns
* - SLStatus.INVALID_STATE - The SL_ZIGBEE_EZSP_UNICAST_REPLIES_POLICY is set to SL_ZIGBEE_EZSP_HOST_WILL_NOT_SUPPLY_REPLY.
* This means the NCP will automatically send an empty reply. The Host must change
* the policy to SL_ZIGBEE_EZSP_HOST_WILL_SUPPLY_REPLY before it can supply the reply.
* There is one exception to this rule: In the case of responses to message
* fragments, the host must call sendReply when a message fragment is received.
* In this case, the policy set on the NCP does not matter. The NCP expects a
* sendReply call from the Host for message fragments regardless of the current
* policy settings.
* - SLStatus.ALLOCATION_FAILED - Not enough memory was available to send the reply.
* - SLStatus.BUSY - Either no route or insufficient resources available.
* - SLStatus.OK - The reply was successfully queued for transmission.
*/
async ezspSendReply(sender, apsFrame, messageContents) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SEND_REPLY);
sendBuffalo.writeUInt16(sender);
sendBuffalo.writeEmberApsFrame(apsFrame);
sendBuffalo.writePayload(messageContents);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Callback
* A callback indicating the stack has completed sending a message.
* @param status
* - SL_STATUS_OK if an ACK was received from the destination
* - SL_STATUS_ZIGBEE_DELIVERY_FAILED if no ACK was received.
* @param type The type of message sent.
* @param indexOrDestination uint16_t The destination to which the message was sent, for direct unicasts,
* or the address table or binding index for other unicasts. The value is unspecified for multicasts and broadcasts.
* @param apsFrame EmberApsFrame * The APS frame for the message.
* @param messageTag uint8_t The value supplied by the Host in the ezspSendUnicast, ezspSendBroadcast or ezspSendMulticast command.
* @param messageContents uint8_t * The unicast message supplied by the Host. The message contents are only included here if the decision
* for the messageContentsInCallback policy is messageTagAndContentsInCallback.
*/
ezspMessageSentHandler(status, type, indexOrDestination, apsFrame, messageTag, messageContents) {
logger_1.logger.debug(() => `ezspMessageSentHandler: status=${enums_1.SLStatus[status]} type=${enums_1.EmberOutgoingMessageType[type]} indexOrDestination=${indexOrDestination} apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag}${messageContents ? ` messageContents=${messageContents.toString("hex")}` : ""}`, NS);
this.emit("messageSent", status, type, indexOrDestination, apsFrame, messageTag);
}
/**
* Sends a route request packet that creates routes from every node in the
* network back to this node. This function should be called by an application
* that wishes to communicate with many nodes, for example, a gateway, central
* monitor, or controller. A device using this function was referred to as an
* 'aggregator' in EmberZNet 2.x and earlier, and is referred to as a
* 'concentrator' in the Zigbee specification and EmberZNet 3. This function
* enables large scale networks, because the other devices do not have to
* individually perform bandwidth-intensive route discoveries. Instead, when a
* remote node sends an APS unicast to a concentrator, its network layer
* automatically delivers a special route record packet first, which lists the
* network ids of all the intermediate relays. The concentrator can then use
* source routing to send outbound APS unicasts. (A source routed message is one
* in which the entire route is listed in the network layer header.) This allows
* the concentrator to communicate with thousands of devices without requiring
* large route tables on neighboring nodes. This function is only available in
* Zigbee Pro (stack profile 2), and cannot be called on end devices. Any router
* can be a concentrator (not just the coordinator), and there can be multiple
* concentrators on a network. Note that a concentrator does not automatically
* obtain routes to all network nodes after calling this function. Remote
* applications must first initiate an inbound APS unicast. Many-to-one routes
* are not repaired automatically. Instead, the concentrator application must
* call this function to rediscover the routes as necessary, for example, upon
* failure of a retried APS message. The reason for this is that there is no
* scalable one-size-fits-all route repair strategy. A common and recommended
* strategy is for the concentrator application to refresh the routes by calling
* this function periodically.
* @param concentratorType uint16_t Must be either EMBER_HIGH_RAM_CONCENTRATOR or EMBER_LOW_RAM_CONCENTRATOR.
* The former is used when the caller has enough memory to store source routes for the whole network.
* In that case, remote nodes stop sending route records once the concentrator has successfully received one.
* The latter is used when the concentrator has insufficient RAM to store all outbound source routes.
* In that case, route records are sent to the concentrator prior to every inbound APS unicast.
* @param radius uint8_t The maximum number of hops the route request will be relayed. A radius of zero is converted to EMBER_MAX_HOPS
* @returns
* - SLStatus.OK if the route request was successfully submitted to the transmit queue,
* - SLStatus.FAIL otherwise.
*/
async ezspSendManyToOneRouteRequest(concentratorType, radius) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SEND_MANY_TO_ONE_ROUTE_REQUEST);
sendBuffalo.writeUInt16(concentratorType);
sendBuffalo.writeUInt8(radius);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Periodically request any pending data from our parent. Setting interval to 0
* or units to EMBER_EVENT_INACTIVE will generate a single poll.
* @param interval uint16_t The time between polls. Note that the timer clock is free running and is not synchronized with this command.
* This means that the time will be between interval and (interval - 1). The maximum interval is 32767.
* @param units The units for interval.
* @param failureLimit uint8_t The number of poll failures that will be tolerated before a pollCompleteHandler callback is generated.
* A value of zero will result in a callback for every poll. Any status value apart from EMBER_SUCCESS
* and EMBER_MAC_NO_DATA is counted as a failure.
* @returns The result of sending the first poll.
*/
async ezspPollForData(interval, units, failureLimit) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.POLL_FOR_DATA);
sendBuffalo.writeUInt16(interval);
sendBuffalo.writeUInt8(units);
sendBuffalo.writeUInt8(failureLimit);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Callback
* Indicates the result of a data poll to the parent of the local node.
* @param status An SLStatus value:
* - SLStatus.OK - Data was received in response to the poll.
* - SLStatus.MAC_NO_DATA - No data was pending.
* - SLStatus.ZIGBEE_DELIVERY_FAILED - The poll message could not be sent.
* - SLStatus.MAC_NO_ACK_RECEIVED - The poll message was sent but not acknowledged by the parent.
*/
ezspPollCompleteHandler(status) {
logger_1.logger.debug(`ezspPollCompleteHandler: status=${enums_1.SLStatus[status]}`, NS);
}
/**
* Set a flag to indicate that a message is pending for a child.
* The next time that the child polls, it will be informed that it has a pending message.
* The message is sent from emberPollHandler, which is called when the child requests data.
* @param childId The ID of the child that just polled for data.
* @returns
* - SLStatus.OK - The next time that the child polls, it will be informed that it has pending data.
* - SLStatus.NOT_JOINED - The child identified by childId is not our child.
*/
async ezspSetMessageFlag(childId) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_MESSAGE_FLAG);
sendBuffalo.writeUInt16(childId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Clear a flag to indicate that there are no more messages for a child.
* The next time the child polls, it will be informed that it does not have any pending messages.
* @param childId The ID of the child that no longer has pending messages.
* @returns
* - SLStatus.OK - The next time that the child polls, it will be informed that it does not have any pending messages.
* - SLStatus.NOT_JOINED - The child identified by childId is not our child.
*/
async ezspClearMessageFlag(childId) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CLEAR_MESSAGE_FLAG);
sendBuffalo.writeUInt16(childId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Callback
* Indicates that the local node received a data poll from a child.
* @param childId The node ID of the child that is requesting data.
* @param transmitExpected True if transmit is expected, false otherwise.
*/
ezspPollHandler(childId, transmitExpected) {
logger_1.logger.debug(`ezspPollHandler: childId=${childId} transmitExpected=${transmitExpected}`, NS);
}
/**
* Add a child to the child/neighbor table only on SoC, allowing direct manipulation of these tables by the application.
* This can affect the network functionality, and needs to be used wisely.
* If used appropriately, the application can maintain more than the maximum of children provided by the stack.
* @param shortId The preferred short ID of the node.
* @param longId The long ID of the node.
* @param nodeType The nodetype e.g., SL_ZIGBEE_ROUTER defining, if this would be added to the child table or neighbor table.
* @returns
* - SLStatus.OK - This node has been successfully added.
* - SLStatus.FAIL - The child was not added to the child/neighbor table.
*/
async ezspAddChild(shortId, longId, nodeType) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ADD_CHILD);
sendBuffalo.writeUInt16(shortId);
sendBuffalo.writeIeeeAddr(longId);
sendBuffalo.writeUInt8(nodeType);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Remove a node from child/neighbor table only on SoC, allowing direct manipulation of these tables by the application.
* This can affect the network functionality, and needs to be used wisely.
* @param childEui64 The long ID of the node.
* @returns
* - SLStatus.OK - This node has been successfully removed.
* - SLStatus.FAIL - The node was not found in either of the child or neighbor tables.
*/
async ezspRemoveChild(childEui64) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.REMOVE_CHILD);
sendBuffalo.writeIeeeAddr(childEui64);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Remove a neighbor from neighbor table only on SoC, allowing direct manipulation of neighbor table by the application.
* This can affect the network functionality, and needs to be used wisely.
* @param shortId The short ID of the neighbor.
* @param longId The long ID of the neighbor.
*/
async ezspRemoveNeighbor(shortId, longId) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.REMOVE_NEIGHBOR);
sendBuffalo.writeUInt16(shortId);
sendBuffalo.writeIeeeAddr(longId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
// XXX: watchout for update here, Silabs likely forgot status return
}
/**
* Callback
* A callback indicating a message has been received.
* @param type The type of the incoming message. One of the following: EMBER_INCOMING_UNICAST, EMBER_INCOMING_UNICAST_REPLY,
* EMBER_INCOMING_MULTICAST, EMBER_INCOMING_MULTICAST_LOOPBACK, EMBER_INCOMING_BROADCAST, EMBER_INCOMING_BROADCAST_LOOPBACK
* @param apsFrame EmberApsFrame * The APS frame from the incoming message.
* @param packetInfo Miscellanous message information.
* @param messageContents uint8_t * The incoming message.
*/
ezspIncomingMessageHandler(type, apsFrame, packetInfo, messageContents) {
logger_1.logger.debug(() => `ezspIncomingMessageHandler: type=${enums_1.EmberIncomingMessageType[type]} apsFrame=${JSON.stringify(apsFrame)} ` +
`packetInfo:${JSON.stringify(packetInfo)} messageContents=${messageContents.toString("hex")}`, NS);
if (apsFrame.profileId === Zdo.ZDO_PROFILE_ID) {
this.emit("zdoResponse", apsFrame, packetInfo.senderShortId, messageContents);
}
else if (apsFrame.profileId === ZSpec.HA_PROFILE_ID ||
apsFrame.profileId === ZSpec.WILDCARD_PROFILE_ID ||
(apsFrame.profileId === ZSpec.GP_PROFILE_ID && type !== enums_1.EmberIncomingMessageType.BROADCAST_LOOPBACK) ||
// TODO: Better (dynamic) way to handle this?
// Shelly custom Clusters require a special profile ID
apsFrame.profileId === ZSpec.CUSTOM_SHELLY_PROFILE_ID) {
this.emit("incomingMessage", type, apsFrame, packetInfo.lastHopLqi, packetInfo.senderShortId, messageContents);
}
}
/**
* Sets source route discovery(MTORR) mode to on, off, reschedule
* @param mode uint8_t Source route discovery mode: off:0, on:1, reschedule:2
* @returns uint32_t Remaining time(ms) until next MTORR broadcast if the mode is on, MAX_INT32U_VALUE if the mode is off
*/
async ezspSetSourceRouteDiscoveryMode(mode) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_SOURCE_ROUTE_DISCOVERY_MODE);
sendBuffalo.writeUInt8(mode);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const remainingTime = this.buffalo.readUInt32();
return remainingTime;
}
/**
* Callback
* A callback indicating that a many-to-one route to the concentrator with the given short and long id is available for use.
* @param NodeId The short id of the concentrator.
* @param longId The EUI64 of the concentrator.
* @param cost uint8_t The path cost to the concentrator. The cost may decrease as additional route request packets
* for this discovery arrive, but the callback is made only once.
*/
ezspIncomingManyToOneRouteRequestHandler(source, longId, cost) {
logger_1.logger.debug(`ezspIncomingManyToOneRouteRequestHandler: source=${source} longId=${longId} cost=${cost}`, NS);
}
/**
* Callback
* A callback invoked when a route error message is received.
* The error indicates that a problem routing to or from the target node was encountered.
*
* A status of ::EMBER_SOURCE_ROUTE_FAILURE indicates that a source-routed unicast sent from this node encountered a broken link.
* Note that this case occurs only if this node is a concentrator using many-to-one routing for inbound messages and source-routing for
* outbound messages. The node prior to the broken link generated the route error message and returned it to us along the many-to-one route.
*
* A status of ::EMBER_MANY_TO_ONE_ROUTE_FAILURE also occurs only if the local device is a concentrator, and indicates that a unicast sent
* to the local device along a many-to-one route encountered a broken link. The node prior to the broken link generated the route error
* message and forwarded it to the local device via a randomly chosen neighbor, taking advantage of the many-to-one nature of the route.
*
* A status of ::EMBER_MAC_INDIRECT_TIMEOUT indicates that a message sent to the target end device could not be delivered by the parent
* because the indirect transaction timer expired. Upon receipt of the route error, the stack sets the extended timeout for the target node
* in the address table, if present. It then calls this handler to indicate receipt of the error.
*
* Note that if the original unicast data message is sent using the ::EMBER_APS_OPTION_RETRY option, a new route error message is generated
* for each failed retry. Therefore, it is not unusual to receive three route error messages in succession for a single failed retried APS
* unicast. On the other hand, it is also not guaranteed that any route error messages will be delivered successfully at all.
* The only sure way to detect a route failure is to use retried APS messages and to check the status of the ::emberMessageSentHandler().
*
* @param status ::EMBER_SOURCE_ROUTE_FAILURE, ::EMBER_MANY_TO_ONE_ROUTE_FAILURE, ::EMBER_MAC_INDIRECT_TIMEOUT
* @param target The short id of the remote node.
*/
ezspIncomingRouteErrorHandler(status, target) {
logger_1.logger.debug(`ezspIncomingRouteErrorHandler: status=${enums_1.SLStatus[status]} target=${target}`, NS);
// NOTE: This can trigger immediately after removal of a device with status MAC_INDIRECT_TIMEOUT
}
/**
* Callback
* A callback invoked when a network status/route error message is received.
* The error indicates that there was a problem sending/receiving messages from the target node.
*
* Note: Network analyzer may flag this message as "route error" which is the old name for the "network status" command.
*
* This handler is a superset of ezspIncomingRouteErrorHandler. The old API was only invoking the handler for a couple of the possible
* error codes and these were being translated into EmberStatus.
*
* @param errorCode uint8_t One byte over-the-air error code from network status message
* @param target The short ID of the remote node
*/
ezspIncomingNetworkStatusHandler(errorCode, target) {
logger_1.logger.debug(`ezspIncomingNetworkStatusHandler: errorCode=${enums_1.EmberStackError[errorCode]} target=${target}`, NS);
logger_1.logger.info(`Received network/route error ${enums_1.EmberStackError[errorCode]} for "${target}".`, NS);
}
/**
* Callback
* Reports the arrival of a route record command frame.
* @param NodeId The source of the route record.
* @param EUI64 The EUI64 of the source.
* @param lastHopLqi uint8_t The link quality from the node that last relayed the route record.
* @param lastHopRssi int8_t The energy level (in units of dBm) observed during the reception.
* @param uint8_t The number of relays in relayList.
* @param relayList uint8_t * The route record. Each relay in the list is an uint16_t node ID.
* The list is passed as uint8_t * to avoid alignment problems.
*/
ezspIncomingRouteRecordHandler(source, sourceEui, lastHopLqi, lastHopRssi, relayCount, relayList) {
logger_1.logger.debug(`ezspIncomingRouteRecordHandler: source=${source} sourceEui=${sourceEui} lastHopLqi=${lastHopLqi} lastHopRssi=${lastHopRssi} relayCount=${relayCount} relayList=${relayList}`, NS);
// XXX: could at least trigger a `Events.lastSeenChanged` but this is not currently being listened to at the adapter level
}
/**
* Send the network key to a destination.
* @param targetShort The destination node of the key.
* @param targetLong The long address of the destination node.
* @param parentShortId The parent node of the destination node.
* @returns SLStatus.OK if send was successful
*/
async ezspUnicastCurrentNetworkKey(targetShort, targetLong, parentShortId) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.UNICAST_CURRENT_NETWORK_KEY);
sendBuffalo.writeUInt16(targetShort);
sendBuffalo.writeIeeeAddr(targetLong);
sendBuffalo.writeUInt16(parentShortId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Indicates whether any messages are currently being sent using this address
* table entry. Note that this function does not indicate whether the address
* table entry is unused. To determine whether an address table entry is unused,
* check the remote node ID. The remote node ID will have the value
* EMBER_TABLE_ENTRY_UNUSED_NODE_ID when the address table entry is not in use.
* @param uint8_tThe index of an address table entry.
* @returns True if the address table entry is active, false otherwise.
*/
async ezspAddressTableEntryIsActive(addressTableIndex) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ADDRESS_TABLE_ENTRY_IS_ACTIVE);
sendBuffalo.writeUInt8(addressTableIndex);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const active = this.buffalo.readUInt8() !== 0;
return active;
}
/**
* Sets the EUI64 and short ID of an address table entry.
* Usually the application will not need to set the short ID in the address table.
* Once the remote EUI64 is set the stack is capable of figuring out the short ID on its own.
* However, in cases where the application does set the short ID, the application must set the remote EUI64 prior to setting the short ID.
* This function will also check other address table entries, the child table and the neighbor table to see
* if the node ID for the given EUI64 is already known.
* If known then this function will set node ID. If not known it will set the node ID to SL_ZIGBEE_UNKNOWN_NODE_ID.
* @param addressTableIndex
* @param eui64
* @param id
* @returns
* - SLStatus.OK if the information was successfully set,
* - SLStatus.ZIGBEE_ADDRESS_TABLE_ENTRY_IS_ACTIVE otherwise.
*/
async ezspSetAddressTableInfo(addressTableIndex, eui64, id) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_ADDRESS_TABLE_INFO);
sendBuffalo.writeUInt8(addressTableIndex);
sendBuffalo.writeIeeeAddr(eui64);
sendBuffalo.writeUInt16(id);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Gets the EUI64 and short ID of an address table entry.
* @param addressTableIndex
* @returns An SLStatus value indicating success or the reason for failure.
* @returns One of the following:
* - The short ID corresponding to the remote node whose EUI64 is stored in the address table at the given index.
* - SL_ZIGBEE_UNKNOWN_NODE_ID:
* Indicates that the EUI64 stored in the address table at the given index is valid but the short ID is currently unknown.
* - SL_ZIGBEE_DISCOVERY_ACTIVE_NODE_ID:
* Indicates that the EUI64 stored in the address table at the given location is valid and network address discovery is underway.
* - SL_ZIGBEE_TABLE_ENTRY_UNUSED_NODE_ID:
* Indicates that the entry stored in the address table at the given index is not in use.
* @returns The EUI64 of the address table entry is copied to this location.
*/
async ezspGetAddressTableInfo(addressTableIndex) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_ADDRESS_TABLE_INFO);
sendBuffalo.writeUInt8(addressTableIndex);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
const nodeId = this.buffalo.readUInt16();
const eui64 = this.buffalo.readIeeeAddr();
return [status, nodeId, eui64];
}
/**
* Tells the stack whether or not the normal interval between retransmissions of a retried unicast message should
* be increased by EMBER_INDIRECT_TRANSMISSION_TIMEOUT.
* The interval needs to be increased when sending to a sleepy node so that the message is not retransmitted until the destination
* has had time to wake up and poll its parent.
* The stack will automatically extend the timeout:
* - For our own sleepy children.
* - When an address response is received from a parent on behalf of its child.
* - When an indirect transaction expiry route error is received.
* - When an end device announcement is received from a sleepy node.
* @param remoteEui64 The address of the node for which the timeout is to be set.
* @param extendedTimeout true if the retry interval should be increased by EMBER_INDIRECT_TRANSMISSION_TIMEOUT.
* false if the normal retry interval should be used.
* @returns An SLStatus value indicating success or the reason for failure. Always `OK` in v13-.
*/
async ezspSetExtendedTimeout(remoteEui64, extendedTimeout) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_EXTENDED_TIMEOUT);
sendBuffalo.writeIeeeAddr(remoteEui64);
sendBuffalo.writeUInt8(extendedTimeout ? 1 : 0);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
if (this.version < 0x0e) {
return enums_1.SLStatus.OK;
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Indicates whether or not the stack will extend the normal interval between
* retransmissions of a retried unicast message by
* EMBER_INDIRECT_TRANSMISSION_TIMEOUT.
* @param remoteEui64 The address of the node for which the timeout is to be returned.
* @returns
* - SLStatus.OK if the retry interval will be increased by SL_ZIGBEE_INDIRECT_TRANSMISSION_TIMEOUT
* - SLStatus.FAIL if the normal retry interval will be used.
*/
async ezspGetExtendedTimeout(remoteEui64) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_EXTENDED_TIMEOUT);
sendBuffalo.writeIeeeAddr(remoteEui64);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
if (this.version < 0x0e) {
const extendedTimeout = this.buffalo.readUInt8() !== 0;
return extendedTimeout ? enums_1.SLStatus.OK : enums_1.SLStatus.FAIL;
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Replaces the EUI64, short ID and extended timeout setting of an address table
* entry. The previous EUI64, short ID and extended timeout setting are
* returned.
* @param addressTableIndex uint8_t The index of the address table entry that will be modified.
* @param newEui64 The EUI64 to be written to the address table entry.
* @param newId One of the following: The short ID corresponding to the new EUI64.
* SL_ZIGBEE_UNKNOWN_NODE_ID if the new EUI64 is valid but the short ID is unknown and should be discovered by the stack.
* SL_ZIGBEE_TABLE_ENTRY_UNUSED_NODE_ID if the address table entry is now unused.
* @param newExtendedTimeout true if the retry interval should be increased by SL_ZIGBEE_INDIRECT_TRANSMISSION_TIMEOUT.
* false if the normal retry interval should be used.
* @returns
* - SLStatus.OK if the EUI64, short ID and extended timeout setting were successfully modified,
* - SLStatus.ZIGBEE_ADDRESS_TABLE_ENTRY_IS_ACTIVE otherwise.
* @returns oldEui64 The EUI64 of the address table entry before it was modified.
* @returns oldId NodeId * One of the following: The short ID corresponding to the EUI64 before it was modified.
* SL_ZIGBEE_UNKNOWN_NODE_ID if the short ID was unknown. SL_ZIGBEE_DISCOVERY_ACTIVE_NODE_ID if discovery of the short ID was underway.
* SL_ZIGBEE_TABLE_ENTRY_UNUSED_NODE_ID if the address table entry was unused.
* @returns oldExtendedTimeouttrue bool * if the retry interval was being increased by SL_ZIGBEE_INDIRECT_TRANSMISSION_TIMEOUT.
* false if the normal retry interval was being used.
*/
async ezspReplaceAddressTableEntry(addressTableIndex, newEui64, newId, newExtendedTimeout) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.REPLACE_ADDRESS_TABLE_ENTRY);
sendBuffalo.writeUInt8(addressTableIndex);
sendBuffalo.writeIeeeAddr(newEui64);
sendBuffalo.writeUInt16(newId);
sendBuffalo.writeUInt8(newExtendedTimeout ? 1 : 0);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const oldEui64 = this.buffalo.readIeeeAddr();
const oldId = this.buffalo.readUInt16();
const oldExtendedTimeout = this.buffalo.readUInt8() !== 0;
return [status, oldEui64, oldId, oldExtendedTimeout];
}
/**
* Returns the node ID that corresponds to the specified EUI64. The node ID is
* found by searching through all stack tables for the specified EUI64.
* @param eui64 The EUI64 of the node to look up.
* @returns The short ID of the node or SL_ZIGBEE_NULL_NODE_ID if the short ID is not known.
*/
async ezspLookupNodeIdByEui64(eui64) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.LOOKUP_NODE_ID_BY_EUI64);
sendBuffalo.writeIeeeAddr(eui64);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const nodeId = this.buffalo.readUInt16();
return nodeId;
}
/**
* Returns the EUI64 that corresponds to the specified node ID. The EUI64 is
* found by searching through all stack tables for the specified node ID.
* @param nodeId The short ID of the node to look up.
* @returns
* - SLStatus.OK if the EUI64 was found,
* - SLStatus.FAIL if the EUI64 is not known.
* @returns eui64 The EUI64 of the node.
*/
async ezspLookupEui64ByNodeId(nodeId) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.LOOKUP_EUI64_BY_NODE_ID);
sendBuffalo.writeUInt16(nodeId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const eui64 = this.buffalo.readIeeeAddr();
return [status, eui64];
}
/**
* Gets an entry from the multicast table.
* @param uint8_t The index of a multicast table entry.
* @returns An SLStatus value indicating success or the reason for failure.
* @returns EmberMulticastTableEntry * The contents of the multicast entry.
*/
async ezspGetMulticastTableEntry(index) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_MULTICAST_TABLE_ENTRY);
sendBuffalo.writeUInt8(index);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const value = this.buffalo.readEmberMulticastTableEntry();
return [status, value];
}
/**
* Sets an entry in the multicast table.
* @param index uint8_t The index of a multicast table entry
* @param EmberMulticastTableEntry * The contents of the multicast entry.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspSetMulticastTableEntry(index, value) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_MULTICAST_TABLE_ENTRY);
sendBuffalo.writeUInt8(index);
sendBuffalo.writeEmberMulticastTableEntry(value);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Callback
* A callback invoked by the EmberZNet stack when an id conflict is discovered,
* that is, two different nodes in the network were found to be using the same
* short id. The stack automatically removes the conflicting short id from its
* internal tables (address, binding, route, neighbor, and child tables). The
* application should discontinue any other use of the id.
* @param id The short id for which a conflict was detected
*/
ezspIdConflictHandler(id) {
logger_1.logger.debug(`ezspIdConflictHandler: id=${id}`, NS);
logger_1.logger.warning(`An ID conflict was detected for network address '${id}'. Corresponding devices kicked from the network.`, NS);
// XXX: this is currently causing more problems than not doing it, so disabled for now.
// devices should rejoin on ID conflict anyway, so the database isn't out of sync for very long.
// hijacking the event from `ezspTrustCenterJoinHandler`, and forging a DEVICE_LEFT to avoid another event ending up doing the same logic
// this.emit('trustCenterJoin', id, null, EmberDeviceUpdate.DEVICE_LEFT, EmberJoinDecision.NO_ACTION, NULL_NODE_ID);
}
/**
* Write the current node Id, PAN ID, or Node type to the tokens
* @param erase Erase the node type or not
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspWriteNodeData(erase) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.WRITE_NODE_DATA);
sendBuffalo.writeUInt8(erase ? 1 : 0);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Transmits the given message without modification. The MAC header is assumed
* to be configured in the message at the time this function is called.
* @param messageContents uint8_t * The raw message.
* @param priority uint8_t transmit priority.
* @param useCca Should we enable CCA or not.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspSendRawMessage(messageContents, priority, useCca) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SEND_RAW_MESSAGE);
sendBuffalo.writePayload(messageContents);
sendBuffalo.writeUInt8(priority);
sendBuffalo.writeUInt8(useCca ? 1 : 0);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Callback
* A callback invoked by the EmberZNet stack when a MAC passthrough message is
* received.
* @param messageType The type of MAC passthrough message received.
* @param packetInfo Information about the incoming packet.
* @param messageContents uint8_t * The raw message that was received.
*/
ezspMacPassthroughMessageHandler(messageType, packetInfo, messageContents) {
logger_1.logger.debug(() => `ezspMacPassthroughMessageHandler: messageType=${messageType} packetInfo=${JSON.stringify(packetInfo)} messageContents=${messageContents.toString("hex")}`, NS);
}
/**
* Callback
* A callback invoked by the EmberZNet stack when a raw MAC message that has
* matched one of the application's configured MAC filters.
* @param filterValueMatch uint16_t The value of the filter that was matched.
* - < v18: uint8_t The index of the filter that was matched.
* @param legacyPassthroughType The type of MAC passthrough message received.
* @param packetInfo Information about the incoming packet.
* @param messageContents uint8_t * The raw message that was received.
*/
ezspMacFilterMatchMessageHandler(filterValueMatch, legacyPassthroughType, packetInfo, messageContents) {
logger_1.logger.debug(() => `ezspMacFilterMatchMessageHandler: filterValueMatch=${filterValueMatch} legacyPassthroughType=${legacyPassthroughType} ` +
`packetInfo=${JSON.stringify(packetInfo)} messageContents=${messageContents.toString("hex")}`, NS);
// TODO: needs triple-checking, this is only valid for InterPAN messages
const msgBuffalo = new buffalo_1.EzspBuffalo(messageContents, 0);
const macFrameControl = msgBuffalo.readUInt16() & ~consts_1.MAC_ACK_REQUIRED;
msgBuffalo.readUInt8(); // sequence, unused
msgBuffalo.readUInt16(); // destPanId, unused
if (macFrameControl === consts_1.LONG_DEST_FRAME_CONTROL) {
msgBuffalo.readIeeeAddr(); // destAddress, unused
}
else if (macFrameControl === consts_1.SHORT_DEST_FRAME_CONTROL) {
msgBuffalo.readUInt16(); // destAddress, unused
}
else {
logger_1.logger.debug(`ezspMacFilterMatchMessageHandler INVALID InterPAN macFrameControl '${macFrameControl}'.`, NS);
return;
}
const sourcePanId = msgBuffalo.readUInt16();
const sourceAddress = msgBuffalo.readIeeeAddr();
// Now that we know the correct MAC length, verify the interpan frame is the correct length.
let remainingLength = msgBuffalo.getBufferLength() - msgBuffalo.getPosition();
if (remainingLength < consts_1.STUB_NWK_SIZE + consts_1.MIN_STUB_APS_SIZE) {
logger_1.logger.debug(`ezspMacFilterMatchMessageHandler INVALID InterPAN length '${remainingLength}'.`, NS);
return;
}
const nwkFrameControl = msgBuffalo.readUInt16();
remainingLength -= 2; // read 2 more bytes before APS stuff
if (nwkFrameControl !== consts_1.STUB_NWK_FRAME_CONTROL) {
logger_1.logger.debug(`ezspMacFilterMatchMessageHandler INVALID InterPAN nwkFrameControl '${nwkFrameControl}'.`, NS);
return;
}
const apsFrameControl = msgBuffalo.readUInt8();
if ((apsFrameControl & ~consts_1.INTERPAN_APS_FRAME_DELIVERY_MODE_MASK & ~consts_1.INTERPAN_APS_FRAME_SECURITY) !==
consts_1.INTERPAN_APS_FRAME_CONTROL_NO_DELIVERY_MODE) {
logger_1.logger.debug(`ezspMacFilterMatchMessageHandler INVALID InterPAN apsFrameControl '${apsFrameControl}'.`, NS);
return;
}
const messageType = apsFrameControl & consts_1.INTERPAN_APS_FRAME_DELIVERY_MODE_MASK;
let groupId = 0; // XXX: looks fine from z2m code?
switch (messageType) {
case enums_1.EmberInterpanMessageType.UNICAST:
case enums_1.EmberInterpanMessageType.BROADCAST: {
if (remainingLength < consts_1.INTERPAN_APS_UNICAST_BROADCAST_SIZE) {
logger_1.logger.debug(`ezspMacFilterMatchMessageHandler INVALID InterPAN length '${remainingLength}'.`, NS);
return;
}
break;
}
case enums_1.EmberInterpanMessageType.MULTICAST: {
if (remainingLength < consts_1.INTERPAN_APS_MULTICAST_SIZE) {
logger_1.logger.debug(`ezspMacFilterMatchMessageHandler INVALID InterPAN length '${remainingLength}'.`, NS);
return;
}
groupId = msgBuffalo.readUInt16();
break;
}
default: {
logger_1.logger.debug(`ezspMacFilterMatchMessageHandler INVALID InterPAN messageType '${messageType}'.`, NS);
return;
}
}
const clusterId = msgBuffalo.readUInt16();
const profileId = msgBuffalo.readUInt16();
const payload = msgBuffalo.readRest();
if (profileId === ZSpec.TOUCHLINK_PROFILE_ID && clusterId === cluster_1.Clusters.touchlink.ID) {
this.emit("touchlinkMessage", sourcePanId, sourceAddress, groupId, packetInfo.lastHopLqi, payload);
}
}
/**
* Callback
* A callback invoked by the EmberZNet stack when the MAC has finished
* transmitting a raw message.
* @param messageContents (v14+)
* @param status
* - SLStatus.OK if the transmission was successful,
* - SLStatus.ZIGBEE_DELIVERY_FAILED if not
*/
ezspRawTransmitCompleteHandler(messageContents, status) {
logger_1.logger.debug(`ezspRawTransmitCompleteHandler: messageContents=${messageContents.toString("hex")} status=${enums_1.SLStatus[status]}`, NS);
}
/**
* This function is useful to sleepy end devices.
* This function will set the retry interval (in milliseconds) for mac data poll.
* This interval is the time in milliseconds the device waits before retrying a data poll when a MAC level
* data poll fails for any reason.
* @param waitBeforeRetryIntervalMs uint32_t Time in milliseconds the device waits before retrying
* a data poll when a MAC level data poll fails for any reason.
*/
async ezspSetMacPollFailureWaitTime(waitBeforeRetryIntervalMs) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_MAC_POLL_FAILURE_WAIT_TIME);
sendBuffalo.writeUInt32(waitBeforeRetryIntervalMs);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Returns the maximum number of no-ack retries that will be attempted
* @returns Max MAC retries
*/
async ezspGetMaxMacRetries() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_MAX_MAC_RETRIES);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const retries = this.buffalo.readUInt32();
return retries;
}
/**
* Sets the priority masks and related variables for choosing the best beacon.
* @param param EmberBeaconClassificationParams * The beacon prioritization related variable
* @returns The attempt to set the parameters returns SLStatus.OK
*/
async ezspSetBeaconClassificationParams(param) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_BEACON_CLASSIFICATION_PARAMS);
sendBuffalo.writeEmberBeaconClassificationParams(param);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Gets the priority masks and related variables for choosing the best beacon.
* @returns The attempt to get the parameters returns SLStatus.OK
* @returns EmberBeaconClassificationParams * Gets the beacon prioritization related variable
*/
async ezspGetBeaconClassificationParams() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_BEACON_CLASSIFICATION_PARAMS);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const param = this.buffalo.readEmberBeaconClassificationParams();
return [status, param];
}
/**
* Indicate whether there are pending messages in the APS retry queue.
* @returns True if there is a pending message for this network in the APS retry queue, false if not.
*/
async ezspPendingAckedMessages() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.PENDING_ACKED_MESSAGES);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const pendingMessages = this.buffalo.readUInt8() !== 0;
return pendingMessages;
}
/**
* Reschedule sending link status message, with first one being sent immediately.
* @returns
*/
async ezspRescheduleLinkStatusMsg() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.RESCHEDULE_LINK_STATUS_MSG);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Set the network update ID to the desired value. Must be called before joining or forming the network.
* @param nwkUpdateId uint8_t Desired value of the network update ID.
* @param setWhenOnNetwork Set to true in case change should also apply when on network.
* @returns Status of set operation for the network update ID.
*/
async ezspSetNwkUpdateId(nwkUpdateId, setWhenOnNetwork) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_NWK_UPDATE_ID);
sendBuffalo.writeUInt8(nwkUpdateId);
sendBuffalo.writeUInt8(setWhenOnNetwork ? 1 : 0);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
//-----------------------------------------------------------------------------
// Security Frames
//-----------------------------------------------------------------------------
/**
* Sets the security state that will be used by the device when it forms or
* joins the network. This call should not be used when restoring saved network
* state via networkInit as this will result in a loss of security data and will
* cause communication problems when the device re-enters the network.
* @param state EmberInitialSecurityState * The security configuration to be set.
* @returns The success or failure code of the operation.
*/
async ezspSetInitialSecurityState(state) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_INITIAL_SECURITY_STATE);
sendBuffalo.writeEmberInitialSecurityState(state);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Gets the current security state that is being used by a device that is joined
* in the network.
* @returns The success or failure code of the operation.
* @returns EmberCurrentSecurityState * The security configuration in use by the stack.
*/
async ezspGetCurrentSecurityState() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_CURRENT_SECURITY_STATE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const state = this.buffalo.readEmberCurrentSecurityState();
return [status, state];
}
/**
* Exports a key from security manager based on passed context.
* @param context sl_zb_sec_man_context_t * Metadata to identify the requested key.
* @returns sl_zb_sec_man_key_t * Data to store the exported key in.
* @returns SLStatus * The success or failure code of the operation.
*/
async ezspExportKey(context) {
/**
* Export a key from storage. Certain keys are indexed, while others are not, as described here.
*
* If context->core_key_type is..
*
* ..SL_ZB_SEC_MAN_KEY_TYPE_NETWORK, then context->key_index dictates whether to
* export the current (active) network key (index 0) or the alternate network
* key (index 1).
*
* ..SL_ZB_SEC_MAN_KEY_TYPE_TC_LINK, then context->eui64 is checked if
* context->flags is set to ZB_SEC_MAN_FLAG_EUI_IS_VALID. If the EUI supplied
* does not match the TC EUI stored on the local device (if it is known), then
* an error is thrown.
*
* ..SL_ZB_SEC_MAN_KEY_TYPE_TC_LINK_WITH_TIMEOUT, then keys may be searched by
* context->eui64 or context->key_index. context->flags determines how to search
* (see ::sl_zigbee_sec_man_flags_t).
*
* ..SL_ZB_SEC_MAN_KEY_TYPE_APP_LINK, then keys may be searched by
* context->eui64 or context->key_index. context->flags determines how to search
* (see ::sl_zigbee_sec_man_flags_t).
*
* ..SL_ZB_SEC_MAN_KEY_TYPE_GREEN_POWER_PROXY_TABLE_KEY or
* SL_ZB_SEC_MAN_KEY_TYPE_GREEN_POWER_SINK_TABLE_KEY, then context->key_index
* dictates which key entry to export. These Green Power keys are indexed keys,
* and there are EMBER_GP_PROXY_TABLE_SIZE/EMBER_GP_SINK_TABLE_SIZE many of them.
*
* For all other key types, both context->key_index and context->eui64 are not used.
*
* @param context sl_zb_sec_man_context_t* [IN/OUT] The context to set. The context dictates which key
* type to export, which key_index (if applicable) into the relevant key
* storage, which eui64 (if applicable), etc.
* @param plaintext_key sl_zb_sec_man_key_t* [OUT] The key to export.
*
* @note The context->derived_type must be SL_ZB_SEC_MAN_DERIVED_KEY_TYPE_NONE.
* Other values are ignored.
*
* @return SLStatus.OK upon success, a valid error code otherwise.
*/
// NOTE: added for good measure
if (context.coreKeyType === enums_1.SecManKeyType.INTERNAL) {
logger_1.logger.error("ezspExportKey cannot use INTERNAL key type.", NS);
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_CALL);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.EXPORT_KEY);
sendBuffalo.writeSecManContext(context);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
if (this.version < 0x0e) {
const key = this.buffalo.readSecManKey();
const status = this.buffalo.readUInt32();
return [status, key];
}
const status = this.buffalo.readUInt32();
const key = this.buffalo.readSecManKey();
context = this.buffalo.readSecManContext();
return [status, key];
}
/**
* Imports a key into security manager based on passed context.
* @param context sl_zb_sec_man_context_t * Metadata to identify where the imported key should be stored.
* @param key sl_zb_sec_man_key_t * The key to be imported.
* @returns The success or failure code of the operation.
*/
async ezspImportKey(context, key) {
/**
* Import a key into storage. Certain keys are
* indexed, while others are not, as described here.
*
* If context->core_key_type is..
*
* ..SL_ZB_SEC_MAN_KEY_TYPE_NETWORK, then context->key_index dictates whether to
* import the current (active) network key (index 0) or the alternate network
* key (index 1).
*
* ..SL_ZB_SEC_MAN_KEY_TYPE_TC_LINK_WITH_TIMEOUT, then context->eui64 must be
* set. context->key_index is unused.
*
* ..SL_ZB_SEC_MAN_KEY_TYPE_APP_LINK, then context->key_index determines which
* index in the persisted key table that the entry should be stored to.
* context->eui64 must also be set.
* If context->key_index is 0xFF, a suitable key index will be found (either one
* storing an existing key with address of context->eui64, or an open entry),
* and context->key_index will be updated with where the entry was stored.
*
* ..SL_ZB_SEC_MAN_KEY_TYPE_GREEN_POWER_PROXY_TABLE_KEY or
* SL_ZB_SEC_MAN_KEY_TYPE_GREEN_POWER_SINK_TABLE_KEY, then context->key_index
* dictates which key entry to import. These Green Power keys are indexed keys,
* and there are EMBER_GP_PROXY_TABLE_SIZE/EMBER_GP_SINK_TABLE_SIZE many of them.
*
* For all other key types, both context->key_index and context->eui64 are not
* used.
*
* @param context sl_zb_sec_man_context_t* [IN] The context to set. The context dictates which key type
* to save, key_index (if applicable) into the relevant key storage, eui64 (if
* applicable), etc.
* @param plaintext_key sl_zb_sec_man_key_t* [IN] The key to import.
* @note The context->derived_type must be SL_ZB_SEC_MAN_DERIVED_KEY_TYPE_NONE,
* else, an error will be thrown. Key derivations, which are used in crypto
* operations, are performed using the ::sl_zb_sec_man_load_key_context routine.
* @return SLStatus.OK upon success, a valid error code otherwise.
*/
// NOTE: added for good measure
if (context.coreKeyType === enums_1.SecManKeyType.INTERNAL) {
logger_1.logger.error("ezspImportKey cannot use INTERNAL key type.", NS);
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_CALL);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.IMPORT_KEY);
sendBuffalo.writeSecManContext(context);
sendBuffalo.writeSecManKey(key);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
context = this.buffalo.readSecManContext();
return status;
}
/**
* Callback
* A callback to inform the application that the Network Key has been updated
* and the node has been switched over to use the new key. The actual key being
* used is not passed up, but the sequence number is.
* @param sequenceNumber uint8_t The sequence number of the new network key.
*/
ezspSwitchNetworkKeyHandler(sequenceNumber) {
logger_1.logger.debug(`ezspSwitchNetworkKeyHandler: sequenceNumber=${sequenceNumber}`, NS);
}
/**
* This function searches through the Key Table and tries to find the entry that
* matches the passed search criteria.
* @param address The address to search for. Alternatively, all zeros may be passed in to search for the first empty entry.
* @param linkKey This indicates whether to search for an entry that contains a link key or a master key.
* true means to search for an entry with a Link Key.
* @returns uint8_t This indicates the index of the entry that matches the search criteria.
* A value of 0xFF is returned if not matching entry is found.
*/
async ezspFindKeyTableEntry(address, linkKey) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.FIND_KEY_TABLE_ENTRY);
sendBuffalo.writeIeeeAddr(address);
sendBuffalo.writeUInt8(linkKey ? 1 : 0);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const index = this.buffalo.readUInt8();
return index;
}
/**
* This function sends an APS TransportKey command containing the current trust
* center link key. The node to which the command is sent is specified via the
* short and long address arguments.
* @param destinationNodeId The short address of the node to which this command will be sent
* @param destinationEui64 The long address of the node to which this command will be sent
* @returns An SLStatus value indicating success of failure of the operation
*/
async ezspSendTrustCenterLinkKey(destinationNodeId, destinationEui64) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SEND_TRUST_CENTER_LINK_KEY);
sendBuffalo.writeUInt16(destinationNodeId);
sendBuffalo.writeIeeeAddr(destinationEui64);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* This function erases the data in the key table entry at the specified index.
* If the index is invalid, false is returned.
* @param index uint8_t This indicates the index of entry to erase.
* @returns The success or failure of the operation.
*/
async ezspEraseKeyTableEntry(index) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ERASE_KEY_TABLE_ENTRY);
sendBuffalo.writeUInt8(index);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* This function clears the key table of the current network.
* @returns The success or failure of the operation.
*/
async ezspClearKeyTable() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CLEAR_KEY_TABLE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* A function to request a Link Key from the Trust Center with another device on
* the Network (which could be the Trust Center). A Link Key with the Trust
* Center is possible but the requesting device cannot be the Trust Center. Link
* Keys are optional in Zigbee Standard Security and thus the stack cannot know
* whether the other device supports them. If EMBER_REQUEST_KEY_TIMEOUT is
* non-zero on the Trust Center and the partner device is not the Trust Center,
* both devices must request keys with their partner device within the time
* period. The Trust Center only supports one outstanding key request at a time
* and therefore will ignore other requests. If the timeout is zero then the
* Trust Center will immediately respond and not wait for the second request.
* The Trust Center will always immediately respond to requests for a Link Key
* with it. Sleepy devices should poll at a higher rate until a response is
* received or the request times out. The success or failure of the request is
* returned via ezspZigbeeKeyEstablishmentHandler(...)
* @param partner This is the IEEE address of the partner device that will share the link key.
* @returns The success or failure of sending the request.
* This is not the final result of the attempt. ezspZigbeeKeyEstablishmentHandler(...) will return that.
*/
async ezspRequestLinkKey(partner) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.REQUEST_LINK_KEY);
sendBuffalo.writeIeeeAddr(partner);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Requests a new link key from the Trust Center. This function starts by
* sending a Node Descriptor request to the Trust Center to verify its R21+
* stack version compliance. A Request Key message will then be sent, followed
* by a Verify Key Confirm message.
* @param maxAttempts uint8_t The maximum number of attempts a node should make when sending the Node Descriptor,
* Request Key, and Verify Key Confirm messages. The number of attempts resets for each message type sent
* (e.g., if maxAttempts is 3, up to 3 Node Descriptors are sent, up to 3 Request Keys, and up to 3 Verify Key Confirm messages are sent).
* @returns The success or failure of sending the request.
* If the Node Descriptor is successfully transmitted, ezspZigbeeKeyEstablishmentHandler(...)
* will be called at a later time with a final status result.
*/
async ezspUpdateTcLinkKey(maxAttempts) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.UPDATE_TC_LINK_KEY);
sendBuffalo.writeUInt8(maxAttempts);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Callback
* This is a callback that indicates the success or failure of an attempt to establish a key with a partner device.
* @param partner This is the IEEE address of the partner that the device successfully established a key with.
* This value is all zeros on a failure.
* @param status This is the status indicating what was established or why the key establishment failed.
*/
ezspZigbeeKeyEstablishmentHandler(partner, status) {
logger_1.logger.debug(`ezspZigbeeKeyEstablishmentHandler: partner=${partner} status=${enums_1.EmberKeyStatus[status]}`, NS);
// NOTE: For security reasons, any valid `partner` (not wildcard) that return with a status=TC_REQUESTER_VERIFY_KEY_TIMEOUT
// are kicked off the network for posing a risk, unless HA devices allowed (as opposed to Z3)
// and always if status=TC_REQUESTER_VERIFY_KEY_FAILURE
}
/**
* Clear all of the transient link keys from RAM.
*/
async ezspClearTransientLinkKeys() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CLEAR_TRANSIENT_LINK_KEYS);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Retrieve information about the current and alternate network key, excluding their contents.
* @returns Success or failure of retrieving network key info.
* @returns sl_zb_sec_man_network_key_info_t * Information about current and alternate network keys.
*/
async ezspGetNetworkKeyInfo() {
/**
* Retrieve information about the network key and alternate network key.
* It will not retrieve the actual network key contents.
*
* @param network_key_info sl_zb_sec_man_network_key_info_t* [OUT] The network key info struct used to store network key metadata,
* containing information about whether the current and next network keys are set, and the
* sequence numbers associated with each key.
*
* @return SLStatus SLStatus.OK
*
*/
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_NETWORK_KEY_INFO);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
const networkKeyInfo = this.buffalo.readSecManNetworkKeyInfo();
return [status, networkKeyInfo];
}
/**
* Retrieve metadata about an APS link key. Does not retrieve contents.
* @param context sl_zb_sec_man_context_t * Context used to input information about key.
* @returns Status of metadata retrieval operation.
* @returns sl_zb_sec_man_aps_key_metadata_t * Metadata about the referenced key.
*/
async ezspGetApsKeyInfo(context) {
/**
* Retrieve metadata about an APS key.
* It does not retrieve the actual key contents.
*
* @param context sl_zb_sec_man_context_t* [IN/OUT] The context to use to look up a key entry. If the
* user calls this function with the ::ZB_SEC_MAN_FLAG_KEY_INDEX_IS_VALID bit
* set in the context->flag field, then the key_index field in the context
* argument dictates which entry to retrieve. For keys with timeout and
* application link keys, the key_index retrieves the indexed entry into the
* respective table. Upon success, the eui64 field in the context is updated.
* If the user calls this function with the
* ::ZB_SEC_MAN_FLAG_EUI_IS_VALID bit set in the
* context->flag field, then the eui64 field in the context argument
* dictates which entry to retrieve. If the context->core_key_type argument is
* set to SL_ZB_SEC_MAN_KEY_TYPE_NETWORK, an error is returned as network keys
* are not tied to any specific EUI.
* If neither the ::ZB_SEC_MAN_FLAG_KEY_INDEX_IS_VALID bit nor the
* ::ZB_SEC_MAN_FLAG_EUI_IS_VALID bit is set in context->flags, then an error
* will be returned by this function.
* Upon success in fetching a key, the other fields in this argument are
* updated (e.g. a successful search by key_index will update the euii64
* field).
*
* @returns keyData sl_zb_sec_man_aps_key_metadata_t* [OUT] Metadata to fill in.
*
* @returns SLStatus.OK if successful, SLStatus.NOT_FOUND if
* the key_index or eui64 does not result in a found entry,
* SLStatus.INVALID_TYPE if the core key type is not an APS layer key (e.g.
* SL_ZB_SEC_MAN_KEY_TYPE_NETWORK), or SLStatus.INVALID_MODE if core_key_type
* is SL_ZB_SEC_MAN_KEY_TYPE_TC_LINK and the initial security state does not
* indicate the a preconfigured key has been set (that is, both
* EMBER_HAVE_PRECONFIGURED_KEY and
* EMBER_GET_PRECONFIGURED_KEY_FROM_INSTALL_CODE have not been set in the
* initial security state).
*/
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_APS_KEY_INFO);
sendBuffalo.writeSecManContext(context);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
if (this.version < 0x0e) {
context.eui64 = this.buffalo.readIeeeAddr();
const keyData = this.buffalo.readSecManAPSKeyMetadata();
const status = this.buffalo.readUInt32();
return [status, keyData];
}
const status = this.buffalo.readUInt32();
const keyData = this.buffalo.readSecManAPSKeyMetadata();
context = this.buffalo.readSecManContext();
return [status, keyData];
}
/**
* Import an application link key into the key table.
* @param index uint8_t Index where this key is to be imported to.
* @param address EUI64 this key is associated with.
* @param plaintextKey sl_zb_sec_man_key_t * The key data to be imported.
* @returns Status of key import operation.
*/
async ezspImportLinkKey(index, address, plaintextKey) {
/**
* Import a link key, or SL_ZB_SEC_MAN_KEY_TYPE_APP_LINK key, into storage.
*
* @param index uint8_t [IN] The index to set or overwrite in the key table for keys of
* type SL_ZB_SEC_MAN_KEY_TYPE_APP_LINK. If index is set to 0xFF (255), then
* the key will either overwrite whichever key table entry has an EUI of address
* (if one exists) or write to the first available key table entry. The index
* that the key was placed into will not be returned by this API.
* @param address EUI64 [IN] The EUI belonging to the key.
* @param plaintext_key sl_zb_sec_man_key_t* [IN] A pointer to the key to import.
*
* @return SLStatus.OK upon success, a valid error code otherwise.
*
*/
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.IMPORT_LINK_KEY);
sendBuffalo.writeUInt8(index);
sendBuffalo.writeIeeeAddr(address);
sendBuffalo.writeSecManKey(plaintextKey);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Export the link key at given index from the key table.
* @param uint8_t Index of key to export.
* @returns Status of key export operation.
* @returns sl_zigbee_sec_man_context_t * Context referencing the exported key. Contains information like the EUI64 address it is associated with.
* @returns sl_zb_sec_man_key_t * The exported key.
* @returns sl_zb_sec_man_aps_key_metadata_t * Metadata about the key.
*/
async ezspExportLinkKeyByIndex(index) {
/**
* Export an APS link key by index.
*
* @param index uint8_t
* @param address EUI64
* @param plaintext_key sl_zb_sec_man_key_t*
* @param key_data sl_zb_sec_man_aps_key_metadata_t*
*/
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.EXPORT_LINK_KEY_BY_INDEX);
sendBuffalo.writeUInt8(index);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
if (this.version < 0x0e) {
const context = (0, initters_1.initSecurityManagerContext)();
context.coreKeyType = enums_1.SecManKeyType.APP_LINK;
context.keyIndex = index;
context.eui64 = this.buffalo.readIeeeAddr();
const plaintextKey = this.buffalo.readSecManKey();
const keyData = this.buffalo.readSecManAPSKeyMetadata();
const status = this.buffalo.readUInt32();
return [status, context, plaintextKey, keyData];
}
const status = this.buffalo.readUInt32();
const context = this.buffalo.readSecManContext();
const plaintextKey = this.buffalo.readSecManKey();
const keyData = this.buffalo.readSecManAPSKeyMetadata();
return [status, context, plaintextKey, keyData];
}
/**
* Export the link key associated with the given EUI from the key table.
* @param eui EUI64 associated with the key to export.
* @returns Status of key export operation.
* @returns sl_zb_sec_man_key_t * The exported key.
* @returns sl_zigbee_sec_man_context_t * Context referencing the exported key. Contains information like the EUI64 address it is associated with.
* @returns sl_zb_sec_man_aps_key_metadata_t * Metadata about the key.
*/
async ezspExportLinkKeyByEui(eui) {
/**
* Search through the Key table to find an entry that has the same EUI address as the passed value.
* If NULL is passed in for the address then it finds the first unused entry and sets the index in the context.
* It is valid to pass in NULL to plaintext_key or key_data in case the index of the referenced key is desired
* but not its value or other metadata.
* @param eui EUI64
* @param context sl_zb_sec_man_context_t*
* @param plaintext_key sl_zb_sec_man_key_t*
* @param key_data sl_zb_sec_man_aps_key_metadata_t*
*/
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.EXPORT_LINK_KEY_BY_EUI);
sendBuffalo.writeIeeeAddr(eui);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
if (this.version < 0x0e) {
const plaintextKey = this.buffalo.readSecManKey();
const context = (0, initters_1.initSecurityManagerContext)();
context.coreKeyType = enums_1.SecManKeyType.APP_LINK;
context.keyIndex = this.buffalo.readUInt8();
context.eui64 = eui;
const keyData = this.buffalo.readSecManAPSKeyMetadata();
const status = this.buffalo.readUInt32();
return [status, context, plaintextKey, keyData];
}
const status = this.buffalo.readUInt32();
const context = this.buffalo.readSecManContext();
const plaintextKey = this.buffalo.readSecManKey();
const keyData = this.buffalo.readSecManAPSKeyMetadata();
return [status, context, plaintextKey, keyData];
}
/**
* Check whether a key context can be used to load a valid key.
* @param context sl_zb_sec_man_context_t * Context struct to check the validity of.
* @returns Validity of the checked context.
*/
async ezspCheckKeyContext(context) {
/**
* Check that the passed key exists and can be successfully loaded.
* This function does not actually load the context, but only checks that it can be loaded.
*
* @param context sl_zb_sec_man_context_t* [IN] The context to check for validity. The fields that must be set depend
* on the key type set in the context, as enough information is needed to identify the key.
*
* @return SLStatus SLStatus.OK upon success, SLStatus.NOT_FOUND otherwise.
*/
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CHECK_KEY_CONTEXT);
sendBuffalo.writeSecManContext(context);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Import a transient link key.
* @param eui64 EUI64 associated with this transient key.
* @param plaintextKey sl_zb_sec_man_key_t * The key to import.
* @param flags sl_zigbee_sec_man_flags_t (unused in v14+) Flags associated with this transient key.
* @returns Status of key import operation.
*/
async ezspImportTransientKey(eui64, plaintextKey, flags = enums_1.SecManFlag.NONE) {
/**
* @brief Add a transient or temporary key entry to key storage.
* A key entry added with this API is timed out after
* ::EMBER_TRANSIENT_KEY_TIMEOUT_S seconds, unless the key entry was added using
* the Network Creator Security component, in which case the key will time out
* after the longer between
* ::EMBER_AF_PLUGIN_NETWORK_CREATOR_SECURITY_NETWORK_OPEN_TIME_S seconds and
* ::EMBER_TRANSIENT_KEY_TIMEOUT_S seconds.
*/
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.IMPORT_TRANSIENT_KEY);
sendBuffalo.writeIeeeAddr(eui64);
sendBuffalo.writeSecManKey(plaintextKey);
if (this.version < 0x0e) {
sendBuffalo.writeUInt8(flags);
}
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* Export a transient link key from a given table index.
* @param uint8_t Index to export from.
* @returns Status of key export operation.
* @returns sl_zb_sec_man_context_t * Context struct for export operation.
* @returns sl_zb_sec_man_key_t * The exported key.
* @returns sl_zb_sec_man_aps_key_metadata_t * Metadata about the key.
*/
async ezspExportTransientKeyByIndex(index) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.EXPORT_TRANSIENT_KEY_BY_INDEX);
sendBuffalo.writeUInt8(index);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
if (this.version < 0x0e) {
const context = this.buffalo.readSecManContext();
const plaintextKey = this.buffalo.readSecManKey();
const keyData = this.buffalo.readSecManAPSKeyMetadata();
const status = this.buffalo.readUInt32();
return [status, context, plaintextKey, keyData];
}
const status = this.buffalo.readUInt32();
const context = this.buffalo.readSecManContext();
const plaintextKey = this.buffalo.readSecManKey();
const keyData = this.buffalo.readSecManAPSKeyMetadata();
return [status, context, plaintextKey, keyData];
}
/**
* Export a transient link key associated with a given EUI64
* @param eui Index to export from.
* @returns Status of key export operation.
* @returns sl_zb_sec_man_context_t * Context struct for export operation.
* @returns sl_zb_sec_man_key_t * The exported key.
* @returns sl_zb_sec_man_aps_key_metadata_t * Metadata about the key.
*/
async ezspExportTransientKeyByEui(eui) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.EXPORT_TRANSIENT_KEY_BY_EUI);
sendBuffalo.writeIeeeAddr(eui);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
if (this.version < 0x0e) {
const context = this.buffalo.readSecManContext();
const plaintextKey = this.buffalo.readSecManKey();
const keyData = this.buffalo.readSecManAPSKeyMetadata();
const status = this.buffalo.readUInt32();
return [status, context, plaintextKey, keyData];
}
const status = this.buffalo.readUInt32();
const context = this.buffalo.readSecManContext();
const plaintextKey = this.buffalo.readSecManKey();
const keyData = this.buffalo.readSecManAPSKeyMetadata();
return [status, context, plaintextKey, keyData];
}
/**
* Set the incoming TC link key frame counter to desired value.
* @param frameCounter Value to set the frame counter to.
*/
async ezspSetIncomingTcLinkKeyFrameCounter(frameCounter) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_INCOMING_TC_LINK_KEY_FRAME_COUNTER);
sendBuffalo.writeUInt32(frameCounter);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Encrypt/decrypt a message in-place using APS.
* @param encrypt Encrypt (true) or decrypt (false) the message.
* @param lengthCombinedArg uint8_t Length of the array containing message, needs to be long enough to include the auxiliary header and MIC.
* @param message uint8_t * The message to be en/de-crypted.
* @param apsHeaderEndIndex uint8_t Index just past the APS frame.
* @param remoteEui64 IEEE address of the device this message is associated with.
* @returns Status of the encryption/decryption call.
*/
async ezspApsCryptMessage(encrypt, lengthCombinedArg, message, apsHeaderEndIndex, remoteEui64) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.APS_CRYPT_MESSAGE);
sendBuffalo.writeUInt8(encrypt ? 1 : 0);
sendBuffalo.writeUInt8(lengthCombinedArg);
sendBuffalo.writeBuffer(message, lengthCombinedArg);
sendBuffalo.writeUInt8(apsHeaderEndIndex);
sendBuffalo.writeIeeeAddr(remoteEui64);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
const cryptedMessage = this.buffalo.readBuffer(lengthCombinedArg);
return [status, cryptedMessage];
}
//-----------------------------------------------------------------------------
// Trust Center Frames
//-----------------------------------------------------------------------------
/**
* Callback
* The NCP used the trust center behavior policy to decide whether to allow a
* new node to join the network. The Host cannot change the current decision,
* but it can change the policy for future decisions using the setPolicy
* command.
* @param newNodeId The Node Id of the node whose status changed
* @param newNodeEui64 The EUI64 of the node whose status changed.
* @param status The status of the node: Secure Join/Rejoin, Unsecure Join/Rejoin, Device left.
* @param policyDecision An EmberJoinDecision reflecting the decision made.
* @param parentOfNewNodeId The parent of the node whose status has changed.
*/
ezspTrustCenterJoinHandler(newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId) {
logger_1.logger.debug(`ezspTrustCenterJoinHandler: newNodeId=${newNodeId} newNodeEui64=${newNodeEui64} ` +
`status=${enums_1.EmberDeviceUpdate[status]} policyDecision=${enums_1.EmberJoinDecision[policyDecision]} parentOfNewNodeId=${parentOfNewNodeId}`, NS);
// NOTE: this is mostly just passing stuff up to Z2M, so use only one emit for all, let adapter do the rest, no parsing needed
this.emit("trustCenterJoin", newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId);
}
/**
* This function broadcasts a new encryption key, but does not tell the nodes in
* the network to start using it. To tell nodes to switch to the new key, use
* ezspBroadcastNetworkKeySwitch(). This is only valid for the Trust
* Center/Coordinator. It is up to the application to determine how quickly to
* send the Switch Key after sending the alternate encryption key.
* @param key EmberKeyData * An optional pointer to a 16-byte encryption key (EMBER_ENCRYPTION_KEY_SIZE).
* An all zero key may be passed in, which will cause the stack to randomly generate a new key.
* @returns SLStatus value that indicates the success or failure of the command.
*/
async ezspBroadcastNextNetworkKey(key) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.BROADCAST_NEXT_NETWORK_KEY);
sendBuffalo.writeEmberKeyData(key);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* This function broadcasts a switch key message to tell all nodes to change to
* the sequence number of the previously sent Alternate Encryption Key.
* @returns SLStatus value that indicates the success or failure of the command.
*/
async ezspBroadcastNetworkKeySwitch() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.BROADCAST_NETWORK_KEY_SWITCH);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* This routine processes the passed chunk of data and updates the hash context
* based on it. If the 'finalize' parameter is not set, then the length of the
* data passed in must be a multiple of 16. If the 'finalize' parameter is set
* then the length can be any value up 1-16, and the final hash value will be
* calculated.
* @param context EmberAesMmoHashContext * The hash context to update.
* @param finalize This indicates whether the final hash value should be calculated
* @param data uint8_t * The data to hash.
* @returns The result of the operation
* @returns EmberAesMmoHashContext * The updated hash context.
*/
async ezspAesMmoHash(context, finalize, data) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.AES_MMO_HASH);
sendBuffalo.writeEmberAesMmoHashContext(context);
sendBuffalo.writeUInt8(finalize ? 1 : 0);
sendBuffalo.writePayload(data);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const returnContext = this.buffalo.readEmberAesMmoHashContext();
return [status, returnContext];
}
/**
* This command sends an APS remove device using APS encryption to the
* destination indicating either to remove itself from the network, or one of
* its children.
* @param destShort The node ID of the device that will receive the message
* @param destLong The long address (EUI64) of the device that will receive the message.
* @param targetLong The long address (EUI64) of the device to be removed.
* @returns An SLStatus value indicating success, or the reason for failure
*/
async ezspRemoveDevice(destShort, destLong, targetLong) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.REMOVE_DEVICE);
sendBuffalo.writeUInt16(destShort);
sendBuffalo.writeIeeeAddr(destLong);
sendBuffalo.writeIeeeAddr(targetLong);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* This command will send a unicast transport key message with a new NWK key to
* the specified device. APS encryption using the device's existing link key
* will be used.
* @param destShort The node ID of the device that will receive the message
* @param destLong The long address (EUI64) of the device that will receive the message.
* @param key EmberKeyData * The NWK key to send to the new device.
* @returns An SLStatus value indicating success, or the reason for failure
*/
async ezspUnicastNwkKeyUpdate(destShort, destLong, key) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.UNICAST_NWK_KEY_UPDATE);
sendBuffalo.writeUInt16(destShort);
sendBuffalo.writeIeeeAddr(destLong);
sendBuffalo.writeEmberKeyData(key);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
//-----------------------------------------------------------------------------
// Certificate Based Key Exchange (CBKE) Frames
//-----------------------------------------------------------------------------
/**
* This call starts the generation of the ECC Ephemeral Public/Private key pair.
* When complete it stores the private key. The results are returned via
* ezspGenerateCbkeKeysHandler().
*/
async ezspGenerateCbkeKeys() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GENERATE_CBKE_KEYS);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Callback
* A callback by the Crypto Engine indicating that a new ephemeral
* public/private key pair has been generated. The public/private key pair is
* stored on the NCP, but only the associated public key is returned to the
* host. The node's associated certificate is also returned.
* @param status The result of the CBKE operation.
* @param ephemeralPublicKey EmberPublicKeyData * The generated ephemeral public key.
*/
ezspGenerateCbkeKeysHandler(status, ephemeralPublicKey) {
logger_1.logger.debug(`ezspGenerateCbkeKeysHandler: status=${enums_1.SLStatus[status]} ephemeralPublicKey=${ephemeralPublicKey}`, NS);
}
/**
* Calculates the SMAC verification keys for both the initiator and responder
* roles of CBKE using the passed parameters and the stored public/private key
* pair previously generated with ezspGenerateKeysRetrieveCert(). It also stores
* the unverified link key data in temporary storage on the NCP until the key
* establishment is complete.
* @param amInitiator The role of this device in the Key Establishment protocol.
* @param partnerCertificate EmberCertificateData * The key establishment partner's implicit certificate.
* @param partnerEphemeralPublicKey EmberPublicKeyData * The key establishment partner's ephemeral public key
*/
async ezspCalculateSmacs(amInitiator, partnerCertificate, partnerEphemeralPublicKey) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CALCULATE_SMACS);
sendBuffalo.writeUInt8(amInitiator ? 1 : 0);
sendBuffalo.writeEmberCertificateData(partnerCertificate);
sendBuffalo.writeEmberPublicKeyData(partnerEphemeralPublicKey);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Callback
* A callback to indicate that the NCP has finished calculating the Secure
* Message Authentication Codes (SMAC) for both the initiator and responder. The
* associated link key is kept in temporary storage until the host tells the NCP
* to store or discard the key via emberClearTemporaryDataMaybeStoreLinkKey().
* @param status The Result of the CBKE operation.
* @param initiatorSmac EmberSmacData * The calculated value of the initiator's SMAC
* @param responderSmac EmberSmacData * The calculated value of the responder's SMAC
*/
ezspCalculateSmacsHandler(status, initiatorSmac, responderSmac) {
logger_1.logger.debug(`ezspCalculateSmacsHandler: status=${enums_1.SLStatus[status]} initiatorSmac=${initiatorSmac} responderSmac=${responderSmac}`, NS);
}
/**
* This call starts the generation of the ECC 283k1 curve Ephemeral
* Public/Private key pair. When complete it stores the private key. The results
* are returned via ezspGenerateCbkeKeysHandler283k1().
*/
async ezspGenerateCbkeKeys283k1() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GENERATE_CBKE_KEYS283K1);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Callback
* A callback by the Crypto Engine indicating that a new 283k1 ephemeral
* public/private key pair has been generated. The public/private key pair is
* stored on the NCP, but only the associated public key is returned to the
* host. The node's associated certificate is also returned.
* @param status The result of the CBKE operation.
* @param ephemeralPublicKey EmberPublicKey283k1Data * The generated ephemeral public key.
*/
ezspGenerateCbkeKeysHandler283k1(status, ephemeralPublicKey) {
logger_1.logger.debug(`ezspGenerateCbkeKeysHandler283k1(): status=${enums_1.SLStatus[status]} ephemeralPublicKey=${ephemeralPublicKey}`, NS);
}
/**
* Calculates the SMAC verification keys for both the initiator and responder
* roles of CBKE for the 283k1 ECC curve using the passed parameters and the
* stored public/private key pair previously generated with
* ezspGenerateKeysRetrieveCert283k1(). It also stores the unverified link key
* data in temporary storage on the NCP until the key establishment is complete.
* @param amInitiator The role of this device in the Key Establishment protocol.
* @param partnerCertificate EmberCertificate283k1Data * The key establishment partner's implicit certificate.
* @param partnerEphemeralPublicKey EmberPublicKey283k1Data * The key establishment partner's ephemeral public key
*/
async ezspCalculateSmacs283k1(amInitiator, partnerCertificate, partnerEphemeralPublicKey) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CALCULATE_SMACS283K1);
sendBuffalo.writeUInt8(amInitiator ? 1 : 0);
sendBuffalo.writeEmberCertificate283k1Data(partnerCertificate);
sendBuffalo.writeEmberPublicKey283k1Data(partnerEphemeralPublicKey);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Callback
* A callback to indicate that the NCP has finished calculating the Secure
* Message Authentication Codes (SMAC) for both the initiator and responder for
* the CBKE 283k1 Library. The associated link key is kept in temporary storage
* until the host tells the NCP to store or discard the key via
* emberClearTemporaryDataMaybeStoreLinkKey().
* @param status The Result of the CBKE operation.
* @param initiatorSmac EmberSmacData * The calculated value of the initiator's SMAC
* @param responderSmac EmberSmacData * The calculated value of the responder's SMAC
*/
ezspCalculateSmacsHandler283k1(status, initiatorSmac, responderSmac) {
logger_1.logger.debug(`ezspCalculateSmacsHandler283k1(): status=${enums_1.SLStatus[status]} initiatorSmac=${initiatorSmac} responderSmac=${responderSmac}`, NS);
}
/**
* Clears the temporary data associated with CBKE and the key establishment,
* most notably the ephemeral public/private key pair. If storeLinKey is true it
* moves the unverified link key stored in temporary storage into the link key
* table. Otherwise it discards the key.
* @param storeLinkKey A bool indicating whether to store (true) or discard (false) the unverified link
* key derived when ezspCalculateSmacs() was previously called.
*/
async ezspClearTemporaryDataMaybeStoreLinkKey(storeLinkKey) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CLEAR_TEMPORARY_DATA_MAYBE_STORE_LINK_KEY);
sendBuffalo.writeUInt8(storeLinkKey ? 1 : 0);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Clears the temporary data associated with CBKE and the key establishment,
* most notably the ephemeral public/private key pair. If storeLinKey is true it
* moves the unverified link key stored in temporary storage into the link key
* table. Otherwise it discards the key.
* @param storeLinkKey A bool indicating whether to store (true) or discard (false) the unverified link
* key derived when ezspCalculateSmacs() was previously called.
*/
async ezspClearTemporaryDataMaybeStoreLinkKey283k1(storeLinkKey) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.CLEAR_TEMPORARY_DATA_MAYBE_STORE_LINK_KEY283K1);
sendBuffalo.writeUInt8(storeLinkKey ? 1 : 0);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Retrieves the certificate installed on the NCP.
* @returns EmberCertificateData * The locally installed certificate.
*/
async ezspGetCertificate() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_CERTIFICATE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const localCert = this.buffalo.readEmberCertificateData();
return [status, localCert];
}
/**
* Retrieves the 283k certificate installed on the NCP.
* @returns EmberCertificate283k1Data * The locally installed certificate.
*/
async ezspGetCertificate283k1() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_CERTIFICATE283K1);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const localCert = this.buffalo.readEmberCertificate283k1Data();
return [status, localCert];
}
/**
* Callback
* The handler that returns the results of the signing operation. On success,
* the signature will be appended to the original message (including the
* signature type indicator that replaced the startIndex field for the signing)
* and both are returned via this callback.
* @param status The result of the DSA signing operation.
* @param messageLength uint8_t The length of the messageContents parameter in bytes.
* @param messageContents uint8_t *The message and attached which includes the original message and the appended signature.
*/
ezspDsaSignHandler(status, messageContents) {
logger_1.logger.debug(`ezspDsaSignHandler: status=${enums_1.SLStatus[status]} messageContents=${messageContents.toString("hex")}`, NS);
}
/**
* Verify that signature of the associated message digest was signed by the
* private key of the associated certificate.
* @param digest EmberMessageDigest * The AES-MMO message digest of the signed data.
* If dsaSign command was used to generate the signature for this data, the final byte (replaced by signature type of 0x01)
* in the messageContents array passed to dsaSign is included in the hash context used for the digest calculation.
* @param signerCertificate EmberCertificateData * The certificate of the signer. Note that the signer's certificate and the verifier's
* certificate must both be issued by the same Certificate Authority, so they should share the same CA Public Key.
* @param receivedSig EmberSignatureData * The signature of the signed data.
*/
async ezspDsaVerify(digest, signerCertificate, receivedSig) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.DSA_VERIFY);
sendBuffalo.writeEmberMessageDigest(digest);
sendBuffalo.writeEmberCertificateData(signerCertificate);
sendBuffalo.writeEmberSignatureData(receivedSig);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Callback
* This callback is executed by the stack when the DSA verification has
* completed and has a result. If the result is EMBER_SUCCESS, the signature is
* valid. If the result is EMBER_SIGNATURE_VERIFY_FAILURE then the signature is
* invalid. If the result is anything else then the signature verify operation
* failed and the validity is unknown.
* @param status The result of the DSA verification operation.
*/
ezspDsaVerifyHandler(status) {
logger_1.logger.debug(`ezspDsaVerifyHandler: status=${enums_1.SLStatus[status]}`, NS);
}
/**
* Verify that signature of the associated message digest was signed by the
* private key of the associated certificate.
* @param digest EmberMessageDigest * The AES-MMO message digest of the signed data.
* If dsaSign command was used to generate the signature for this data, the final byte (replaced by signature type of 0x01)
* in the messageContents array passed to dsaSign is included in the hash context used for the digest calculation.
* @param signerCertificate EmberCertificate283k1Data * The certificate of the signer. Note that the signer's certificate and the verifier's
* certificate must both be issued by the same Certificate Authority, so they should share the same CA Public Key.
* @param receivedSig EmberSignature283k1Data * The signature of the signed data.
*/
async ezspDsaVerify283k1(digest, signerCertificate, receivedSig) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.DSA_VERIFY283K1);
sendBuffalo.writeEmberMessageDigest(digest);
sendBuffalo.writeEmberCertificate283k1Data(signerCertificate);
sendBuffalo.writeEmberSignature283k1Data(receivedSig);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Sets the device's CA public key, local certificate, and static private key on
* the NCP associated with this node.
* @param caPublic EmberPublicKeyData * The Certificate Authority's public key.
* @param myCert EmberCertificateData * The node's new certificate signed by the CA.
* @param myKey EmberPrivateKeyData *The node's new static private key.
*/
async ezspSetPreinstalledCbkeData(caPublic, myCert, myKey) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_PREINSTALLED_CBKE_DATA);
sendBuffalo.writeEmberPublicKeyData(caPublic);
sendBuffalo.writeEmberCertificateData(myCert);
sendBuffalo.writeEmberPrivateKeyData(myKey);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Sets the device's 283k1 curve CA public key, local certificate, and static
* private key on the NCP associated with this node.
* @returns Status of operation
*/
async ezspSavePreinstalledCbkeData283k1() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SAVE_PREINSTALLED_CBKE_DATA283K1);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
//-----------------------------------------------------------------------------
// Mfglib Frames
//-----------------------------------------------------------------------------
/**
* Activate use of mfglib test routines and enables the radio receiver to report
* packets it receives to the mfgLibRxHandler() callback. These packets will not
* be passed up with a CRC failure. All other mfglib functions will return an
* error until the mfglibStart() has been called
* @param rxCallback true to generate a mfglibRxHandler callback when a packet is received.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async mfglibInternalStart(rxCallback) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFGLIB_INTERNAL_START);
sendBuffalo.writeUInt8(rxCallback ? 1 : 0);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Deactivate use of mfglib test routines; restores the hardware to the state it
* was in prior to mfglibStart() and stops receiving packets started by
* mfglibStart() at the same time.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async mfglibInternalEnd() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFGLIB_INTERNAL_END);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Starts transmitting an unmodulated tone on the currently set channel and
* power level. Upon successful return, the tone will be transmitting. To stop
* transmitting tone, application must call mfglibStopTone(), allowing it the
* flexibility to determine its own criteria for tone duration (time, event,
* etc.)
* @returns An SLStatus value indicating success or the reason for failure.
*/
async mfglibInternalStartTone() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFGLIB_INTERNAL_START_TONE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Stops transmitting tone started by mfglibStartTone().
* @returns An SLStatus value indicating success or the reason for failure.
*/
async mfglibInternalStopTone() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFGLIB_INTERNAL_STOP_TONE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Starts transmitting a random stream of characters. This is so that the radio
* modulation can be measured.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async mfglibInternalStartStream() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFGLIB_INTERNAL_START_STREAM);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Stops transmitting a random stream of characters started by
* mfglibStartStream().
* @returns An SLStatus value indicating success or the reason for failure.
*/
async mfglibInternalStopStream() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFGLIB_INTERNAL_STOP_STREAM);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Sends a single packet consisting of the following bytes: packetLength,
* packetContents[0], ... , packetContents[packetLength - 3], CRC[0], CRC[1].
* The total number of bytes sent is packetLength + 1. The radio replaces the
* last two bytes of packetContents[] with the 16-bit CRC for the packet.
* @param packetLength uint8_t The length of the packetContents parameter in bytes. Must be greater than 3 and less than 123.
* @param packetContents uint8_t * The packet to send. The last two bytes will be replaced with the 16-bit CRC.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async mfglibInternalSendPacket(packetContents) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFGLIB_INTERNAL_SEND_PACKET);
sendBuffalo.writePayload(packetContents);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Sets the radio channel. Calibration occurs if this is the first time the
* channel has been used.
* @param channel uint8_t The channel to switch to. Valid values are 11 - 26.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async mfglibInternalSetChannel(channel) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFGLIB_INTERNAL_SET_CHANNEL);
sendBuffalo.writeUInt8(channel);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Returns the current radio channel, as previously set via mfglibSetChannel().
* @returns uint8_t The current channel.
*/
async mfglibInternalGetChannel() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFGLIB_INTERNAL_GET_CHANNEL);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const channel = this.buffalo.readUInt8();
return channel;
}
/**
* First select the transmit power mode, and then include a method for selecting
* the radio transmit power. The valid power settings depend upon the specific
* radio in use. Ember radios have discrete power settings, and then requested
* power is rounded to a valid power setting; the actual power output is
* available to the caller via mfglibGetPower().
* @param txPowerMode uint16_t Power mode. Refer to txPowerModes in stack/include/ember-types.h for possible values.
* @param power int8_t Power in units of dBm. Refer to radio data sheet for valid range.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async mfglibInternalSetPower(txPowerMode, power) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFGLIB_INTERNAL_SET_POWER);
sendBuffalo.writeUInt16(txPowerMode);
sendBuffalo.writeInt8(power);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Returns the current radio power setting, as previously set via mfglibSetPower().
* @returns int8_t Power in units of dBm. Refer to radio data sheet for valid range.
*/
async mfglibInternalGetPower() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFGLIB_INTERNAL_GET_POWER);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const power = this.buffalo.readInt8();
return power;
}
/**
* Callback
* A callback indicating a packet with a valid CRC has been received.
* @param linkQuality uint8_t The link quality observed during the reception
* @param rssi int8_t The energy level (in units of dBm) observed during the reception.
* @param packetContents uint8_t * The received packet (last 2 bytes are not FCS / CRC and may be discarded)
* Length will be greater than 3 and less than 123.
*/
ezspMfglibRxHandler(linkQuality, rssi, packetContents) {
logger_1.logger.debug(`ezspMfglibRxHandler: linkQuality=${linkQuality} rssi=${rssi} packetContents=${packetContents.toString("hex")}`, NS);
}
//-----------------------------------------------------------------------------
// Bootloader Frames
//-----------------------------------------------------------------------------
/**
* Quits the current application and launches the standalone bootloader (if installed).
* The function returns an error if the standalone bootloader is not present.
* @param enabled If true, launch the standalone bootloader. If false, do nothing.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspLaunchStandaloneBootloader(enabled) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.LAUNCH_STANDALONE_BOOTLOADER);
sendBuffalo.writeUInt8(enabled ? 1 : 0);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
// XXX: SDK says this is SLStatus, but received frame has just 1 byte, so, still EmberStatus
const status = this.buffalo.readStatus(0);
return status;
}
/**
* Transmits the given bootload message to a neighboring node using a specific
* 802.15.4 header that allows the EmberZNet stack as well as the bootloader to
* recognize the message, but will not interfere with other Zigbee stacks.
* @param broadcast If true, the destination address and pan id are both set to the broadcast address.
* @param destEui64 The EUI64 of the target node. Ignored if the broadcast field is set to true.
* @param messageLength uint8_t The length of the messageContents parameter in bytes.
* @param messageContents uint8_t * The multicast message.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspSendBootloadMessage(broadcast, destEui64, messageContents) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SEND_BOOTLOAD_MESSAGE);
sendBuffalo.writeUInt8(broadcast ? 1 : 0);
sendBuffalo.writeIeeeAddr(destEui64);
sendBuffalo.writePayload(messageContents);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Detects if the standalone bootloader is installed, and if so returns the
* installed version. If not return 0xffff. A returned version of 0x1234 would
* indicate version 1.2 build 34. Also return the node's version of PLAT, MICRO
* and PHY.
* @returns uint16_t BOOTLOADER_INVALID_VERSION if the standalone bootloader is not present,
* or the version of the installed standalone bootloader.
* @returns uint8_t * The value of PLAT on the node
* @returns uint8_t * The value of MICRO on the node
* @returns uint8_t * The value of PHY on the node
*/
async ezspGetStandaloneBootloaderVersionPlatMicroPhy() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_STANDALONE_BOOTLOADER_VERSION_PLAT_MICRO_PHY);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const bootloaderVersion = this.buffalo.readUInt16();
const nodePlat = this.buffalo.readUInt8();
const nodeMicro = this.buffalo.readUInt8();
const nodePhy = this.buffalo.readUInt8();
return [bootloaderVersion, nodePlat, nodeMicro, nodePhy];
}
/**
* Callback
* A callback invoked by the EmberZNet stack when a bootload message is
* received.
* @param longId The EUI64 of the sending node.
* @param packetInfo Information about the incoming packet.
* @param messageContents uint8_t *The bootload message that was sent.
*/
ezspIncomingBootloadMessageHandler(longId, packetInfo, messageContents) {
logger_1.logger.debug(() => `ezspIncomingBootloadMessageHandler: longId=${longId} packetInfo=${JSON.stringify(packetInfo)} messageContents=${messageContents.toString("hex")}`, NS);
}
/**
* Callback
* A callback invoked by the EmberZNet stack when the MAC has finished
* transmitting a bootload message.
* @param status An EmberStatus value of SLStatus.OK if an ACK was received from the destination
* or SLStatus.ZIGBEE_DELIVERY_FAILED if no ACK was received.
* @param messageLength uint8_t The length of the messageContents parameter in bytes.
* @param messageContents uint8_t * The message that was sent.
*/
ezspBootloadTransmitCompleteHandler(status, messageContents) {
logger_1.logger.debug(`ezspBootloadTransmitCompleteHandler: status=${enums_1.SLStatus[status]} messageContents=${messageContents.toString("hex")}`, NS);
}
/**
* Perform AES encryption on plaintext using key.
* @param uint8_t * 16 bytes of plaintext.
* @param uint8_t * The 16-byte encryption key to use.
* @returns uint8_t * 16 bytes of ciphertext.
*/
async ezspAesEncrypt(plaintext, key) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.AES_ENCRYPT);
sendBuffalo.writeListUInt8(plaintext);
sendBuffalo.writeListUInt8(key);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const ciphertext = this.buffalo.readListUInt8(consts_2.EMBER_ENCRYPTION_KEY_SIZE);
return ciphertext;
}
/**
* Callback
* A callback to be implemented on the Golden Node to process acknowledgements.
* If you supply a custom version of this handler, you must define SL_ZIGBEE_APPLICATION_HAS_INCOMING_MFG_TEST_MESSAGE_HANDLER
* in your application's CONFIGURATION_HEADER
* @param messageType uint8_t The type of the incoming message. Currently, the only possibility is MFG_TEST_TYPE_ACK.
* @param data uint8_t * A pointer to the data received in the current message.
*/
ezspIncomingMfgTestMessageHandler(messageType, messageContents) {
logger_1.logger.debug(`ezspIncomingMfgTestMessageHandler: messageType=${messageType} messageContents=${messageContents.toString("hex")}`, NS);
}
/**
* A function used on the Golden Node to switch between normal network operation (for testing) and manufacturing configuration.
* Like emberSleep(), it may not be possible to execute this command due to pending network activity.
* For the transition from normal network operation to manufacturing configuration, it is customary to loop,
* calling this function alternately with emberTick() until the mode change succeeds.
* @param beginConfiguration Determines the new mode of operation.
* true causes the node to enter manufacturing configuration.
* false causes the node to return to normal network operation.
* @returns An SLStatus value indicating success or failure of the command.
*/
async ezspMfgTestSetPacketMode(beginConfiguration) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFG_TEST_SET_PACKET_MODE);
sendBuffalo.writeUInt8(beginConfiguration ? 1 : 0);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* A function used during manufacturing configuration on the Golden Node to send the DUT a reboot command.
* The usual practice is to execute this command at the end of manufacturing configuration,
* to place the DUT into normal network operation for testing.
* This function executes only during manufacturing configuration mode and returns an error otherwise.
* If successful, the DUT acknowledges the reboot command within 20 milliseconds and then reboots.
* @returns An SLStatus value indicating success or failure of the command.
*/
async ezspMfgTestSendRebootCommand() {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFG_TEST_SEND_REBOOT_COMMAND);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* A function used during manufacturing configuration on the Golden Node to set the DUT's 8-byte EUI ID.
* This function executes only during manufacturing configuration mode and returns an error otherwise.
* If successful, the DUT acknowledges the new EUI ID within 150 milliseconds.
* @param newId The 8-byte EUID for the DUT.
* @returns An SLStatus value indicating success or failure of the command.
*/
async ezspMfgTestSendEui64(newId) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFG_TEST_SEND_EUI64);
sendBuffalo.writeIeeeAddr(newId);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* A function used during manufacturing configuration on the Golden Node to set the DUT's 16-byte configuration string.
* This function executes only during manufacturing configuration mode and will return an error otherwise.
* If successful, the DUT will acknowledge the new string within 150 milliseconds.
* @param newString The 16-byte manufacturing string.
* @returns An SLStatus value indicating success or failure of the command.
*/
async ezspMfgTestSendManufacturingString(newString) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFG_TEST_SEND_MANUFACTURING_STRING);
sendBuffalo.writeListUInt16(newString); // expects 16 bytes
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* A function used during manufacturing configuration on the Golden Node to set the DUT's radio parameters.
* This function executes only during manufacturing configuration mode and returns an error otherwise.
* If successful, the DUT acknowledges the new parameters within 25 milliseconds.
* @param supportedBands Sets the radio band for the DUT. See ember-common.h for possible values.
* @param crystalOffset Sets the CC1020 crystal offset. This parameter has no effect on the EM2420, and it may safely be set to 0 for this RFIC.
* @returns An SLStatus value indicating success or failure of the command.
*/
async ezspMfgTestSendRadioParameters(supportedBands, crystalOffset) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFG_TEST_SEND_RADIO_PARAMETERS);
sendBuffalo.writeUInt8(supportedBands);
sendBuffalo.writeInt8(crystalOffset);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
/**
* A function used in each of the manufacturing configuration API calls.
* Most implementations will not need to call this function directly. See mfg-test.c for more detail.
* This function executes only during manufacturing configuration mode and returns an error otherwise.
* @param command A pointer to the outgoing command string.
* @returns An SLStatus value indicating success or failure of the command.
*/
async ezspMfgTestSendCommand(command) {
if (this.version < 0x0e) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.MFG_TEST_SEND_COMMAND);
sendBuffalo.writeUInt8(command);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readUInt32();
return status;
}
//-----------------------------------------------------------------------------
// ZLL Frames
//-----------------------------------------------------------------------------
/**
* A consolidation of ZLL network operations with similar signatures;
* specifically, forming and joining networks or touch-linking.
* @param networkInfo EmberZllNetwork * Information about the network.
* @param op Operation indicator.
* @param radioTxPower int8_t Radio transmission power.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspZllNetworkOps(networkInfo, op, radioTxPower) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ZLL_NETWORK_OPS);
sendBuffalo.writeEmberZllNetwork(networkInfo);
sendBuffalo.writeUInt8(op);
sendBuffalo.writeInt8(radioTxPower);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* This call will cause the device to setup the security information used in its
* network. It must be called prior to forming, starting, or joining a network.
* @param networkKey EmberKeyData * ZLL Network key.
* @param securityState EmberZllInitialSecurityState * Initial security state of the network.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspZllSetInitialSecurityState(networkKey, securityState) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ZLL_SET_INITIAL_SECURITY_STATE);
sendBuffalo.writeEmberKeyData(networkKey);
sendBuffalo.writeEmberZllInitialSecurityState(securityState);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* This call will update ZLL security token information. Unlike
* emberZllSetInitialSecurityState, this can be called while a network is
* already established.
* @param securityState EmberZllInitialSecurityState * Security state of the network.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspZllSetSecurityStateWithoutKey(securityState) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ZLL_SET_SECURITY_STATE_WITHOUT_KEY);
sendBuffalo.writeEmberZllInitialSecurityState(securityState);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* This call will initiate a ZLL network scan on all the specified channels.
* @param channelMask uint32_t The range of channels to scan.
* @param radioPowerForScan int8_t The radio output power used for the scan requests.
* @param nodeType The node type of the local device.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspZllStartScan(channelMask, radioPowerForScan, nodeType) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ZLL_START_SCAN);
sendBuffalo.writeUInt32(channelMask);
sendBuffalo.writeInt8(radioPowerForScan);
sendBuffalo.writeUInt8(nodeType);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* This call will change the mode of the radio so that the receiver is on for a
* specified amount of time when the device is idle.
* @param durationMs uint32_t The duration in milliseconds to leave the radio on.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspZllSetRxOnWhenIdle(durationMs) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ZLL_SET_RX_ON_WHEN_IDLE);
sendBuffalo.writeUInt32(durationMs);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Callback
* This call is fired when a ZLL network scan finds a ZLL network.
* @param networkInfo EmberZllNetwork * Information about the network.
* @param isDeviceInfoNull Used to interpret deviceInfo field.
* @param deviceInfo EmberZllDeviceInfoRecord * Device specific information.
* @param packetInfo Information about the incoming packet received from this network.
*/
ezspZllNetworkFoundHandler(networkInfo, isDeviceInfoNull, deviceInfo, packetInfo) {
logger_1.logger.debug(() => `ezspZllNetworkFoundHandler: networkInfo=${networkInfo} isDeviceInfoNull=${isDeviceInfoNull} deviceInfo=${deviceInfo} packetInfo=${JSON.stringify(packetInfo)}`, NS);
}
/**
* Callback
* This call is fired when a ZLL network scan is complete.
* @param status Status of the operation.
*/
ezspZllScanCompleteHandler(status) {
logger_1.logger.debug(`ezspZllScanCompleteHandler: status=${enums_1.SLStatus[status]}`, NS);
}
/**
* Callback
* This call is fired when network and group addresses are assigned to a remote
* mode in a network start or network join request.
* @param addressInfo EmberZllAddressAssignment * Address assignment information.
* @param packetInfo Information about the incoming packet received from this network.
*/
ezspZllAddressAssignmentHandler(addressInfo, packetInfo) {
logger_1.logger.debug(() => `ezspZllAddressAssignmentHandler: addressInfo=${addressInfo} packetInfo=${JSON.stringify(packetInfo)}`, NS);
}
/**
* Callback
* This call is fired when the device is a target of a touch link.
* @param networkInfo EmberZllNetwork * Information about the network.
*/
ezspZllTouchLinkTargetHandler(networkInfo) {
logger_1.logger.debug(`ezspZllTouchLinkTargetHandler: networkInfo=${networkInfo}`, NS);
}
/**
* Get the ZLL tokens.
* @returns EmberTokTypeStackZllData * Data token return value.
* @returns EmberTokTypeStackZllSecurity * Security token return value.
*/
async ezspZllGetTokens() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ZLL_GET_TOKENS);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const data = this.buffalo.readEmberTokTypeStackZllData();
const security = this.buffalo.readEmberTokTypeStackZllSecurity();
return [data, security];
}
/**
* Set the ZLL data token.
* @param data EmberTokTypeStackZllData * Data token to be set.
*/
async ezspZllSetDataToken(data) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ZLL_SET_DATA_TOKEN);
sendBuffalo.writeEmberTokTypeStackZllData(data);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Set the ZLL data token bitmask to reflect the ZLL network state.
*/
async ezspZllSetNonZllNetwork() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ZLL_SET_NON_ZLL_NETWORK);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Is this a ZLL network?
* @returns ZLL network?
*/
async ezspIsZllNetwork() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.IS_ZLL_NETWORK);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const isZllNetwork = this.buffalo.readUInt8() !== 0;
return isZllNetwork;
}
/**
* This call sets the radio's default idle power mode.
* @param mode The power mode to be set.
*/
async ezspZllSetRadioIdleMode(mode) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ZLL_SET_RADIO_IDLE_MODE);
sendBuffalo.writeUInt8(mode);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* This call gets the radio's default idle power mode.
* @returns uint8_t The current power mode.
*/
async ezspZllGetRadioIdleMode() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ZLL_GET_RADIO_IDLE_MODE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const radioIdleMode = this.buffalo.readUInt8();
return radioIdleMode;
}
/**
* This call sets the default node type for a factory new ZLL device.
* @param nodeType The node type to be set.
*/
async ezspSetZllNodeType(nodeType) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_ZLL_NODE_TYPE);
sendBuffalo.writeUInt8(nodeType);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* This call sets additional capability bits in the ZLL state.
* @param uint16_t A mask with the bits to be set or cleared.
*/
async ezspSetZllAdditionalState(state) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_ZLL_ADDITIONAL_STATE);
sendBuffalo.writeUInt16(state);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Is there a ZLL (Touchlink) operation in progress?
* @returns ZLL operation in progress? false on error
*/
async ezspZllOperationInProgress() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ZLL_OPERATION_IN_PROGRESS);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const zllOperationInProgress = this.buffalo.readUInt8() !== 0;
return zllOperationInProgress;
}
/**
* Is the ZLL radio on when idle mode is active?
* @returns ZLL radio on when idle mode is active? false on error
*/
async ezspZllRxOnWhenIdleGetActive() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ZLL_RX_ON_WHEN_IDLE_GET_ACTIVE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const zllRxOnWhenIdleGetActive = this.buffalo.readUInt8() !== 0;
return zllRxOnWhenIdleGetActive;
}
/**
* Informs the ZLL API that application scanning is complete
*/
async ezspZllScanningComplete() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ZLL_SCANNING_COMPLETE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Get the primary ZLL (touchlink) channel mask.
* @returns uint32_t The primary ZLL channel mask
*/
async ezspGetZllPrimaryChannelMask() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_ZLL_PRIMARY_CHANNEL_MASK);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const zllPrimaryChannelMask = this.buffalo.readUInt32();
return zllPrimaryChannelMask;
}
/**
* Get the secondary ZLL (touchlink) channel mask.
* @returns uint32_t The secondary ZLL channel mask
*/
async ezspGetZllSecondaryChannelMask() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_ZLL_SECONDARY_CHANNEL_MASK);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const zllSecondaryChannelMask = this.buffalo.readUInt32();
return zllSecondaryChannelMask;
}
/**
* Set the primary ZLL (touchlink) channel mask
* @param uint32_t The primary ZLL channel mask
*/
async ezspSetZllPrimaryChannelMask(zllPrimaryChannelMask) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_ZLL_PRIMARY_CHANNEL_MASK);
sendBuffalo.writeUInt32(zllPrimaryChannelMask);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Set the secondary ZLL (touchlink) channel mask.
* @param uint32_t The secondary ZLL channel mask
*/
async ezspSetZllSecondaryChannelMask(zllSecondaryChannelMask) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_ZLL_SECONDARY_CHANNEL_MASK);
sendBuffalo.writeUInt32(zllSecondaryChannelMask);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Clear ZLL stack tokens.
*/
async ezspZllClearTokens() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.ZLL_CLEAR_TOKENS);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
//-----------------------------------------------------------------------------
// Green Power Frames
//-----------------------------------------------------------------------------
/**
* Update the GP Proxy table based on a GP pairing.
* @param options uint32_t The options field of the GP Pairing command.
* @param addr EmberGpAddress * The target GPD.
* @param commMode uint8_t The communication mode of the GP Sink.
* @param sinkNetworkAddress uint16_t The network address of the GP Sink.
* @param sinkGroupId uint16_t The group ID of the GP Sink.
* @param assignedAlias uint16_t The alias assigned to the GPD.
* @param sinkIeeeAddress uint8_t * The IEEE address of the GP Sink.
* @param gpdKey EmberKeyData * The key to use for the target GPD.
* @param gpdSecurityFrameCounter uint32_t The GPD security frame counter.
* @param forwardingRadius uint8_t The forwarding radius.
* @returns Whether a GP Pairing has been created or not.
*/
async ezspGpProxyTableProcessGpPairing(options, addr, commMode, sinkNetworkAddress, sinkGroupId, assignedAlias, sinkIeeeAddress, gpdKey, gpdSecurityFrameCounter, forwardingRadius) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_PROXY_TABLE_PROCESS_GP_PAIRING);
sendBuffalo.writeUInt32(options);
sendBuffalo.writeEmberGpAddress(addr);
sendBuffalo.writeUInt8(commMode);
sendBuffalo.writeUInt16(sinkNetworkAddress);
sendBuffalo.writeUInt16(sinkGroupId);
sendBuffalo.writeUInt16(assignedAlias);
sendBuffalo.writeIeeeAddr(sinkIeeeAddress);
sendBuffalo.writeEmberKeyData(gpdKey);
sendBuffalo.writeUInt32(gpdSecurityFrameCounter);
sendBuffalo.writeUInt8(forwardingRadius);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const gpPairingAdded = this.buffalo.readUInt8() !== 0;
return gpPairingAdded;
}
/**
* Adds/removes an entry from the GP Tx Queue.
* @param action The action to perform on the GP TX queue (true to add, false to remove).
* @param useCca Whether to use ClearChannelAssessment when transmitting the GPDF.
* @param addr EmberGpAddress * The Address of the destination GPD.
* @param gpdCommandId uint8_t The GPD command ID to send.
* @param gpdAsdu uint8_t * The GP command payload.
* @param gpepHandle uint8_t The handle to refer to the GPDF.
* @param gpTxQueueEntryLifetimeMs uint16_t How long to keep the GPDF in the TX Queue.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspDGpSend(action, useCca, addr, gpdCommandId, gpdAsdu, gpepHandle, gpTxQueueEntryLifetimeMs) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.D_GP_SEND);
sendBuffalo.writeUInt8(action ? 1 : 0);
sendBuffalo.writeUInt8(useCca ? 1 : 0);
sendBuffalo.writeEmberGpAddress(addr);
sendBuffalo.writeUInt8(gpdCommandId);
sendBuffalo.writePayload(gpdAsdu);
sendBuffalo.writeUInt8(gpepHandle);
sendBuffalo.writeUInt16(gpTxQueueEntryLifetimeMs);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Callback
* A callback to the GP endpoint to indicate the result of the GPDF
* transmission.
* @param status An SLStatus value indicating success or the reason for failure.
* @param gpepHandle uint8_t The handle of the GPDF.
*/
ezspDGpSentHandler(status, gpepHandle) {
logger_1.logger.debug(`ezspDGpSentHandler: status=${enums_1.SLStatus[status]} gpepHandle=${gpepHandle}`, NS);
}
/**
* Callback
* A callback invoked by the Zigbee GP stack when a GPDF is received.
* @param status The status of the GPDF receive.
* @param gpdLink uint8_t The gpdLink value of the received GPDF.
* @param sequenceNumber uint8_t The GPDF sequence number.
* @param addr EmberGpAddress *The address of the source GPD.
* @param gpdfSecurityLevel The security level of the received GPDF.
* @param gpdfSecurityKeyType The securityKeyType used to decrypt/authenticate the incoming GPDF.
* @param autoCommissioning Whether the incoming GPDF had the auto-commissioning bit set.
* @param bidirectionalInfo uint8_t Bidirectional information represented in bitfields,
* where bit0 holds the rxAfterTx of incoming gpdf and bit1 holds if tx queue is available for outgoing gpdf.
* @param gpdSecurityFrameCounter uint32_t The security frame counter of the incoming GPDF.
* @param gpdCommandId uint8_t The gpdCommandId of the incoming GPDF.
* @param mic uint32_t The received MIC of the GPDF.
* @param proxyTableIndex uint8_tThe proxy table index of the corresponding proxy table entry to the incoming GPDF.
* @param gpdCommandPayload uint8_t * The GPD command payload.
* @param packetInfo Rx packet information.
*/
ezspGpepIncomingMessageHandler(status, gpdLink, sequenceNumber, addr, gpdfSecurityLevel, gpdfSecurityKeyType, autoCommissioning, bidirectionalInfo, gpdSecurityFrameCounter, gpdCommandId, mic, proxyTableIndex, gpdCommandPayload, packetInfo) {
logger_1.logger.debug(() => `ezspGpepIncomingMessageHandler: status=${enums_1.EmberGPStatus[status] ?? status} gpdLink=${gpdLink} ` +
`sequenceNumber=${sequenceNumber} addr=${JSON.stringify(addr)} gpdfSecurityLevel=${enums_1.EmberGpSecurityLevel[gpdfSecurityLevel]} ` +
`gpdfSecurityKeyType=${enums_1.EmberGpKeyType[gpdfSecurityKeyType]} autoCommissioning=${autoCommissioning} ` +
`bidirectionalInfo=${bidirectionalInfo} gpdSecurityFrameCounter=${gpdSecurityFrameCounter} gpdCommandId=${gpdCommandId} ` +
`mic=${mic} proxyTableIndex=${proxyTableIndex} gpdCommandPayload=${gpdCommandPayload.toString("hex")} packetInfo=${JSON.stringify(packetInfo)}`, NS);
if (addr.applicationId === enums_1.EmberGpApplicationId.IEEE_ADDRESS) {
// XXX: don't bother parsing for upstream for now, since it will be rejected
logger_1.logger.error("<=x= [GP] Received IEEE address type in message. Support not implemented upstream. Dropping.", NS);
return;
}
let commandIdentifier = cluster_1.Clusters.greenPower.commands.notification.ID;
let options = 0;
const isCommissioning = gpdCommandId === 0xe0;
if (isCommissioning) {
if (!gpdCommandPayload.length) {
// XXX: seem to be receiving duplicate commissioningNotification from some devices, second one with empty payload?
// this will mess with the process no doubt, so dropping them
logger_1.logger.debug("<=x= [GP] Received commissioning notification with empty payload. Dropping.", NS);
return;
}
commandIdentifier = cluster_1.Clusters.greenPower.commands.commissioningNotification.ID;
options =
(addr.applicationId & 0x7) | ((bidirectionalInfo & 0x1) << 3) | ((gpdfSecurityLevel & 0x3) << 4) | ((gpdfSecurityKeyType & 0x7) << 6);
}
else {
options =
(addr.applicationId & 0x7) |
((gpdfSecurityLevel & 0x3) << 6) |
((gpdfSecurityKeyType & 0x7) << 8) |
((bidirectionalInfo & 0x1) << 11);
}
const apsFrame = {
profileId: ZSpec.GP_PROFILE_ID,
clusterId: Zcl.Clusters.greenPower.ID,
sourceEndpoint: ZSpec.GP_ENDPOINT,
destinationEndpoint: ZSpec.GP_ENDPOINT,
options: 0, // not used
groupId: ZSpec.GP_GROUP_ID,
sequence: 0, // not used
};
// this stuff is already parsed by EmberZNet stack, but Z2M expects the full buffer, so combine it back
let offset = 0;
const messageContents = Buffer.alloc(15 + gpdCommandPayload.length);
offset = messageContents.writeUInt8(0b00000001, offset); // frameControl: FrameType.SPECIFIC + Direction.CLIENT_TO_SERVER + disableDefaultResponse=false
offset = messageContents.writeUInt8(sequenceNumber, offset);
offset = messageContents.writeUInt8(commandIdentifier, offset); // commandIdentifier
offset = messageContents.writeUInt16LE(options, offset);
offset = messageContents.writeUInt32LE(addr.sourceId, offset);
offset = messageContents.writeUInt32LE(gpdSecurityFrameCounter, offset);
offset = messageContents.writeUInt8(gpdCommandId, offset);
offset = messageContents.writeUInt8(gpdCommandPayload.length, offset);
offset = gpdCommandPayload.copy(messageContents, offset);
// upstream will use COORDINATOR_ADDRESS if GPP not present
this.emit("incomingMessage", enums_1.EmberIncomingMessageType.UNICAST, apsFrame, packetInfo.lastHopLqi, addr.sourceId & 0xffff, messageContents);
}
/**
* Retrieves the proxy table entry stored at the passed index.
* @param proxyIndex uint8_t The index of the requested proxy table entry.
* @returns An SLStatus value indicating success or the reason for failure.
* @returns EmberGpProxyTableEntry * An EmberGpProxyTableEntry struct containing a copy of the requested proxy entry.
*/
async ezspGpProxyTableGetEntry(proxyIndex) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_PROXY_TABLE_GET_ENTRY);
sendBuffalo.writeUInt8(proxyIndex);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const entry = this.buffalo.readEmberGpProxyTableEntry();
return [status, entry];
}
/**
* Finds the index of the passed address in the gp table.
* @param addr EmberGpAddress * The address to search for
* @returns uint8_t The index, or 0xFF for not found
*/
async ezspGpProxyTableLookup(addr) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_PROXY_TABLE_LOOKUP);
sendBuffalo.writeEmberGpAddress(addr);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const index = this.buffalo.readUInt8();
return index;
}
/**
* Removes the proxy table entry stored at the passed index.
* @param proxyIndex The index of the requested proxy table entry.
*/
async ezspGpProxyTableRemoveEntry(proxyIndex) {
if (this.version < 0x11) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_PROXY_TABLE_REMOVE_ENTRY);
sendBuffalo.writeUInt8(proxyIndex);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Clear the entire proxy table
*/
async ezspGpClearProxyTable() {
if (this.version < 0x11) {
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_FRAME_ID);
}
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_CLEAR_PROXY_TABLE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Retrieves the sink table entry stored at the passed index.
* @param sinkIndex uint8_t The index of the requested sink table entry.
* @returns An SLStatus value indicating success or the reason for failure.
* @returns EmberGpSinkTableEntry * An EmberGpSinkTableEntry struct containing a copy of the requested sink entry.
*/
async ezspGpSinkTableGetEntry(sinkIndex) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_SINK_TABLE_GET_ENTRY);
sendBuffalo.writeUInt8(sinkIndex);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const entry = this.buffalo.readEmberGpSinkTableEntry();
return [status, entry];
}
/**
* Finds the index of the passed address in the gp table.
* @param addr EmberGpAddress *The address to search for.
* @returns uint8_t The index, or 0xFF for not found
*/
async ezspGpSinkTableLookup(addr) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_SINK_TABLE_LOOKUP);
sendBuffalo.writeEmberGpAddress(addr);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const index = this.buffalo.readUInt8();
return index;
}
/**
* Retrieves the sink table entry stored at the passed index.
* @param sinkIndex uint8_t The index of the requested sink table entry.
* @param entry EmberGpSinkTableEntry * An EmberGpSinkTableEntry struct containing a copy of the sink entry to be updated.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspGpSinkTableSetEntry(sinkIndex, entry) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_SINK_TABLE_SET_ENTRY);
sendBuffalo.writeUInt8(sinkIndex);
sendBuffalo.writeEmberGpSinkTableEntry(entry);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Removes the sink table entry stored at the passed index.
* @param uint8_t The index of the requested sink table entry.
*/
async ezspGpSinkTableRemoveEntry(sinkIndex) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_SINK_TABLE_REMOVE_ENTRY);
sendBuffalo.writeUInt8(sinkIndex);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Finds or allocates a sink entry
* @param addr EmberGpAddress * An EmberGpAddress struct containing a copy of the gpd address to be found.
* @returns uint8_t An index of found or allocated sink or 0xFF if failed.
*/
async ezspGpSinkTableFindOrAllocateEntry(addr) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_SINK_TABLE_FIND_OR_ALLOCATE_ENTRY);
sendBuffalo.writeEmberGpAddress(addr);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const index = this.buffalo.readUInt8();
return index;
}
/**
* Clear the entire sink table
*/
async ezspGpSinkTableClearAll() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_SINK_TABLE_CLEAR_ALL);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Iniitializes Sink Table
*/
async ezspGpSinkTableInit() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_SINK_TABLE_INIT);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Sets security framecounter in the sink table
* @param index uint8_t Index to the Sink table
* @param sfc uint32_t Security Frame Counter
*/
async ezspGpSinkTableSetSecurityFrameCounter(index, sfc) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_SINK_TABLE_SET_SECURITY_FRAME_COUNTER);
sendBuffalo.writeUInt8(index);
sendBuffalo.writeUInt32(sfc);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Puts the GPS in commissioning mode.
* @param uint8_t commissioning options
* @param uint16_t gpm address for security.
* @param uint16_t gpm address for pairing.
* @param uint8_t sink endpoint.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspGpSinkCommission(options, gpmAddrForSecurity, gpmAddrForPairing, sinkEndpoint) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_SINK_COMMISSION);
sendBuffalo.writeUInt8(options);
sendBuffalo.writeUInt16(gpmAddrForSecurity);
sendBuffalo.writeUInt16(gpmAddrForPairing);
sendBuffalo.writeUInt8(sinkEndpoint);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Clears all entries within the translation table.
*/
async ezspGpTranslationTableClear() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_TRANSLATION_TABLE_CLEAR);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Return number of active entries in sink table.
* @returns uint8_t Number of active entries in sink table. 0 if error.
*/
async ezspGpSinkTableGetNumberOfActiveEntries() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_SINK_TABLE_GET_NUMBER_OF_ACTIVE_ENTRIES);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const numberOfEntries = this.buffalo.readUInt8();
return numberOfEntries;
}
//-----------------------------------------------------------------------------
// Token Interface Frames
//-----------------------------------------------------------------------------
/**
* Gets the total number of tokens.
* @returns uint32_t Total number of tokens.
* - < v18: uint8_t
*/
async ezspGetTokenCount() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_TOKEN_COUNT);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const count = this.version < 0x12 ? this.buffalo.readUInt8() : this.buffalo.readUInt32();
return count;
}
/**
* Gets the token information for a single token at provided index
* @param index uint8_t Index of the token in the token table for which information is needed.
* @returns An SLStatus value indicating success or the reason for failure.
* @returns EmberTokenInfo * Token information.
*/
async ezspGetTokenInfo(index) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_TOKEN_INFO);
sendBuffalo.writeUInt8(index);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const tokenInfo = this.buffalo.readEmberTokenInfo();
return [status, tokenInfo];
}
/**
* Gets the token data for a single token with provided key
* @param token uint32_t Key of the token in the token table for which data is needed.
* @param index uint32_t Index in case of the indexed token.
* @returns An SLStatus value indicating success or the reason for failure.
* @returns EmberTokenData * Token Data
*/
async ezspGetTokenData(token, index) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GET_TOKEN_DATA);
sendBuffalo.writeUInt32(token);
sendBuffalo.writeUInt32(index);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
const tokenData = this.buffalo.readEmberTokenData();
return [status, tokenData];
}
/**
* Sets the token data for a single token with provided key
* @param token uint32_t Key of the token in the token table for which data is to be set.
* @param index uint32_t Index in case of the indexed token.
* @param EmberTokenData * Token Data
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspSetTokenData(token, index, tokenData) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.SET_TOKEN_DATA);
sendBuffalo.writeUInt32(token);
sendBuffalo.writeUInt32(index);
sendBuffalo.writeEmberTokenData(tokenData);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Reset the node by calling halReboot.
*/
async ezspResetNode() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.RESET_NODE);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
/**
* Run GP security test vectors.
* @returns An SLStatus value indicating success or the reason for failure.
*/
async ezspGpSecurityTestVectors() {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.GP_SECURITY_TEST_VECTORS);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
const status = this.buffalo.readStatus(this.version);
return status;
}
/**
* Factory reset all configured zigbee tokens
* @param excludeOutgoingFC Exclude network and APS outgoing frame counter tokens.
* @param excludeBootCounter Exclude stack boot counter token.
*/
async ezspTokenFactoryReset(excludeOutgoingFC, excludeBootCounter) {
const sendBuffalo = this.startCommand(enums_2.EzspFrameID.TOKEN_FACTORY_RESET);
sendBuffalo.writeUInt8(excludeOutgoingFC ? 1 : 0);
sendBuffalo.writeUInt8(excludeBootCounter ? 1 : 0);
const sendStatus = await this.sendCommand(sendBuffalo);
if (sendStatus !== enums_1.EzspStatus.SUCCESS) {
throw new ezspError_1.EzspError(sendStatus);
}
}
}
exports.Ezsp = Ezsp;
//# sourceMappingURL=ezsp.js.map