zigbee-herdsman
Version:
An open source ZigBee gateway solution with node.js.
677 lines • 28.7 kB
JavaScript
/* v8 ignore start */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Ezsp = exports.EZSPZDOResponseFrameData = exports.EZSPZDORequestFrameData = exports.EZSPFrameData = void 0;
const node_events_1 = require("node:events");
const utils_1 = require("../../../utils");
const logger_1 = require("../../../utils/logger");
const commands_1 = require("./commands");
const t = __importStar(require("./types"));
const named_1 = require("./types/named");
const uart_1 = require("./uart");
const NS = "zh:ezsp:ezsp";
const MAX_SERIAL_CONNECT_ATTEMPTS = 4;
/** In ms. This is multiplied by tries count (above), e.g. 4 tries = 5000, 10000, 15000 */
const SERIAL_CONNECT_NEW_ATTEMPT_MIN_DELAY = 5000;
const MTOR_MIN_INTERVAL = 10;
const MTOR_MAX_INTERVAL = 90;
const MTOR_ROUTE_ERROR_THRESHOLD = 4;
const MTOR_DELIVERY_FAIL_THRESHOLD = 3;
const MAX_WATCHDOG_FAILURES = 4;
//const RESET_ATTEMPT_BACKOFF_TIME = 5;
const WATCHDOG_WAKE_PERIOD = 10; // in sec
//const EZSP_COUNTER_CLEAR_INTERVAL = 180; // Clear counters every n * WATCHDOG_WAKE_PERIOD
const EZSP_DEFAULT_RADIUS = 0;
const EZSP_MULTICAST_NON_MEMBER_RADIUS = 3;
const CONFIG_IDS_PRE_V9 = [
[named_1.EzspConfigId.CONFIG_TC_REJOINS_USING_WELL_KNOWN_KEY_TIMEOUT_S, 90],
[named_1.EzspConfigId.CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE, 2],
//[EzspConfigId.CONFIG_SUPPORTED_NETWORKS, 1],
[named_1.EzspConfigId.CONFIG_FRAGMENT_DELAY_MS, 50],
[named_1.EzspConfigId.CONFIG_PAN_ID_CONFLICT_REPORT_THRESHOLD, 2],
//[EzspConfigId.CONFIG_SOURCE_ROUTE_TABLE_SIZE, 16],
//[EzspConfigId.CONFIG_ADDRESS_TABLE_SIZE, 16],
[
named_1.EzspConfigId.CONFIG_APPLICATION_ZDO_FLAGS,
named_1.EmberZdoConfigurationFlags.APP_HANDLES_UNSUPPORTED_ZDO_REQUESTS | named_1.EmberZdoConfigurationFlags.APP_RECEIVES_SUPPORTED_ZDO_REQUESTS,
],
[named_1.EzspConfigId.CONFIG_INDIRECT_TRANSMISSION_TIMEOUT, 7680],
[named_1.EzspConfigId.CONFIG_END_DEVICE_POLL_TIMEOUT, 14],
[named_1.EzspConfigId.CONFIG_SECURITY_LEVEL, 5],
[named_1.EzspConfigId.CONFIG_STACK_PROFILE, 2],
//[EzspConfigId.CONFIG_TX_POWER_MODE, 3],
[named_1.EzspConfigId.CONFIG_FRAGMENT_WINDOW_SIZE, 1],
//[EzspConfigId.CONFIG_NEIGHBOR_TABLE_SIZE, 16],
//[EzspConfigId.CONFIG_ROUTE_TABLE_SIZE, 16],
//[EzspConfigId.CONFIG_BINDING_TABLE_SIZE, 32],
//[EzspConfigId.CONFIG_KEY_TABLE_SIZE, 12],
//[EzspConfigId.CONFIG_ZLL_GROUP_ADDRESSES, 0],
//[EzspConfigId.CONFIG_ZLL_RSSI_THRESHOLD, 0],
//[EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT, 255],
//[EzspConfigId.CONFIG_BROADCAST_TABLE_SIZE, 43],
//[EzspConfigId.CONFIG_MAX_HOPS, 30],
//[EzspConfigId.CONFIG_MAX_END_DEVICE_CHILDREN, 32],
[named_1.EzspConfigId.CONFIG_PACKET_BUFFER_COUNT, 255],
];
/**
* Can only decrease "NCP Memory Allocation" configs at runtime from V9 on.
* @see https://www.silabs.com/documents/public/release-notes/emberznet-release-notes-7.0.1.0.pdf
*/
const CONFIG_IDS_CURRENT = [
[named_1.EzspConfigId.CONFIG_TC_REJOINS_USING_WELL_KNOWN_KEY_TIMEOUT_S, 90],
[named_1.EzspConfigId.CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE, 2],
[named_1.EzspConfigId.CONFIG_FRAGMENT_DELAY_MS, 50],
[named_1.EzspConfigId.CONFIG_PAN_ID_CONFLICT_REPORT_THRESHOLD, 2],
[
named_1.EzspConfigId.CONFIG_APPLICATION_ZDO_FLAGS,
named_1.EmberZdoConfigurationFlags.APP_HANDLES_UNSUPPORTED_ZDO_REQUESTS | named_1.EmberZdoConfigurationFlags.APP_RECEIVES_SUPPORTED_ZDO_REQUESTS,
],
[named_1.EzspConfigId.CONFIG_INDIRECT_TRANSMISSION_TIMEOUT, 7680],
[named_1.EzspConfigId.CONFIG_END_DEVICE_POLL_TIMEOUT, 14],
[named_1.EzspConfigId.CONFIG_SECURITY_LEVEL, 5],
[named_1.EzspConfigId.CONFIG_STACK_PROFILE, 2],
[named_1.EzspConfigId.CONFIG_FRAGMENT_WINDOW_SIZE, 1],
];
const POLICY_IDS_PRE_V8 = [
// [EzspPolicyId.BINDING_MODIFICATION_POLICY,
// EzspDecisionId.DISALLOW_BINDING_MODIFICATION],
// [EzspPolicyId.UNICAST_REPLIES_POLICY, EzspDecisionId.HOST_WILL_NOT_SUPPLY_REPLY],
// [EzspPolicyId.POLL_HANDLER_POLICY, EzspDecisionId.POLL_HANDLER_IGNORE],
// [EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY,
// EzspDecisionId.MESSAGE_TAG_ONLY_IN_CALLBACK],
// [EzspPolicyId.PACKET_VALIDATE_LIBRARY_POLICY,
// EzspDecisionId.PACKET_VALIDATE_LIBRARY_CHECKS_DISABLED],
// [EzspPolicyId.ZLL_POLICY, EzspDecisionId.ALLOW_JOINS],
// [EzspPolicyId.TC_REJOINS_USING_WELL_KNOWN_KEY_POLICY, EzspDecisionId.ALLOW_JOINS],
[named_1.EzspPolicyId.APP_KEY_REQUEST_POLICY, named_1.EzspDecisionId.DENY_APP_KEY_REQUESTS],
[named_1.EzspPolicyId.TC_KEY_REQUEST_POLICY, named_1.EzspDecisionId.ALLOW_TC_KEY_REQUESTS],
];
const POLICY_IDS_CURRENT = [
[named_1.EzspPolicyId.APP_KEY_REQUEST_POLICY, named_1.EzspDecisionId.DENY_APP_KEY_REQUESTS],
[named_1.EzspPolicyId.TC_KEY_REQUEST_POLICY, named_1.EzspDecisionId.ALLOW_TC_KEY_REQUESTS],
[named_1.EzspPolicyId.TRUST_CENTER_POLICY, named_1.EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS | named_1.EzspDecisionBitmask.ALLOW_JOINS],
];
class EZSPFrameData {
_cls_;
_id_;
_isRequest_;
static createFrame(ezspv, frameId, isRequest, params) {
const names = commands_1.FRAME_NAMES_BY_ID[frameId];
if (!names) {
throw new Error(`Unrecognized frame FrameID ${frameId}`);
}
let frm;
names.every((frameName) => {
const frameDesc = EZSPFrameData.getFrame(frameName);
if ((frameDesc.maxV && frameDesc.maxV < ezspv) || (frameDesc.minV && frameDesc.minV > ezspv)) {
return true;
}
try {
frm = new EZSPFrameData(frameName, isRequest, params);
}
catch (error) {
logger_1.logger.error(`Frame ${frameName} parsing error: ${error}`, NS);
return true;
}
return false;
});
// biome-ignore lint/style/noNonNullAssertion: ignored using `--suppress`
return frm;
}
static getFrame(name) {
const frameDesc = commands_1.FRAMES[name];
if (!frameDesc)
throw new Error(`Unrecognized frame from FrameID ${name}`);
return frameDesc;
}
constructor(key, isRequest, params) {
this._cls_ = key;
this._id_ = commands_1.FRAMES[this._cls_].ID;
this._isRequest_ = isRequest;
const frame = EZSPFrameData.getFrame(key);
const frameDesc = this._isRequest_ ? frame.request || {} : frame.response || {};
if (Buffer.isBuffer(params)) {
let data = params;
for (const prop of Object.getOwnPropertyNames(frameDesc)) {
[this[prop], data] = frameDesc[prop].deserialize(frameDesc[prop], data);
}
}
else {
for (const prop of Object.getOwnPropertyNames(frameDesc)) {
// biome-ignore lint/style/noNonNullAssertion: ignored using `--suppress`
this[prop] = params[prop]; // XXX: assumed defined with logic
}
}
}
serialize() {
const frame = EZSPFrameData.getFrame(this._cls_);
const frameDesc = this._isRequest_ ? frame.request || {} : frame.response || {};
const result = [];
for (const prop of Object.getOwnPropertyNames(frameDesc)) {
result.push(frameDesc[prop].serialize(frameDesc[prop], this[prop]));
}
return Buffer.concat(result);
}
get name() {
return this._cls_;
}
get id() {
return this._id_;
}
}
exports.EZSPFrameData = EZSPFrameData;
class EZSPZDORequestFrameData {
_cls_;
_id_;
_isRequest_;
static getFrame(key) {
const name = typeof key === "string" ? key : commands_1.ZDOREQUEST_NAME_BY_ID[key];
const frameDesc = commands_1.ZDOREQUESTS[name];
if (!frameDesc)
throw new Error(`Unrecognized ZDOFrame from FrameID ${key}`);
return frameDesc;
}
constructor(key, isRequest, params) {
if (typeof key === "string") {
this._cls_ = key;
this._id_ = commands_1.ZDOREQUESTS[this._cls_].ID;
}
else {
this._id_ = key;
this._cls_ = commands_1.ZDOREQUEST_NAME_BY_ID[key];
}
this._isRequest_ = isRequest;
const frame = EZSPZDORequestFrameData.getFrame(key);
const frameDesc = this._isRequest_ ? frame.request || {} : frame.response || {};
if (Buffer.isBuffer(params)) {
let data = params;
for (const prop of Object.getOwnPropertyNames(frameDesc)) {
[this[prop], data] = frameDesc[prop].deserialize(frameDesc[prop], data);
}
}
else {
for (const prop of Object.getOwnPropertyNames(frameDesc)) {
this[prop] = params[prop];
}
}
}
serialize() {
const frame = EZSPZDORequestFrameData.getFrame(this._cls_);
const frameDesc = this._isRequest_ ? frame.request || {} : frame.response || {};
const result = [];
for (const prop of Object.getOwnPropertyNames(frameDesc)) {
result.push(frameDesc[prop].serialize(frameDesc[prop], this[prop]));
}
return Buffer.concat(result);
}
get name() {
return this._cls_;
}
get id() {
return this._id_;
}
}
exports.EZSPZDORequestFrameData = EZSPZDORequestFrameData;
class EZSPZDOResponseFrameData {
_cls_;
_id_;
static getFrame(key) {
const name = typeof key === "string" ? key : commands_1.ZDORESPONSE_NAME_BY_ID[key];
const frameDesc = commands_1.ZDORESPONSES[name];
if (!frameDesc)
throw new Error(`Unrecognized ZDOFrame from FrameID ${key}`);
return frameDesc.params;
}
constructor(key, params) {
if (typeof key === "string") {
this._cls_ = key;
this._id_ = commands_1.ZDORESPONSES[this._cls_].ID;
}
else {
this._id_ = key;
this._cls_ = commands_1.ZDORESPONSE_NAME_BY_ID[key];
}
const frameDesc = EZSPZDOResponseFrameData.getFrame(key);
if (Buffer.isBuffer(params)) {
let data = params;
for (const prop of Object.getOwnPropertyNames(frameDesc)) {
[this[prop], data] = frameDesc[prop].deserialize(frameDesc[prop], data);
}
}
else {
for (const prop of Object.getOwnPropertyNames(frameDesc)) {
this[prop] = params[prop];
}
}
}
serialize() {
const frameDesc = EZSPZDOResponseFrameData.getFrame(this._cls_);
const result = [];
for (const prop of Object.getOwnPropertyNames(frameDesc)) {
result.push(frameDesc[prop].serialize(frameDesc[prop], this[prop]));
}
return Buffer.concat(result);
}
get name() {
return this._cls_;
}
get id() {
return this._id_;
}
}
exports.EZSPZDOResponseFrameData = EZSPZDOResponseFrameData;
class Ezsp extends node_events_1.EventEmitter {
ezspV = 4;
cmdSeq = 0; // command sequence
// COMMANDS_BY_ID = new Map<number, { name: string, inArgs: any[], outArgs: any[] }>();
serialDriver;
waitress;
queue;
watchdogTimer;
failures = 0;
inResetingProcess = false;
constructor() {
super();
this.queue = new utils_1.Queue();
this.waitress = new utils_1.Waitress(this.waitressValidator, this.waitressTimeoutFormatter);
this.serialDriver = new uart_1.SerialDriver();
this.serialDriver.on("received", this.onFrameReceived.bind(this));
this.serialDriver.on("close", this.onSerialClose.bind(this));
}
async connect(options) {
let lastError = null;
const resetForReconnect = () => {
throw new Error("Failure to connect");
};
this.serialDriver.on("reset", resetForReconnect);
for (let i = 1; i <= MAX_SERIAL_CONNECT_ATTEMPTS; i++) {
try {
await this.serialDriver.connect(options);
break;
}
catch (error) {
logger_1.logger.error(`Connection attempt ${i} error: ${error}`, NS);
if (i < MAX_SERIAL_CONNECT_ATTEMPTS) {
await (0, utils_1.wait)(SERIAL_CONNECT_NEW_ATTEMPT_MIN_DELAY * i);
logger_1.logger.debug(`Next attempt ${i + 1}`, NS);
}
lastError = error;
}
}
this.serialDriver.off("reset", resetForReconnect);
if (!this.serialDriver.isInitialized()) {
throw new Error("Failure to connect", { cause: lastError });
}
this.inResetingProcess = false;
this.serialDriver.on("reset", this.onSerialReset.bind(this));
if (WATCHDOG_WAKE_PERIOD) {
this.watchdogTimer = setInterval(this.watchdogHandler.bind(this), WATCHDOG_WAKE_PERIOD * 1000);
}
}
isInitialized() {
return this.serialDriver?.isInitialized();
}
onSerialReset() {
logger_1.logger.debug("onSerialReset()", NS);
this.inResetingProcess = true;
this.emit("reset");
}
onSerialClose() {
logger_1.logger.debug("onSerialClose()", NS);
if (!this.inResetingProcess) {
this.emit("close");
}
}
async close(emitClose) {
logger_1.logger.debug("Closing Ezsp", NS);
clearTimeout(this.watchdogTimer);
this.queue.clear();
await this.serialDriver.close(emitClose);
}
/**
* Handle a received EZSP frame
*
* The protocol has taken care of UART specific framing etc, so we should
* just have EZSP application stuff here, with all escaping/stuffing and
* data randomization removed.
* @param data
*/
onFrameReceived(data) {
logger_1.logger.debug(`<== Frame: ${data.toString("hex")}`, NS);
let frameId;
const sequence = data[0];
if (this.ezspV < 8) {
[frameId, data] = [data[2], data.subarray(3)];
}
else {
[[frameId], data] = t.deserialize(data.subarray(3), [t.uint16_t]);
}
if (frameId === 255) {
frameId = 0;
if (data.length > 1) {
frameId = data[1];
data = data.subarray(2);
}
}
const frm = EZSPFrameData.createFrame(this.ezspV, frameId, false, data);
if (!frm) {
logger_1.logger.error(`Unparsed frame 0x${frameId.toString(16)}. Skipped`, NS);
return;
}
logger_1.logger.debug(() => `<== 0x${frameId.toString(16)}: ${JSON.stringify(frm)}`, NS);
const handled = this.waitress.resolve({
frameId,
frameName: frm.name,
sequence,
payload: frm,
});
if (!handled) {
this.emit("frame", frm.name, frm);
}
if (frameId === 0) {
this.ezspV = frm.protocolVersion;
}
}
async version() {
const version = this.ezspV;
const result = await this.execCommand("version", { desiredProtocolVersion: version });
if (result.protocolVersion >= 14) {
throw new Error(`'ezsp' driver is not compatible with firmware 8.x.x or above (EZSP v14+). Use 'ember' driver instead.`);
}
if (result.protocolVersion !== version) {
logger_1.logger.debug(`Switching to eszp version ${result.protocolVersion}`, NS);
await this.execCommand("version", { desiredProtocolVersion: result.protocolVersion });
}
return result.protocolVersion;
}
async networkInit() {
const waiter = this.waitFor("stackStatusHandler", null);
const result = await this.execCommand("networkInit");
logger_1.logger.debug(`Network init result: ${JSON.stringify(result)}`, NS);
if (result.status !== named_1.EmberStatus.SUCCESS) {
this.waitress.remove(waiter.ID);
logger_1.logger.error("Failure to init network", NS);
return false;
}
const response = await waiter.start().promise;
return response.payload.status === named_1.EmberStatus.NETWORK_UP;
}
async leaveNetwork() {
const waiter = this.waitFor("stackStatusHandler", null);
const result = await this.execCommand("leaveNetwork");
logger_1.logger.debug(`Network init result: ${JSON.stringify(result)}`, NS);
if (result.status !== named_1.EmberStatus.SUCCESS) {
this.waitress.remove(waiter.ID);
logger_1.logger.debug("Failure to leave network", NS);
throw new Error(`Failure to leave network: ${JSON.stringify(result)}`);
}
const response = await waiter.start().promise;
if (response.payload.status !== named_1.EmberStatus.NETWORK_DOWN) {
const msg = `Wrong network status: ${JSON.stringify(response.payload)}`;
logger_1.logger.debug(msg, NS);
throw new Error(msg);
}
return response.payload.status;
}
async setConfigurationValue(configId, value) {
const configName = named_1.EzspConfigId.valueToName(named_1.EzspConfigId, configId);
logger_1.logger.debug(`Set ${configName} = ${value}`, NS);
const ret = await this.execCommand("setConfigurationValue", { configId: configId, value: value });
if (ret.status !== named_1.EmberStatus.SUCCESS) {
logger_1.logger.error(`Command (setConfigurationValue(${configName}, ${value})) returned unexpected state: ${JSON.stringify(ret)}`, NS);
}
}
async getConfigurationValue(configId) {
const configName = named_1.EzspConfigId.valueToName(named_1.EzspConfigId, configId);
logger_1.logger.debug(`Get ${configName}`, NS);
const ret = await this.execCommand("getConfigurationValue", { configId: configId });
if (ret.status !== named_1.EmberStatus.SUCCESS) {
logger_1.logger.error(`Command (getConfigurationValue(${configName})) returned unexpected state: ${JSON.stringify(ret)}`, NS);
}
logger_1.logger.debug(`Got ${configName} = ${ret.value}`, NS);
return ret.value;
}
async getMulticastTableEntry(index) {
const ret = await this.execCommand("getMulticastTableEntry", { index: index });
return ret.value;
}
async setMulticastTableEntry(index, entry) {
const ret = await this.execCommand("setMulticastTableEntry", { index: index, value: entry });
if (ret.status !== named_1.EmberStatus.SUCCESS) {
logger_1.logger.error(`Command (setMulticastTableEntry) returned unexpected state: ${JSON.stringify(ret)}`, NS);
}
return ret.status;
}
async setInitialSecurityState(entry) {
const ret = await this.execCommand("setInitialSecurityState", { state: entry });
if (ret.success !== named_1.EmberStatus.SUCCESS) {
logger_1.logger.error(`Command (setInitialSecurityState) returned unexpected state: ${JSON.stringify(ret)}`, NS);
}
return ret.success;
}
async getCurrentSecurityState() {
const ret = await this.execCommand("getCurrentSecurityState");
if (ret.status !== named_1.EmberStatus.SUCCESS) {
logger_1.logger.error(`Command (getCurrentSecurityState) returned unexpected state: ${JSON.stringify(ret)}`, NS);
}
return ret;
}
async setValue(valueId, value) {
const valueName = t.EzspValueId.valueToName(t.EzspValueId, valueId);
logger_1.logger.debug(`Set ${valueName} = ${value}`, NS);
const ret = await this.execCommand("setValue", { valueId, value });
if (ret.status !== named_1.EmberStatus.SUCCESS) {
logger_1.logger.error(`Command (setValue(${valueName}, ${value})) returned unexpected state: ${JSON.stringify(ret)}`, NS);
}
return ret;
}
async getValue(valueId) {
const valueName = t.EzspValueId.valueToName(t.EzspValueId, valueId);
logger_1.logger.debug(`Get ${valueName}`, NS);
const ret = await this.execCommand("getValue", { valueId });
if (ret.status !== named_1.EmberStatus.SUCCESS) {
logger_1.logger.error(`Command (getValue(${valueName})) returned unexpected state: ${JSON.stringify(ret)}`, NS);
}
logger_1.logger.debug(`Got ${valueName} = ${ret.value}`, NS);
return ret.value;
}
async setPolicy(policyId, value) {
const policyName = named_1.EzspPolicyId.valueToName(named_1.EzspPolicyId, policyId);
logger_1.logger.debug(`Set ${policyName} = ${value}`, NS);
const ret = await this.execCommand("setPolicy", { policyId: policyId, decisionId: value });
if (ret.status !== named_1.EmberStatus.SUCCESS) {
logger_1.logger.error(`Command (setPolicy(${policyName}, ${value})) returned unexpected state: ${JSON.stringify(ret)}`, NS);
}
return ret;
}
async updateConfig() {
const config = this.ezspV < 9 ? CONFIG_IDS_PRE_V9 : CONFIG_IDS_CURRENT;
for (const [confName, value] of config) {
try {
await this.setConfigurationValue(confName, value);
}
catch (error) {
logger_1.logger.error(`setConfigurationValue(${confName}, ${value}) error: ${error}`, NS);
}
}
}
async updatePolicies() {
// Set up the policies for what the NCP should do.
const policies = this.ezspV < 8 ? POLICY_IDS_PRE_V8 : POLICY_IDS_CURRENT;
for (const [policy, value] of policies) {
try {
await this.setPolicy(policy, value);
}
catch (error) {
logger_1.logger.error(`setPolicy(${policy}, ${value}) error: ${error}`, NS);
}
}
}
makeZDOframe(name, params) {
const frmData = new EZSPZDORequestFrameData(name, true, params);
return frmData.serialize();
}
makeFrame(name, params, seq) {
const frmData = new EZSPFrameData(name, true, params);
logger_1.logger.debug(() => `==> ${JSON.stringify(frmData)}`, NS);
const frame = [seq & 255];
if (this.ezspV < 8) {
if (this.ezspV >= 5) {
frame.push(0x00, 0xff, 0x00, frmData.id);
}
else {
frame.push(0x00, frmData.id);
}
}
else {
const cmd_id = t.serialize([frmData.id], [t.uint16_t]);
frame.push(0x00, 0x01, ...cmd_id);
}
return Buffer.concat([Buffer.from(frame), frmData.serialize()]);
}
async execCommand(name, params) {
logger_1.logger.debug(() => `==> ${name}: ${JSON.stringify(params)}`, NS);
if (!this.serialDriver.isInitialized()) {
throw new Error("Connection not initialized");
}
return await this.queue.execute(async () => {
const data = this.makeFrame(name, params, this.cmdSeq);
const waiter = this.waitFor(name, this.cmdSeq);
this.cmdSeq = (this.cmdSeq + 1) & 255;
try {
await this.serialDriver.sendDATA(data);
const response = await waiter.start().promise;
return response.payload;
}
catch {
this.waitress.remove(waiter.ID);
throw new Error(`Failure send ${name}:${JSON.stringify(data)}`);
}
});
}
async formNetwork(params) {
const waiter = this.waitFor("stackStatusHandler", null);
const v = await this.execCommand("formNetwork", { parameters: params });
if (v.status !== named_1.EmberStatus.SUCCESS) {
this.waitress.remove(waiter.ID);
logger_1.logger.error(`Failure forming network: ${JSON.stringify(v)}`, NS);
throw new Error(`Failure forming network: ${JSON.stringify(v)}`);
}
const response = await waiter.start().promise;
if (response.payload.status !== named_1.EmberStatus.NETWORK_UP) {
logger_1.logger.error(`Wrong network status: ${JSON.stringify(response.payload)}`, NS);
throw new Error(`Wrong network status: ${JSON.stringify(response.payload)}`);
}
return response.payload.status;
}
sendUnicast(direct, nwk, apsFrame, seq, data) {
return this.execCommand("sendUnicast", {
type: direct,
indexOrDestination: nwk,
apsFrame: apsFrame,
messageTag: seq,
message: data,
});
}
sendMulticast(apsFrame, seq, data) {
return this.execCommand("sendMulticast", {
apsFrame: apsFrame,
hops: EZSP_DEFAULT_RADIUS,
nonmemberRadius: EZSP_MULTICAST_NON_MEMBER_RADIUS,
messageTag: seq,
message: data,
});
}
async setSourceRouting() {
const res = await this.execCommand("setConcentrator", {
on: true,
concentratorType: named_1.EmberConcentratorType.HIGH_RAM_CONCENTRATOR,
minTime: MTOR_MIN_INTERVAL,
maxTime: MTOR_MAX_INTERVAL,
routeErrorThreshold: MTOR_ROUTE_ERROR_THRESHOLD,
deliveryFailureThreshold: MTOR_DELIVERY_FAIL_THRESHOLD,
maxHops: 0,
});
logger_1.logger.debug(`Set concentrator type: ${JSON.stringify(res)}`, NS);
if (res.status !== named_1.EmberStatus.SUCCESS) {
logger_1.logger.error(`Couldn't set concentrator ${JSON.stringify(res)}`, NS);
}
if (this.ezspV >= 8) {
await this.execCommand("setSourceRouteDiscoveryMode", { mode: 1 });
}
}
sendBroadcast(destination, apsFrame, seq, data) {
return this.execCommand("sendBroadcast", {
destination: destination,
apsFrame: apsFrame,
radius: EZSP_DEFAULT_RADIUS,
messageTag: seq,
message: data,
});
}
waitFor(frameId, sequence, timeout = 10000) {
return this.waitress.waitFor({ frameId, sequence }, timeout);
}
waitressTimeoutFormatter(matcher, timeout) {
return `${JSON.stringify(matcher)} after ${timeout}ms`;
}
waitressValidator(payload, matcher) {
const frameNames = typeof matcher.frameId === "string" ? [matcher.frameId] : commands_1.FRAME_NAMES_BY_ID[matcher.frameId];
return (matcher.sequence == null || payload.sequence === matcher.sequence) && frameNames.includes(payload.frameName);
}
async watchdogHandler() {
logger_1.logger.debug(`Time to watchdog ... ${this.failures}`, NS);
if (this.inResetingProcess) {
logger_1.logger.debug("The reset process is in progress...", NS);
return;
}
try {
await this.execCommand("nop");
}
catch (error) {
logger_1.logger.error(`Watchdog heartbeat timeout ${error}`, NS);
if (!this.inResetingProcess) {
this.failures += 1;
if (this.failures > MAX_WATCHDOG_FAILURES) {
this.failures = 0;
this.emit("reset");
}
}
}
}
}
exports.Ezsp = Ezsp;
//# sourceMappingURL=ezsp.js.map
;