zigbee-herdsman 0.55.0 → 0.55.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +9 -0
- package/dist/adapter/ember/adapter/emberAdapter.d.ts +22 -76
- package/dist/adapter/ember/adapter/emberAdapter.d.ts.map +1 -1
- package/dist/adapter/ember/adapter/emberAdapter.js +209 -368
- package/dist/adapter/ember/adapter/emberAdapter.js.map +1 -1
- package/dist/adapter/ember/consts.d.ts +0 -1
- package/dist/adapter/ember/consts.d.ts.map +1 -1
- package/dist/adapter/ember/consts.js +1 -3
- package/dist/adapter/ember/consts.js.map +1 -1
- package/dist/adapter/ember/ezsp/ezsp.d.ts.map +1 -1
- package/dist/adapter/ember/ezsp/ezsp.js +27 -43
- package/dist/adapter/ember/ezsp/ezsp.js.map +1 -1
- package/dist/adapter/ember/uart/ash.d.ts +2 -0
- package/dist/adapter/ember/uart/ash.d.ts.map +1 -1
- package/dist/adapter/ember/uart/ash.js +5 -7
- package/dist/adapter/ember/uart/ash.js.map +1 -1
- package/dist/adapter/ember/utils/initters.d.ts.map +1 -1
- package/dist/adapter/ember/utils/initters.js +0 -1
- package/dist/adapter/ember/utils/initters.js.map +1 -1
- package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts +1 -0
- package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts.map +1 -1
- package/dist/adapter/z-stack/adapter/zStackAdapter.js +14 -21
- package/dist/adapter/z-stack/adapter/zStackAdapter.js.map +1 -1
- package/package.json +1 -1
|
@@ -26,8 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.EmberAdapter = void 0;
|
|
30
|
-
/* istanbul ignore file */
|
|
29
|
+
exports.EmberAdapter = exports.DEFAULT_APS_OPTIONS = exports.DEFAULT_STACK_CONFIG = void 0;
|
|
31
30
|
const crypto_1 = require("crypto");
|
|
32
31
|
const es6_1 = __importDefault(require("fast-deep-equal/es6"));
|
|
33
32
|
const fs_1 = require("fs");
|
|
@@ -93,10 +92,10 @@ const autoDetectDefinitions = [
|
|
|
93
92
|
* messages with non-conflicting sequence numbers.
|
|
94
93
|
*/
|
|
95
94
|
const APPLICATION_ZDO_SEQUENCE_MASK = 0x7f;
|
|
95
|
+
/* Default radius used for broadcast ZDO requests. uint8_t */
|
|
96
|
+
const ZDO_REQUEST_RADIUS = 0xff;
|
|
96
97
|
/** Current revision of the spec by zigbee alliance supported by Z2M. */
|
|
97
98
|
const CURRENT_ZIGBEE_SPEC_REVISION = 22;
|
|
98
|
-
/** Each scan period is 15.36ms. Scan for at least 200ms (2^4 + 1 periods) to pick up WiFi beacon frames. */
|
|
99
|
-
const ENERGY_SCAN_DURATION = 4;
|
|
100
99
|
/** Oldest supported EZSP version for backups. Don't take the risk to restore a broken network until older backup versions can be investigated. */
|
|
101
100
|
const BACKUP_OLDEST_SUPPORTED_EZSP_VERSION = 12;
|
|
102
101
|
/**
|
|
@@ -116,7 +115,7 @@ const QUEUE_NETWORK_DOWN_DEFER_MSEC = 1500;
|
|
|
116
115
|
* https://github.com/NabuCasa/silabs-firmware/wiki/Zigbee-EmberZNet-NCP-firmware-configuration#skyconnect
|
|
117
116
|
* https://github.com/SiliconLabs/UnifySDK/blob/main/applications/zigbeed/project_files/zigbeed.slcp
|
|
118
117
|
*/
|
|
119
|
-
|
|
118
|
+
exports.DEFAULT_STACK_CONFIG = {
|
|
120
119
|
CONCENTRATOR_RAM_TYPE: 'high',
|
|
121
120
|
CONCENTRATOR_MIN_TIME: 5, // zigpc: 10
|
|
122
121
|
CONCENTRATOR_MAX_TIME: 60, // zigpc: 60
|
|
@@ -140,11 +139,9 @@ const TRUST_CENTER_ADDRESS_CACHE_SIZE = 2;
|
|
|
140
139
|
*
|
|
141
140
|
* Removing `ENABLE_ROUTE_DISCOVERY` leads to devices that won't reconnect/go offline, and various other issues. Keeping it for now.
|
|
142
141
|
*/
|
|
143
|
-
|
|
142
|
+
exports.DEFAULT_APS_OPTIONS = enums_1.EmberApsOption.RETRY | enums_1.EmberApsOption.ENABLE_ROUTE_DISCOVERY | enums_1.EmberApsOption.ENABLE_ADDRESS_DISCOVERY;
|
|
144
143
|
/** Time for a ZDO request to get a callback response. ASH is 2400*6 for ACK timeout. */
|
|
145
144
|
const DEFAULT_ZDO_REQUEST_TIMEOUT = 15000; // msec
|
|
146
|
-
/** Time for a ZCL request to get a callback response. ASH is 2400*6 for ACK timeout. */
|
|
147
|
-
const DEFAULT_ZCL_REQUEST_TIMEOUT = 15000; //msec
|
|
148
145
|
/** Time for a network-related request to get a response (usually via event). */
|
|
149
146
|
const DEFAULT_NETWORK_REQUEST_TIMEOUT = 10000; // nothing on the network to bother requests, should be much faster than this
|
|
150
147
|
/** Time between watchdog counters reading/clearing */
|
|
@@ -177,8 +174,6 @@ class EmberAdapter extends __1.Adapter {
|
|
|
177
174
|
watchdogCountersHandle;
|
|
178
175
|
/** Sequence number used for ZDO requests. static uint8_t */
|
|
179
176
|
zdoRequestSequence;
|
|
180
|
-
/** Default radius used for broadcast ZDO requests. uint8_t */
|
|
181
|
-
zdoRequestRadius;
|
|
182
177
|
interpanLock;
|
|
183
178
|
/**
|
|
184
179
|
* Cached network params to avoid NCP calls. Prevents frequent EZSP transactions.
|
|
@@ -189,9 +184,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
189
184
|
constructor(networkOptions, serialPortOptions, backupPath, adapterOptions) {
|
|
190
185
|
super(networkOptions, serialPortOptions, backupPath, adapterOptions);
|
|
191
186
|
this.stackConfig = this.loadStackConfig();
|
|
192
|
-
|
|
193
|
-
logger_1.logger.debug(`Using delay=${delay}.`, NS);
|
|
194
|
-
this.queue = new utils_1.Queue(typeof this.adapterOptions.concurrent === 'number' ? Math.min(Math.max(this.adapterOptions.concurrent, 1), 32) : 16);
|
|
187
|
+
this.queue = new utils_1.Queue(this.adapterOptions.concurrent || 16); // ORed to avoid 0 (not checked in settings/queue constructor)
|
|
195
188
|
this.oneWaitress = new oneWaitress_1.EmberOneWaitress();
|
|
196
189
|
this.ezsp = new ezsp_1.Ezsp(serialPortOptions);
|
|
197
190
|
this.ezsp.on(ezsp_1.EzspEvents.ZDO_RESPONSE, this.onZDOResponse.bind(this));
|
|
@@ -208,46 +201,46 @@ class EmberAdapter extends __1.Adapter {
|
|
|
208
201
|
try {
|
|
209
202
|
const customConfig = JSON.parse((0, fs_1.readFileSync)(configPath, 'utf8'));
|
|
210
203
|
// set any undefined config to default
|
|
211
|
-
const config = { ...DEFAULT_STACK_CONFIG, ...customConfig };
|
|
204
|
+
const config = { ...exports.DEFAULT_STACK_CONFIG, ...customConfig };
|
|
212
205
|
const inRange = (value, min, max) => (value == undefined || value < min || value > max ? false : true);
|
|
213
206
|
if (!['high', 'low'].includes(config.CONCENTRATOR_RAM_TYPE)) {
|
|
214
|
-
config.CONCENTRATOR_RAM_TYPE = DEFAULT_STACK_CONFIG.CONCENTRATOR_RAM_TYPE;
|
|
207
|
+
config.CONCENTRATOR_RAM_TYPE = exports.DEFAULT_STACK_CONFIG.CONCENTRATOR_RAM_TYPE;
|
|
215
208
|
logger_1.logger.error(`[STACK CONFIG] Invalid CONCENTRATOR_RAM_TYPE, using default.`, NS);
|
|
216
209
|
}
|
|
217
210
|
if (!inRange(config.CONCENTRATOR_MIN_TIME, 1, 60) || config.CONCENTRATOR_MIN_TIME >= config.CONCENTRATOR_MAX_TIME) {
|
|
218
|
-
config.CONCENTRATOR_MIN_TIME = DEFAULT_STACK_CONFIG.CONCENTRATOR_MIN_TIME;
|
|
211
|
+
config.CONCENTRATOR_MIN_TIME = exports.DEFAULT_STACK_CONFIG.CONCENTRATOR_MIN_TIME;
|
|
219
212
|
logger_1.logger.error(`[STACK CONFIG] Invalid CONCENTRATOR_MIN_TIME, using default.`, NS);
|
|
220
213
|
}
|
|
221
214
|
if (!inRange(config.CONCENTRATOR_MAX_TIME, 30, 300) || config.CONCENTRATOR_MAX_TIME <= config.CONCENTRATOR_MIN_TIME) {
|
|
222
|
-
config.CONCENTRATOR_MAX_TIME = DEFAULT_STACK_CONFIG.CONCENTRATOR_MAX_TIME;
|
|
215
|
+
config.CONCENTRATOR_MAX_TIME = exports.DEFAULT_STACK_CONFIG.CONCENTRATOR_MAX_TIME;
|
|
223
216
|
logger_1.logger.error(`[STACK CONFIG] Invalid CONCENTRATOR_MAX_TIME, using default.`, NS);
|
|
224
217
|
}
|
|
225
218
|
if (!inRange(config.CONCENTRATOR_ROUTE_ERROR_THRESHOLD, 1, 100)) {
|
|
226
|
-
config.CONCENTRATOR_ROUTE_ERROR_THRESHOLD = DEFAULT_STACK_CONFIG.CONCENTRATOR_ROUTE_ERROR_THRESHOLD;
|
|
219
|
+
config.CONCENTRATOR_ROUTE_ERROR_THRESHOLD = exports.DEFAULT_STACK_CONFIG.CONCENTRATOR_ROUTE_ERROR_THRESHOLD;
|
|
227
220
|
logger_1.logger.error(`[STACK CONFIG] Invalid CONCENTRATOR_ROUTE_ERROR_THRESHOLD, using default.`, NS);
|
|
228
221
|
}
|
|
229
222
|
if (!inRange(config.CONCENTRATOR_DELIVERY_FAILURE_THRESHOLD, 1, 100)) {
|
|
230
|
-
config.CONCENTRATOR_DELIVERY_FAILURE_THRESHOLD = DEFAULT_STACK_CONFIG.CONCENTRATOR_DELIVERY_FAILURE_THRESHOLD;
|
|
223
|
+
config.CONCENTRATOR_DELIVERY_FAILURE_THRESHOLD = exports.DEFAULT_STACK_CONFIG.CONCENTRATOR_DELIVERY_FAILURE_THRESHOLD;
|
|
231
224
|
logger_1.logger.error(`[STACK CONFIG] Invalid CONCENTRATOR_DELIVERY_FAILURE_THRESHOLD, using default.`, NS);
|
|
232
225
|
}
|
|
233
226
|
if (!inRange(config.CONCENTRATOR_MAX_HOPS, 0, 30)) {
|
|
234
|
-
config.CONCENTRATOR_MAX_HOPS = DEFAULT_STACK_CONFIG.CONCENTRATOR_MAX_HOPS;
|
|
227
|
+
config.CONCENTRATOR_MAX_HOPS = exports.DEFAULT_STACK_CONFIG.CONCENTRATOR_MAX_HOPS;
|
|
235
228
|
logger_1.logger.error(`[STACK CONFIG] Invalid CONCENTRATOR_MAX_HOPS, using default.`, NS);
|
|
236
229
|
}
|
|
237
230
|
if (!inRange(config.MAX_END_DEVICE_CHILDREN, 6, 64)) {
|
|
238
|
-
config.MAX_END_DEVICE_CHILDREN = DEFAULT_STACK_CONFIG.MAX_END_DEVICE_CHILDREN;
|
|
231
|
+
config.MAX_END_DEVICE_CHILDREN = exports.DEFAULT_STACK_CONFIG.MAX_END_DEVICE_CHILDREN;
|
|
239
232
|
logger_1.logger.error(`[STACK CONFIG] Invalid MAX_END_DEVICE_CHILDREN, using default.`, NS);
|
|
240
233
|
}
|
|
241
234
|
if (!inRange(config.TRANSIENT_DEVICE_TIMEOUT, 0, 65535)) {
|
|
242
|
-
config.TRANSIENT_DEVICE_TIMEOUT = DEFAULT_STACK_CONFIG.TRANSIENT_DEVICE_TIMEOUT;
|
|
235
|
+
config.TRANSIENT_DEVICE_TIMEOUT = exports.DEFAULT_STACK_CONFIG.TRANSIENT_DEVICE_TIMEOUT;
|
|
243
236
|
logger_1.logger.error(`[STACK CONFIG] Invalid TRANSIENT_DEVICE_TIMEOUT, using default.`, NS);
|
|
244
237
|
}
|
|
245
238
|
if (!inRange(config.END_DEVICE_POLL_TIMEOUT, 0, 14)) {
|
|
246
|
-
config.END_DEVICE_POLL_TIMEOUT = DEFAULT_STACK_CONFIG.END_DEVICE_POLL_TIMEOUT;
|
|
239
|
+
config.END_DEVICE_POLL_TIMEOUT = exports.DEFAULT_STACK_CONFIG.END_DEVICE_POLL_TIMEOUT;
|
|
247
240
|
logger_1.logger.error(`[STACK CONFIG] Invalid END_DEVICE_POLL_TIMEOUT, using default.`, NS);
|
|
248
241
|
}
|
|
249
242
|
if (!inRange(config.TRANSIENT_KEY_TIMEOUT_S, 0, 65535)) {
|
|
250
|
-
config.TRANSIENT_KEY_TIMEOUT_S = DEFAULT_STACK_CONFIG.TRANSIENT_KEY_TIMEOUT_S;
|
|
243
|
+
config.TRANSIENT_KEY_TIMEOUT_S = exports.DEFAULT_STACK_CONFIG.TRANSIENT_KEY_TIMEOUT_S;
|
|
251
244
|
logger_1.logger.error(`[STACK CONFIG] Invalid TRANSIENT_KEY_TIMEOUT_S, using default.`, NS);
|
|
252
245
|
}
|
|
253
246
|
logger_1.logger.info(`Using stack config ${JSON.stringify(config)}.`, NS);
|
|
@@ -255,7 +248,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
255
248
|
}
|
|
256
249
|
catch { }
|
|
257
250
|
logger_1.logger.info(`Using default stack config.`, NS);
|
|
258
|
-
return DEFAULT_STACK_CONFIG;
|
|
251
|
+
return exports.DEFAULT_STACK_CONFIG;
|
|
259
252
|
}
|
|
260
253
|
/**
|
|
261
254
|
* Emitted from @see Ezsp.ezspStackStatusHandler
|
|
@@ -309,53 +302,57 @@ class EmberAdapter extends __1.Adapter {
|
|
|
309
302
|
* @param status
|
|
310
303
|
*/
|
|
311
304
|
async onMessageSent(status, type, indexOrDestination, apsFrame, messageTag) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
`[apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag}]`, NS);
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
305
|
+
switch (status) {
|
|
306
|
+
case enums_1.SLStatus.ZIGBEE_DELIVERY_FAILED: {
|
|
307
|
+
// no ACK was received from the destination
|
|
308
|
+
switch (type) {
|
|
309
|
+
case enums_1.EmberOutgoingMessageType.BROADCAST:
|
|
310
|
+
case enums_1.EmberOutgoingMessageType.BROADCAST_WITH_ALIAS:
|
|
311
|
+
case enums_1.EmberOutgoingMessageType.MULTICAST:
|
|
312
|
+
case enums_1.EmberOutgoingMessageType.MULTICAST_WITH_ALIAS: {
|
|
313
|
+
// BC/MC not checking for message sent, avoid unnecessary waitress lookups
|
|
314
|
+
logger_1.logger.error(`Delivery of ${enums_1.EmberOutgoingMessageType[type]} failed for '${indexOrDestination}' [apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag}]`, NS);
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
default: {
|
|
318
|
+
// reject any waitress early (don't wait for timeout if we know we're gonna get there eventually)
|
|
319
|
+
this.oneWaitress.deliveryFailedFor(indexOrDestination, apsFrame);
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
328
322
|
}
|
|
323
|
+
break;
|
|
329
324
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
325
|
+
case enums_1.SLStatus.OK: {
|
|
326
|
+
/* istanbul ignore else */
|
|
327
|
+
if (type === enums_1.EmberOutgoingMessageType.MULTICAST &&
|
|
328
|
+
apsFrame.destinationEndpoint === 0xff &&
|
|
329
|
+
apsFrame.groupId < consts_1.EMBER_MIN_BROADCAST_ADDRESS &&
|
|
330
|
+
!this.multicastTable.includes(apsFrame.groupId)) {
|
|
331
|
+
// workaround for devices using multicast for state update (coordinator passthrough)
|
|
332
|
+
const tableIdx = this.multicastTable.length;
|
|
333
|
+
const multicastEntry = {
|
|
334
|
+
multicastId: apsFrame.groupId,
|
|
335
|
+
endpoint: endpoints_1.FIXED_ENDPOINTS[0].endpoint,
|
|
336
|
+
networkIndex: endpoints_1.FIXED_ENDPOINTS[0].networkIndex,
|
|
337
|
+
};
|
|
338
|
+
// set immediately to avoid potential race
|
|
339
|
+
this.multicastTable.push(multicastEntry.multicastId);
|
|
340
|
+
try {
|
|
341
|
+
await this.queue.execute(async () => {
|
|
342
|
+
const status = await this.ezsp.ezspSetMulticastTableEntry(tableIdx, multicastEntry);
|
|
343
|
+
if (status !== enums_1.SLStatus.OK) {
|
|
344
|
+
throw new Error(`Failed to register group '${multicastEntry.multicastId}' in multicast table with status=${enums_1.SLStatus[status]}.`);
|
|
345
|
+
}
|
|
346
|
+
logger_1.logger.debug(`Registered multicast table entry (${tableIdx}): ${JSON.stringify(multicastEntry)}.`, NS);
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
catch (error) {
|
|
350
|
+
// remove to allow retry on next occurrence
|
|
351
|
+
this.multicastTable.splice(tableIdx, 1);
|
|
352
|
+
logger_1.logger.error(error, NS);
|
|
353
|
+
}
|
|
358
354
|
}
|
|
355
|
+
break;
|
|
359
356
|
}
|
|
360
357
|
}
|
|
361
358
|
// shouldn't be any other status
|
|
@@ -423,16 +420,17 @@ class EmberAdapter extends __1.Adapter {
|
|
|
423
420
|
* @param messageContents
|
|
424
421
|
*/
|
|
425
422
|
async onTouchlinkMessage(sourcePanId, sourceAddress, groupId, lastHopLqi, messageContents) {
|
|
423
|
+
const endpoint = endpoints_1.FIXED_ENDPOINTS[0].endpoint;
|
|
426
424
|
const payload = {
|
|
427
425
|
clusterID: Zcl.Clusters.touchlink.ID,
|
|
428
426
|
data: messageContents,
|
|
429
427
|
header: Zcl.Header.fromBuffer(messageContents),
|
|
430
428
|
address: sourceAddress,
|
|
431
|
-
endpoint:
|
|
429
|
+
endpoint: endpoint, // arbitrary since not sent over-the-air
|
|
432
430
|
linkquality: lastHopLqi,
|
|
433
431
|
groupID: groupId,
|
|
434
432
|
wasBroadcast: true, // XXX: since always sent broadcast atm...
|
|
435
|
-
destinationEndpoint:
|
|
433
|
+
destinationEndpoint: endpoint,
|
|
436
434
|
};
|
|
437
435
|
this.oneWaitress.resolveZCL(payload);
|
|
438
436
|
this.emit(events_1.Events.zclPayload, payload);
|
|
@@ -538,7 +536,6 @@ class EmberAdapter extends __1.Adapter {
|
|
|
538
536
|
clearInterval(this.watchdogCountersHandle);
|
|
539
537
|
this.queue.clear();
|
|
540
538
|
this.zdoRequestSequence = 0; // start at 1
|
|
541
|
-
this.zdoRequestRadius = 255;
|
|
542
539
|
this.interpanLock = false;
|
|
543
540
|
this.networkCache = (0, initters_1.initNetworkCache)();
|
|
544
541
|
this.manufacturerCode = DEFAULT_MANUFACTURER_CODE; // will be set in NCP in initEzsp
|
|
@@ -590,7 +587,6 @@ class EmberAdapter extends __1.Adapter {
|
|
|
590
587
|
throw new Error(`Failed to get network parameters with status=${enums_1.SLStatus[status]}.`);
|
|
591
588
|
}
|
|
592
589
|
this.networkCache.parameters = parameters;
|
|
593
|
-
this.networkCache.status = await this.ezsp.ezspNetworkState();
|
|
594
590
|
this.networkCache.eui64 = await this.ezsp.ezspGetEui64();
|
|
595
591
|
logger_1.logger.debug(`[INIT] Network Ready! ${JSON.stringify(this.networkCache)}`, NS);
|
|
596
592
|
this.watchdogCountersHandle = setInterval(this.watchdogCounters.bind(this), WATCHDOG_COUNTERS_FEED_INTERVAL);
|
|
@@ -623,10 +619,6 @@ class EmberAdapter extends __1.Adapter {
|
|
|
623
619
|
*/
|
|
624
620
|
async registerFixedEndpoints() {
|
|
625
621
|
for (const ep of endpoints_1.FIXED_ENDPOINTS) {
|
|
626
|
-
if (ep.networkIndex !== 0x00) {
|
|
627
|
-
logger_1.logger.debug(`Multi-network not currently supported. Skipping endpoint ${JSON.stringify(ep)}.`, NS);
|
|
628
|
-
continue;
|
|
629
|
-
}
|
|
630
622
|
const [epStatus] = await this.ezsp.ezspGetEndpointFlags(ep.endpoint);
|
|
631
623
|
// endpoint not already registered
|
|
632
624
|
if (epStatus !== enums_1.SLStatus.OK) {
|
|
@@ -638,11 +630,11 @@ class EmberAdapter extends __1.Adapter {
|
|
|
638
630
|
logger_1.logger.debug(`Registered endpoint '${ep.endpoint}'.`, NS);
|
|
639
631
|
}
|
|
640
632
|
else {
|
|
641
|
-
throw new Error(`Failed to register endpoint
|
|
633
|
+
throw new Error(`Failed to register endpoint '${ep.endpoint}' with status=${enums_1.SLStatus[status]}.`);
|
|
642
634
|
}
|
|
643
635
|
}
|
|
644
636
|
else {
|
|
645
|
-
logger_1.logger.debug(`Endpoint
|
|
637
|
+
logger_1.logger.debug(`Endpoint '${ep.endpoint}' already registered.`, NS);
|
|
646
638
|
}
|
|
647
639
|
for (const multicastId of ep.multicastIds) {
|
|
648
640
|
const multicastEntry = {
|
|
@@ -652,7 +644,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
652
644
|
};
|
|
653
645
|
const status = await this.ezsp.ezspSetMulticastTableEntry(this.multicastTable.length, multicastEntry);
|
|
654
646
|
if (status !== enums_1.SLStatus.OK) {
|
|
655
|
-
throw new Error(`Failed to register group
|
|
647
|
+
throw new Error(`Failed to register group '${multicastId}' in multicast table with status=${enums_1.SLStatus[status]}.`);
|
|
656
648
|
}
|
|
657
649
|
logger_1.logger.debug(`Registered multicast table entry (${this.multicastTable.length}): ${JSON.stringify(multicastEntry)}.`, NS);
|
|
658
650
|
this.multicastTable.push(multicastEntry.multicastId);
|
|
@@ -668,14 +660,13 @@ class EmberAdapter extends __1.Adapter {
|
|
|
668
660
|
{
|
|
669
661
|
let status = await this.emberSetEzspPolicy(enums_2.EzspPolicyId.TC_KEY_REQUEST_POLICY, enums_2.EzspDecisionId.ALLOW_TC_KEY_REQUESTS_AND_SEND_CURRENT_KEY);
|
|
670
662
|
if (status !== enums_1.SLStatus.OK) {
|
|
671
|
-
throw new Error(`[INIT TC] Failed to set EzspPolicyId TC_KEY_REQUEST_POLICY to ALLOW_TC_KEY_REQUESTS_AND_SEND_CURRENT_KEY
|
|
672
|
-
`with status=${enums_1.SLStatus[status]}.`);
|
|
663
|
+
throw new Error(`[INIT TC] Failed to set EzspPolicyId TC_KEY_REQUEST_POLICY to ALLOW_TC_KEY_REQUESTS_AND_SEND_CURRENT_KEY with status=${enums_1.SLStatus[status]}.`);
|
|
673
664
|
}
|
|
665
|
+
/* istanbul ignore next */
|
|
674
666
|
const appKeyRequestsPolicy = ALLOW_APP_KEY_REQUESTS ? enums_2.EzspDecisionId.ALLOW_APP_KEY_REQUESTS : enums_2.EzspDecisionId.DENY_APP_KEY_REQUESTS;
|
|
675
667
|
status = await this.emberSetEzspPolicy(enums_2.EzspPolicyId.APP_KEY_REQUEST_POLICY, appKeyRequestsPolicy);
|
|
676
668
|
if (status !== enums_1.SLStatus.OK) {
|
|
677
|
-
throw new Error(`[INIT TC] Failed to set EzspPolicyId APP_KEY_REQUEST_POLICY to ${enums_2.EzspDecisionId[appKeyRequestsPolicy]}
|
|
678
|
-
`with status=${enums_1.SLStatus[status]}.`);
|
|
669
|
+
throw new Error(`[INIT TC] Failed to set EzspPolicyId APP_KEY_REQUEST_POLICY to ${enums_2.EzspDecisionId[appKeyRequestsPolicy]} with status=${enums_1.SLStatus[status]}.`);
|
|
679
670
|
}
|
|
680
671
|
status = await this.emberSetJoinPolicy(enums_1.EmberJoinDecision.USE_PRECONFIGURED_KEY);
|
|
681
672
|
if (status !== enums_1.SLStatus.OK) {
|
|
@@ -707,7 +698,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
707
698
|
context.keyIndex = 0;
|
|
708
699
|
const [nkStatus, networkKey] = await this.ezsp.ezspExportKey(context);
|
|
709
700
|
if (nkStatus !== enums_1.SLStatus.OK) {
|
|
710
|
-
throw new Error(`[
|
|
701
|
+
throw new Error(`[INIT TC] Failed to export Network Key with status=${enums_1.SLStatus[nkStatus]}.`);
|
|
711
702
|
}
|
|
712
703
|
// config doesn't match adapter anymore
|
|
713
704
|
if (!networkKey.contents.equals(configNetworkKey)) {
|
|
@@ -757,6 +748,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
757
748
|
switch (action) {
|
|
758
749
|
case NetworkInitAction.FORM_BACKUP: {
|
|
759
750
|
logger_1.logger.info(`[INIT TC] Forming from backup.`, NS);
|
|
751
|
+
/* istanbul ignore next */
|
|
760
752
|
const keyList = backup.devices.map((device) => {
|
|
761
753
|
const octets = Array.from(device.ieeeAddress.reverse());
|
|
762
754
|
return {
|
|
@@ -782,9 +774,6 @@ class EmberAdapter extends __1.Adapter {
|
|
|
782
774
|
logger_1.logger.info(`[INIT TC] Adapter network matches config.`, NS);
|
|
783
775
|
break;
|
|
784
776
|
}
|
|
785
|
-
default: {
|
|
786
|
-
throw new Error(`[INIT TC] Invalid action "${NetworkInitAction[action]}" for final stage.`);
|
|
787
|
-
}
|
|
788
777
|
}
|
|
789
778
|
// can't let frame counter wrap to zero (uint32_t), will force a broadcast after init if getting too close
|
|
790
779
|
if (backup != null && backup.networkKeyInfo.frameCounter > 0xfeeeeeee) {
|
|
@@ -909,6 +898,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
909
898
|
[status, context, plaintextKey, apsKeyMeta] = await this.ezsp.ezspExportLinkKeyByIndex(i);
|
|
910
899
|
logger_1.logger.debug(`[BACKUP] Export link key at index ${i}, status=${enums_1.SLStatus[status]}.`, NS);
|
|
911
900
|
// only include key if we could retrieve one at index and hash it properly
|
|
901
|
+
/* istanbul ignore else */
|
|
912
902
|
if (status === enums_1.SLStatus.OK) {
|
|
913
903
|
// Rather than give the real link key, the backup contains a hashed version of the key.
|
|
914
904
|
// This is done to prevent a compromise of the backup data from compromising the current link keys.
|
|
@@ -937,6 +927,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
937
927
|
* @param backupData
|
|
938
928
|
*/
|
|
939
929
|
async importLinkKeys(backupData) {
|
|
930
|
+
/* istanbul ignore else */
|
|
940
931
|
if (!backupData?.length) {
|
|
941
932
|
return;
|
|
942
933
|
}
|
|
@@ -947,7 +938,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
947
938
|
if (backupData.length > keyTableSize) {
|
|
948
939
|
throw new Error(`[BACKUP] Current key table of ${keyTableSize} is too small to import backup of ${backupData.length}!`);
|
|
949
940
|
}
|
|
950
|
-
const networkStatus = await this.
|
|
941
|
+
const networkStatus = await this.ezsp.ezspNetworkState();
|
|
951
942
|
if (networkStatus !== enums_1.EmberNetworkStatus.NO_NETWORK) {
|
|
952
943
|
throw new Error(`[BACKUP] Cannot import TC data while network is up, networkStatus=${enums_1.EmberNetworkStatus[networkStatus]}.`);
|
|
953
944
|
}
|
|
@@ -959,8 +950,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
959
950
|
? await this.ezsp.ezspEraseKeyTableEntry(i)
|
|
960
951
|
: await this.ezsp.ezspImportLinkKey(i, backupData[i].deviceEui64, backupData[i].key);
|
|
961
952
|
if (status !== enums_1.SLStatus.OK) {
|
|
962
|
-
throw new Error(`[BACKUP] Failed to ${i >= backupData.length ? 'erase' : 'set'} key table entry at index ${i}
|
|
963
|
-
`with status=${enums_1.SLStatus[status]}`);
|
|
953
|
+
throw new Error(`[BACKUP] Failed to ${i >= backupData.length ? 'erase' : 'set'} key table entry at index ${i} with status=${enums_1.SLStatus[status]}.`);
|
|
964
954
|
}
|
|
965
955
|
}
|
|
966
956
|
logger_1.logger.info(`[BACKUP] Imported ${backupData.length} keys.`, NS);
|
|
@@ -1021,24 +1011,13 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1021
1011
|
clearNetworkCache() {
|
|
1022
1012
|
this.networkCache = (0, initters_1.initNetworkCache)();
|
|
1023
1013
|
}
|
|
1024
|
-
/**
|
|
1025
|
-
* Return the current network state.
|
|
1026
|
-
* This call caches the results on the host to prevent frequent EZSP transactions.
|
|
1027
|
-
* Check against UNKNOWN_NETWORK_STATE for validity.
|
|
1028
|
-
*/
|
|
1029
|
-
async emberNetworkState() {
|
|
1030
|
-
if (this.networkCache.status === consts_1.UNKNOWN_NETWORK_STATE) {
|
|
1031
|
-
const networkStatus = await this.ezsp.ezspNetworkState();
|
|
1032
|
-
this.networkCache.status = networkStatus;
|
|
1033
|
-
}
|
|
1034
|
-
return this.networkCache.status;
|
|
1035
|
-
}
|
|
1036
1014
|
/**
|
|
1037
1015
|
* Return the EUI 64 of the local node
|
|
1038
1016
|
* This call caches the results on the host to prevent frequent EZSP transactions.
|
|
1039
1017
|
* Check against BLANK_EUI64 for validity.
|
|
1040
1018
|
*/
|
|
1041
1019
|
async emberGetEui64() {
|
|
1020
|
+
/* istanbul ignore else */
|
|
1042
1021
|
if (this.networkCache.eui64 === ZSpec.BLANK_EUI64) {
|
|
1043
1022
|
this.networkCache.eui64 = await this.ezsp.ezspGetEui64();
|
|
1044
1023
|
}
|
|
@@ -1050,13 +1029,14 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1050
1029
|
* Check against INVALID_PAN_ID for validity.
|
|
1051
1030
|
*/
|
|
1052
1031
|
async emberGetPanId() {
|
|
1032
|
+
/* istanbul ignore else */
|
|
1053
1033
|
if (this.networkCache.parameters.panId === ZSpec.INVALID_PAN_ID) {
|
|
1054
1034
|
const [status, , parameters] = await this.ezsp.ezspGetNetworkParameters();
|
|
1055
1035
|
if (status === enums_1.SLStatus.OK) {
|
|
1056
1036
|
this.networkCache.parameters = parameters;
|
|
1057
1037
|
}
|
|
1058
1038
|
else {
|
|
1059
|
-
|
|
1039
|
+
throw new Error(`Failed to get PAN ID (via network parameters) with status=${enums_1.SLStatus[status]}.`);
|
|
1060
1040
|
}
|
|
1061
1041
|
}
|
|
1062
1042
|
return this.networkCache.parameters.panId;
|
|
@@ -1067,13 +1047,14 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1067
1047
|
* Check against BLANK_EXTENDED_PAN_ID for validity.
|
|
1068
1048
|
*/
|
|
1069
1049
|
async emberGetExtendedPanId() {
|
|
1050
|
+
/* istanbul ignore else */
|
|
1070
1051
|
if ((0, es6_1.default)(this.networkCache.parameters.extendedPanId, ZSpec.BLANK_EXTENDED_PAN_ID)) {
|
|
1071
1052
|
const [status, , parameters] = await this.ezsp.ezspGetNetworkParameters();
|
|
1072
1053
|
if (status === enums_1.SLStatus.OK) {
|
|
1073
1054
|
this.networkCache.parameters = parameters;
|
|
1074
1055
|
}
|
|
1075
1056
|
else {
|
|
1076
|
-
|
|
1057
|
+
throw new Error(`Failed to get Extended PAN ID (via network parameters) with status=${enums_1.SLStatus[status]}.`);
|
|
1077
1058
|
}
|
|
1078
1059
|
}
|
|
1079
1060
|
return this.networkCache.parameters.extendedPanId;
|
|
@@ -1084,27 +1065,18 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1084
1065
|
* Check against INVALID_RADIO_CHANNEL for validity.
|
|
1085
1066
|
*/
|
|
1086
1067
|
async emberGetRadioChannel() {
|
|
1068
|
+
/* istanbul ignore else */
|
|
1087
1069
|
if (this.networkCache.parameters.radioChannel === consts_1.INVALID_RADIO_CHANNEL) {
|
|
1088
1070
|
const [status, , parameters] = await this.ezsp.ezspGetNetworkParameters();
|
|
1089
1071
|
if (status === enums_1.SLStatus.OK) {
|
|
1090
1072
|
this.networkCache.parameters = parameters;
|
|
1091
1073
|
}
|
|
1092
1074
|
else {
|
|
1093
|
-
|
|
1075
|
+
throw new Error(`Failed to get radio channel (via network parameters) with status=${enums_1.SLStatus[status]}.`);
|
|
1094
1076
|
}
|
|
1095
1077
|
}
|
|
1096
1078
|
return this.networkCache.parameters.radioChannel;
|
|
1097
1079
|
}
|
|
1098
|
-
// queued
|
|
1099
|
-
async emberStartEnergyScan() {
|
|
1100
|
-
return this.queue.execute(async () => {
|
|
1101
|
-
const status = await this.ezsp.ezspStartScan(enums_1.EzspNetworkScanType.ENERGY_SCAN, consts_1.EMBER_ALL_802_15_4_CHANNELS_MASK, ENERGY_SCAN_DURATION);
|
|
1102
|
-
if (status !== enums_1.SLStatus.OK) {
|
|
1103
|
-
throw new Error(`Failed energy scan request with status=${enums_1.SLStatus[status]}.`);
|
|
1104
|
-
}
|
|
1105
|
-
// TODO: result in logs only atm, since UI doesn't support it
|
|
1106
|
-
});
|
|
1107
|
-
}
|
|
1108
1080
|
//---- END Cache-enabled EZSP wrappers
|
|
1109
1081
|
//---- START EZSP wrappers
|
|
1110
1082
|
/**
|
|
@@ -1131,8 +1103,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1131
1103
|
logger_1.logger.info(`Adapter EZSP protocol version (${ncpEzspProtocolVer}) lower than Host. Switched.`, NS);
|
|
1132
1104
|
}
|
|
1133
1105
|
else {
|
|
1134
|
-
throw new Error(`Adapter EZSP protocol version (${ncpEzspProtocolVer}) is not supported
|
|
1135
|
-
`by Host [${consts_2.EZSP_MIN_PROTOCOL_VERSION}-${consts_2.EZSP_PROTOCOL_VERSION}].`);
|
|
1106
|
+
throw new Error(`Adapter EZSP protocol version (${ncpEzspProtocolVer}) is not supported by Host [${consts_2.EZSP_MIN_PROTOCOL_VERSION}-${consts_2.EZSP_PROTOCOL_VERSION}].`);
|
|
1136
1107
|
}
|
|
1137
1108
|
this.ezsp.setProtocolVersion(ncpEzspProtocolVer);
|
|
1138
1109
|
logger_1.logger.debug(`Adapter info: EZSPVersion=${ncpEzspProtocolVer} StackType=${ncpStackType} StackVersion=${ncpStackVer}`, NS);
|
|
@@ -1161,10 +1132,9 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1161
1132
|
*/
|
|
1162
1133
|
async emberSetEzspConfigValue(configId, value) {
|
|
1163
1134
|
const status = await this.ezsp.ezspSetConfigurationValue(configId, value);
|
|
1164
|
-
logger_1.logger.debug(`[EzspConfigId] SET
|
|
1135
|
+
logger_1.logger.debug(`[EzspConfigId] SET '${enums_2.EzspConfigId[configId]}' TO '${value}' with status=${enums_1.SLStatus[status]}.`, NS);
|
|
1165
1136
|
if (status !== enums_1.SLStatus.OK) {
|
|
1166
|
-
logger_1.logger.info(`[EzspConfigId] Failed to SET
|
|
1167
|
-
`Firmware value will be used instead.`, NS);
|
|
1137
|
+
logger_1.logger.info(`[EzspConfigId] Failed to SET '${enums_2.EzspConfigId[configId]}' TO '${value}' with status=${enums_1.SLStatus[status]}. Firmware value will be used instead.`, NS);
|
|
1168
1138
|
}
|
|
1169
1139
|
return status;
|
|
1170
1140
|
}
|
|
@@ -1177,7 +1147,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1177
1147
|
*/
|
|
1178
1148
|
async emberSetEzspValue(valueId, valueLength, value) {
|
|
1179
1149
|
const status = await this.ezsp.ezspSetValue(valueId, valueLength, value);
|
|
1180
|
-
logger_1.logger.debug(`[EzspValueId] SET
|
|
1150
|
+
logger_1.logger.debug(`[EzspValueId] SET '${enums_2.EzspValueId[valueId]}' TO '${value}' with status=${enums_1.SLStatus[status]}.`, NS);
|
|
1181
1151
|
return status;
|
|
1182
1152
|
}
|
|
1183
1153
|
/**
|
|
@@ -1188,65 +1158,9 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1188
1158
|
*/
|
|
1189
1159
|
async emberSetEzspPolicy(policyId, decisionId) {
|
|
1190
1160
|
const status = await this.ezsp.ezspSetPolicy(policyId, decisionId);
|
|
1191
|
-
logger_1.logger.debug(`[EzspPolicyId] SET
|
|
1161
|
+
logger_1.logger.debug(`[EzspPolicyId] SET '${enums_2.EzspPolicyId[policyId]}' TO '${decisionId}' with status=${enums_1.SLStatus[status]}.`, NS);
|
|
1192
1162
|
return status;
|
|
1193
1163
|
}
|
|
1194
|
-
/**
|
|
1195
|
-
* Here we convert the normal Ember AES hash call to the specialized EZSP call.
|
|
1196
|
-
* This came about because we cannot pass a block of data that is
|
|
1197
|
-
* both input and output into EZSP. The block must be broken up into two
|
|
1198
|
-
* elements. We unify the two pieces here to make it invisible to the users.
|
|
1199
|
-
* @param context EmberAesMmoHashContext *
|
|
1200
|
-
* @param finalize
|
|
1201
|
-
* @param data uint8_t * Expected of valid length (as in, not larger alloc)
|
|
1202
|
-
* @returns status
|
|
1203
|
-
* @returns result context or null
|
|
1204
|
-
*/
|
|
1205
|
-
async aesMmoHash(context, finalize, data) {
|
|
1206
|
-
if (data.length > 255) {
|
|
1207
|
-
// will be caught by request queue and rejected internally.
|
|
1208
|
-
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_CALL);
|
|
1209
|
-
}
|
|
1210
|
-
const [status, reContext] = await this.ezsp.ezspAesMmoHash(context, finalize, data);
|
|
1211
|
-
return [status, reContext];
|
|
1212
|
-
}
|
|
1213
|
-
/**
|
|
1214
|
-
* This routine processes the passed chunk of data and updates
|
|
1215
|
-
* the hash calculation based on it. The data passed in MUST
|
|
1216
|
-
* have a length that is a multiple of 16.
|
|
1217
|
-
*
|
|
1218
|
-
* @param context EmberAesMmoHashContext* A pointer to the location of the hash context to update.
|
|
1219
|
-
* @param data const uint8_t* A pointer to the location of the data to hash.
|
|
1220
|
-
*
|
|
1221
|
-
* @returns An ::SLStatus value indicating EMBER_SUCCESS if the hash was
|
|
1222
|
-
* calculated successfully. EMBER_INVALID_CALL if the block size is not a
|
|
1223
|
-
* multiple of 16 bytes, and EMBER_INDEX_OUT_OF_RANGE is returned when the
|
|
1224
|
-
* data exceeds the maximum limits of the hash function.
|
|
1225
|
-
* @returns result context or null
|
|
1226
|
-
*/
|
|
1227
|
-
async emberAesMmoHashUpdate(context, data) {
|
|
1228
|
-
return this.aesMmoHash(context, false /*finalize?*/, data);
|
|
1229
|
-
}
|
|
1230
|
-
/**
|
|
1231
|
-
* This routine processes the passed chunk of data (if non-NULL)
|
|
1232
|
-
* and update the hash context that is passed in. In then performs
|
|
1233
|
-
* the final calculations on the hash and returns the final answer
|
|
1234
|
-
* in the result parameter of the ::EmberAesMmoHashContext structure.
|
|
1235
|
-
* The length of the data passed in may be any value, it does not have
|
|
1236
|
-
* to be a multiple of 16.
|
|
1237
|
-
*
|
|
1238
|
-
* @param context EmberAesMmoHashContext * A pointer to the location of the hash context to finalize.
|
|
1239
|
-
* @param data uint8_t * A pointer to the location of data to hash. May be NULL.
|
|
1240
|
-
*
|
|
1241
|
-
* @returns An ::SLStatus value indicating EMBER_SUCCESS if the hash was
|
|
1242
|
-
* calculated successfully. EMBER_INVALID_CALL if the block size is not a
|
|
1243
|
-
* multiple of 16 bytes, and EMBER_INDEX_OUT_OF_RANGE is returned when the
|
|
1244
|
-
* data exceeds the maximum limits of the hash function.
|
|
1245
|
-
* @returns result context or null
|
|
1246
|
-
*/
|
|
1247
|
-
async emberAesMmoHashFinal(context, data) {
|
|
1248
|
-
return this.aesMmoHash(context, true /*finalize?*/, data);
|
|
1249
|
-
}
|
|
1250
1164
|
/**
|
|
1251
1165
|
* This is a convenience method when the hash data is less than 255
|
|
1252
1166
|
* bytes. It inits, updates, and finalizes the hash in one function call.
|
|
@@ -1261,7 +1175,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1261
1175
|
*/
|
|
1262
1176
|
async emberAesHashSimple(data) {
|
|
1263
1177
|
const context = (0, initters_1.aesMmoHashInit)();
|
|
1264
|
-
const [status, reContext] = await this.
|
|
1178
|
+
const [status, reContext] = await this.ezsp.ezspAesMmoHash(context, true, data);
|
|
1265
1179
|
return [status, reContext?.result];
|
|
1266
1180
|
}
|
|
1267
1181
|
/**
|
|
@@ -1285,7 +1199,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1285
1199
|
if (broadcastMgmtPermitJoin) {
|
|
1286
1200
|
// `authentication`: TC significance always 1 (zb specs)
|
|
1287
1201
|
const zdoPayload = buffaloZdo_1.BuffaloZdo.buildPermitJoining(duration, 1, []);
|
|
1288
|
-
[status, apsFrame, messageTag] = await this.sendZDORequest(ZSpec.BroadcastAddress.DEFAULT, Zdo.ClusterId.PERMIT_JOINING_REQUEST, zdoPayload, DEFAULT_APS_OPTIONS);
|
|
1202
|
+
[status, apsFrame, messageTag] = await this.sendZDORequest(ZSpec.BroadcastAddress.DEFAULT, Zdo.ClusterId.PERMIT_JOINING_REQUEST, zdoPayload, exports.DEFAULT_APS_OPTIONS);
|
|
1289
1203
|
}
|
|
1290
1204
|
return [status, apsFrame, messageTag];
|
|
1291
1205
|
}
|
|
@@ -1296,37 +1210,24 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1296
1210
|
*/
|
|
1297
1211
|
async emberSetJoinPolicy(decision) {
|
|
1298
1212
|
let policy = enums_2.EzspDecisionBitmask.DEFAULT_CONFIGURATION;
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1213
|
+
switch (decision) {
|
|
1214
|
+
case enums_1.EmberJoinDecision.USE_PRECONFIGURED_KEY: {
|
|
1215
|
+
policy = enums_2.EzspDecisionBitmask.ALLOW_JOINS | enums_2.EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS;
|
|
1216
|
+
break;
|
|
1217
|
+
}
|
|
1218
|
+
case enums_1.EmberJoinDecision.ALLOW_REJOINS_ONLY: {
|
|
1219
|
+
policy = enums_2.EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS;
|
|
1220
|
+
break;
|
|
1221
|
+
}
|
|
1222
|
+
/*case EmberJoinDecision.SEND_KEY_IN_THE_CLEAR: {
|
|
1223
|
+
policy = EzspDecisionBitmask.ALLOW_JOINS | EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS | EzspDecisionBitmask.SEND_KEY_IN_CLEAR;
|
|
1224
|
+
break;
|
|
1225
|
+
}*/
|
|
1307
1226
|
}
|
|
1308
1227
|
return this.emberSetEzspPolicy(enums_2.EzspPolicyId.TRUST_CENTER_POLICY, policy);
|
|
1309
1228
|
}
|
|
1310
1229
|
//---- END EZSP wrappers
|
|
1311
1230
|
//---- START Ember ZDO
|
|
1312
|
-
/**
|
|
1313
|
-
* ZDO
|
|
1314
|
-
* Change the default radius for broadcast ZDO requests
|
|
1315
|
-
*
|
|
1316
|
-
* @param radius uint8_t The radius to be used for future ZDO request broadcasts.
|
|
1317
|
-
*/
|
|
1318
|
-
setZDORequestRadius(radius) {
|
|
1319
|
-
this.zdoRequestRadius = radius;
|
|
1320
|
-
}
|
|
1321
|
-
/**
|
|
1322
|
-
* ZDO
|
|
1323
|
-
* Retrieve the default radius for broadcast ZDO requests
|
|
1324
|
-
*
|
|
1325
|
-
* @return uint8_t The radius to be used for future ZDO request broadcasts.
|
|
1326
|
-
*/
|
|
1327
|
-
getZDORequestRadius() {
|
|
1328
|
-
return this.zdoRequestRadius;
|
|
1329
|
-
}
|
|
1330
1231
|
/**
|
|
1331
1232
|
* ZDO
|
|
1332
1233
|
* Get the next device request sequence number.
|
|
@@ -1352,9 +1253,6 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1352
1253
|
* @returns messageTag The tag passed to ezspSend${x} function.
|
|
1353
1254
|
*/
|
|
1354
1255
|
async sendZDORequest(destination, clusterId, messageContents, options) {
|
|
1355
|
-
if (messageContents.length > consts_2.EZSP_MAX_FRAME_LENGTH) {
|
|
1356
|
-
return [enums_1.SLStatus.MESSAGE_TOO_LONG, null, null];
|
|
1357
|
-
}
|
|
1358
1256
|
const messageTag = this.nextZDORequestSequence();
|
|
1359
1257
|
messageContents[0] = messageTag;
|
|
1360
1258
|
const apsFrame = {
|
|
@@ -1369,18 +1267,16 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1369
1267
|
if (destination === ZSpec.BroadcastAddress.DEFAULT ||
|
|
1370
1268
|
destination === ZSpec.BroadcastAddress.RX_ON_WHEN_IDLE ||
|
|
1371
1269
|
destination === ZSpec.BroadcastAddress.SLEEPY) {
|
|
1372
|
-
logger_1.logger.debug(`~~~> [ZDO ${Zdo.ClusterId[clusterId]} BROADCAST to=${destination} messageTag=${messageTag}
|
|
1373
|
-
`messageContents=${messageContents.toString('hex')}]`, NS);
|
|
1270
|
+
logger_1.logger.debug(`~~~> [ZDO ${Zdo.ClusterId[clusterId]} BROADCAST to=${destination} messageTag=${messageTag} messageContents=${messageContents.toString('hex')}]`, NS);
|
|
1374
1271
|
const [status, apsSequence] = await this.ezsp.ezspSendBroadcast(ZSpec.NULL_NODE_ID, // alias
|
|
1375
1272
|
destination, 0, // nwkSequence
|
|
1376
|
-
apsFrame,
|
|
1273
|
+
apsFrame, ZDO_REQUEST_RADIUS, messageTag, messageContents);
|
|
1377
1274
|
apsFrame.sequence = apsSequence;
|
|
1378
1275
|
logger_1.logger.debug(`~~~> [SENT ZDO type=BROADCAST apsSequence=${apsSequence} messageTag=${messageTag} status=${enums_1.SLStatus[status]}`, NS);
|
|
1379
1276
|
return [status, apsFrame, messageTag];
|
|
1380
1277
|
}
|
|
1381
1278
|
else {
|
|
1382
|
-
logger_1.logger.debug(`~~~> [ZDO ${Zdo.ClusterId[clusterId]} UNICAST to=${destination} messageTag=${messageTag}
|
|
1383
|
-
`messageContents=${messageContents.toString('hex')}]`, NS);
|
|
1279
|
+
logger_1.logger.debug(`~~~> [ZDO ${Zdo.ClusterId[clusterId]} UNICAST to=${destination} messageTag=${messageTag} messageContents=${messageContents.toString('hex')}]`, NS);
|
|
1384
1280
|
const [status, apsSequence] = await this.ezsp.ezspSendUnicast(enums_1.EmberOutgoingMessageType.DIRECT, destination, apsFrame, messageTag, messageContents);
|
|
1385
1281
|
apsFrame.sequence = apsSequence;
|
|
1386
1282
|
logger_1.logger.debug(`~~~> [SENT ZDO type=DIRECT apsSequence=${apsSequence} messageTag=${messageTag} status=${enums_1.SLStatus[status]}`, NS);
|
|
@@ -1389,6 +1285,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1389
1285
|
}
|
|
1390
1286
|
//---- END Ember ZDO
|
|
1391
1287
|
//-- START Adapter implementation
|
|
1288
|
+
/* istanbul ignore next */
|
|
1392
1289
|
static async isValidPath(path) {
|
|
1393
1290
|
// For TCP paths we cannot get device information, therefore we cannot validate it.
|
|
1394
1291
|
if (socketPortUtils_1.default.isTcpPath(path)) {
|
|
@@ -1402,6 +1299,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1402
1299
|
return false;
|
|
1403
1300
|
}
|
|
1404
1301
|
}
|
|
1302
|
+
/* istanbul ignore next */
|
|
1405
1303
|
static async autoDetectPath() {
|
|
1406
1304
|
const paths = await serialPortUtils_1.default.find(autoDetectDefinitions);
|
|
1407
1305
|
paths.sort((a, b) => (a < b ? -1 : 1));
|
|
@@ -1444,9 +1342,8 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1444
1342
|
return { type: `EmberZNet`, meta: this.version };
|
|
1445
1343
|
}
|
|
1446
1344
|
// queued
|
|
1447
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1448
1345
|
async reset(type) {
|
|
1449
|
-
|
|
1346
|
+
throw new Error(`Not supported '${type}'.`);
|
|
1450
1347
|
// NOTE: although this function is legacy atm, a couple of new untested EZSP functions that could also prove useful:
|
|
1451
1348
|
// this.ezsp.ezspTokenFactoryReset(true/*excludeOutgoingFC*/, true/*excludeBootCounter*/);
|
|
1452
1349
|
// this.ezsp.ezspResetNode()
|
|
@@ -1473,6 +1370,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1473
1370
|
if (!netKeyInfo.networkKeySet) {
|
|
1474
1371
|
throw new Error(`[BACKUP] No network key set.`);
|
|
1475
1372
|
}
|
|
1373
|
+
/* istanbul ignore next */
|
|
1476
1374
|
const keyList = ALLOW_APP_KEY_REQUESTS ? await this.exportLinkKeys() : [];
|
|
1477
1375
|
let context = (0, initters_1.initSecurityManagerContext)();
|
|
1478
1376
|
context.coreKeyType = enums_1.SecManKeyType.TC_LINK;
|
|
@@ -1492,7 +1390,9 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1492
1390
|
networkOptions: {
|
|
1493
1391
|
panId: netParams.panId, // uint16_t
|
|
1494
1392
|
extendedPanId: Buffer.from(netParams.extendedPanId),
|
|
1495
|
-
channelList: zbChannels
|
|
1393
|
+
channelList: zbChannels
|
|
1394
|
+
.map(/* istanbul ignore next */ (c) => ((2 ** c) & netParams.channels ? c : null))
|
|
1395
|
+
.filter((x) => x),
|
|
1496
1396
|
networkKey: networkKey.contents,
|
|
1497
1397
|
networkKeyDistribute: false,
|
|
1498
1398
|
},
|
|
@@ -1504,7 +1404,8 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1504
1404
|
securityLevel: consts_1.SECURITY_LEVEL_Z3,
|
|
1505
1405
|
networkUpdateId: netParams.nwkUpdateId,
|
|
1506
1406
|
coordinatorIeeeAddress: Buffer.from(this.networkCache.eui64.substring(2) /*take out 0x*/, 'hex').reverse(),
|
|
1507
|
-
devices: keyList.map(
|
|
1407
|
+
devices: keyList.map(
|
|
1408
|
+
/* istanbul ignore next */ (key) => ({
|
|
1508
1409
|
networkAddress: null, // not used for restore, no reason to make NCP calls for nothing
|
|
1509
1410
|
ieeeAddress: Buffer.from(key.deviceEui64.substring(2) /*take out 0x*/, 'hex').reverse(),
|
|
1510
1411
|
isDirectChild: false, // not used
|
|
@@ -1544,36 +1445,16 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1544
1445
|
return this.queue.execute(async () => {
|
|
1545
1446
|
this.checkInterpanLock();
|
|
1546
1447
|
const zdoPayload = buffaloZdo_1.BuffaloZdo.buildChannelChangeRequest(newChannel, null);
|
|
1547
|
-
const [status] = await this.sendZDORequest(ZSpec.BroadcastAddress.SLEEPY, Zdo.ClusterId.NWK_UPDATE_REQUEST, zdoPayload, DEFAULT_APS_OPTIONS);
|
|
1448
|
+
const [status] = await this.sendZDORequest(ZSpec.BroadcastAddress.SLEEPY, Zdo.ClusterId.NWK_UPDATE_REQUEST, zdoPayload, exports.DEFAULT_APS_OPTIONS);
|
|
1548
1449
|
if (status !== enums_1.SLStatus.OK) {
|
|
1549
|
-
throw new Error(`[ZDO] Failed broadcast channel change to
|
|
1450
|
+
throw new Error(`[ZDO] Failed broadcast channel change to '${newChannel}' with status=${enums_1.SLStatus[status]}.`);
|
|
1550
1451
|
}
|
|
1551
1452
|
await this.oneWaitress.startWaitingForEvent({ eventName: oneWaitress_1.OneWaitressEvents.STACK_STATUS_CHANNEL_CHANGED }, DEFAULT_NETWORK_REQUEST_TIMEOUT * 2, // observed to ~9sec
|
|
1552
1453
|
'[ZDO] Change Channel');
|
|
1553
1454
|
});
|
|
1554
1455
|
}
|
|
1555
1456
|
// queued
|
|
1556
|
-
async scanChannels(networkAddress, channels, duration, count) {
|
|
1557
|
-
return this.queue.execute(async () => {
|
|
1558
|
-
this.checkInterpanLock();
|
|
1559
|
-
const zdoPayload = buffaloZdo_1.BuffaloZdo.buildScanChannelsRequest(channels, duration, count);
|
|
1560
|
-
const [status, apsFrame] = await this.sendZDORequest(networkAddress, Zdo.ClusterId.NWK_UPDATE_REQUEST, zdoPayload, DEFAULT_APS_OPTIONS);
|
|
1561
|
-
if (status !== enums_1.SLStatus.OK) {
|
|
1562
|
-
throw new Error(`[ZDO] Failed to scan channels '${channels}' on '${networkAddress} with status=${enums_1.SLStatus[status]}.`);
|
|
1563
|
-
}
|
|
1564
|
-
const result = await this.oneWaitress.startWaitingFor({
|
|
1565
|
-
target: networkAddress,
|
|
1566
|
-
apsFrame,
|
|
1567
|
-
responseClusterId: Zdo.ClusterId.NWK_UPDATE_RESPONSE,
|
|
1568
|
-
}, DEFAULT_ZDO_REQUEST_TIMEOUT + (((2 ** duration + 1) * (16 * 960)) / 1000) * count * channels.length); // time for scan
|
|
1569
|
-
return result;
|
|
1570
|
-
});
|
|
1571
|
-
}
|
|
1572
|
-
// queued
|
|
1573
1457
|
async setTransmitPower(value) {
|
|
1574
|
-
if (typeof value !== 'number') {
|
|
1575
|
-
throw new Error(`Tried to set transmit power to non-number. Value ${value} of type ${typeof value}.`);
|
|
1576
|
-
}
|
|
1577
1458
|
return this.queue.execute(async () => {
|
|
1578
1459
|
const status = await this.ezsp.ezspSetRadioPower(value);
|
|
1579
1460
|
if (status !== enums_1.SLStatus.OK) {
|
|
@@ -1583,9 +1464,6 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1583
1464
|
}
|
|
1584
1465
|
// queued
|
|
1585
1466
|
async addInstallCode(ieeeAddress, key) {
|
|
1586
|
-
if (!key) {
|
|
1587
|
-
throw new Error(`[ADD INSTALL CODE] Failed for '${ieeeAddress}'; no code given.`);
|
|
1588
|
-
}
|
|
1589
1467
|
// codes with CRC, check CRC before sending to NCP, otherwise let NCP handle
|
|
1590
1468
|
if (consts_1.EMBER_INSTALL_CODE_SIZES.indexOf(key.length) !== -1) {
|
|
1591
1469
|
// Reverse the bits in a byte (uint8_t)
|
|
@@ -1640,7 +1518,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1640
1518
|
},
|
|
1641
1519
|
zclSequence: transactionSequenceNumber,
|
|
1642
1520
|
commandIdentifier,
|
|
1643
|
-
}, timeout
|
|
1521
|
+
}, timeout);
|
|
1644
1522
|
return {
|
|
1645
1523
|
cancel: () => this.oneWaitress.remove(waiter.id),
|
|
1646
1524
|
promise: waiter.start().promise,
|
|
@@ -1670,7 +1548,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1670
1548
|
await this.ezsp.ezspClearTransientLinkKeys();
|
|
1671
1549
|
const setJPstatus = await this.emberSetJoinPolicy(enums_1.EmberJoinDecision.ALLOW_REJOINS_ONLY);
|
|
1672
1550
|
if (setJPstatus !== enums_1.SLStatus.OK) {
|
|
1673
|
-
throw new Error(`[ZDO] Failed set join policy
|
|
1551
|
+
throw new Error(`[ZDO] Failed set join policy with status=${enums_1.SLStatus[setJPstatus]}.`);
|
|
1674
1552
|
}
|
|
1675
1553
|
}
|
|
1676
1554
|
};
|
|
@@ -1681,9 +1559,9 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1681
1559
|
await preJoining();
|
|
1682
1560
|
// `authentication`: TC significance always 1 (zb specs)
|
|
1683
1561
|
const zdoPayload = buffaloZdo_1.BuffaloZdo.buildPermitJoining(seconds, 1, []);
|
|
1684
|
-
const [status, apsFrame] = await this.sendZDORequest(networkAddress, Zdo.ClusterId.PERMIT_JOINING_REQUEST, zdoPayload, DEFAULT_APS_OPTIONS);
|
|
1562
|
+
const [status, apsFrame] = await this.sendZDORequest(networkAddress, Zdo.ClusterId.PERMIT_JOINING_REQUEST, zdoPayload, exports.DEFAULT_APS_OPTIONS);
|
|
1685
1563
|
if (status !== enums_1.SLStatus.OK) {
|
|
1686
|
-
throw new Error(`[ZDO] Failed permit joining request for
|
|
1564
|
+
throw new Error(`[ZDO] Failed permit joining request for '${networkAddress}' with status=${enums_1.SLStatus[status]}.`);
|
|
1687
1565
|
}
|
|
1688
1566
|
await this.oneWaitress.startWaitingFor({
|
|
1689
1567
|
target: networkAddress,
|
|
@@ -1729,9 +1607,9 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1729
1607
|
const neighbors = [];
|
|
1730
1608
|
const request = async (startIndex) => {
|
|
1731
1609
|
const zdoPayload = buffaloZdo_1.BuffaloZdo.buildLqiTableRequest(startIndex);
|
|
1732
|
-
const [status, apsFrame] = await this.sendZDORequest(networkAddress, Zdo.ClusterId.LQI_TABLE_REQUEST, zdoPayload, DEFAULT_APS_OPTIONS);
|
|
1610
|
+
const [status, apsFrame] = await this.sendZDORequest(networkAddress, Zdo.ClusterId.LQI_TABLE_REQUEST, zdoPayload, exports.DEFAULT_APS_OPTIONS);
|
|
1733
1611
|
if (status !== enums_1.SLStatus.OK) {
|
|
1734
|
-
throw new Error(`[ZDO] Failed LQI request for
|
|
1612
|
+
throw new Error(`[ZDO] Failed LQI request for '${networkAddress}' (index '${startIndex}') with status=${enums_1.SLStatus[status]}.`);
|
|
1735
1613
|
}
|
|
1736
1614
|
const result = await this.oneWaitress.startWaitingFor({
|
|
1737
1615
|
target: networkAddress,
|
|
@@ -1766,9 +1644,9 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1766
1644
|
const table = [];
|
|
1767
1645
|
const request = async (startIndex) => {
|
|
1768
1646
|
const zdoPayload = buffaloZdo_1.BuffaloZdo.buildRoutingTableRequest(startIndex);
|
|
1769
|
-
const [status, apsFrame] = await this.sendZDORequest(networkAddress, Zdo.ClusterId.ROUTING_TABLE_REQUEST, zdoPayload, DEFAULT_APS_OPTIONS);
|
|
1647
|
+
const [status, apsFrame] = await this.sendZDORequest(networkAddress, Zdo.ClusterId.ROUTING_TABLE_REQUEST, zdoPayload, exports.DEFAULT_APS_OPTIONS);
|
|
1770
1648
|
if (status !== enums_1.SLStatus.OK) {
|
|
1771
|
-
throw new Error(`[ZDO] Failed routing table request for
|
|
1649
|
+
throw new Error(`[ZDO] Failed routing table request for '${networkAddress}' (index '${startIndex}') with status=${enums_1.SLStatus[status]}.`);
|
|
1772
1650
|
}
|
|
1773
1651
|
const result = await this.oneWaitress.startWaitingFor({
|
|
1774
1652
|
target: networkAddress,
|
|
@@ -1799,9 +1677,9 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1799
1677
|
return this.queue.execute(async () => {
|
|
1800
1678
|
this.checkInterpanLock();
|
|
1801
1679
|
const zdoPayload = buffaloZdo_1.BuffaloZdo.buildNodeDescriptorRequest(networkAddress);
|
|
1802
|
-
const [status, apsFrame] = await this.sendZDORequest(networkAddress, Zdo.ClusterId.NODE_DESCRIPTOR_REQUEST, zdoPayload, DEFAULT_APS_OPTIONS);
|
|
1680
|
+
const [status, apsFrame] = await this.sendZDORequest(networkAddress, Zdo.ClusterId.NODE_DESCRIPTOR_REQUEST, zdoPayload, exports.DEFAULT_APS_OPTIONS);
|
|
1803
1681
|
if (status !== enums_1.SLStatus.OK) {
|
|
1804
|
-
throw new Error(`[ZDO] Failed node descriptor for
|
|
1682
|
+
throw new Error(`[ZDO] Failed node descriptor request for '${networkAddress}' with status=${enums_1.SLStatus[status]}.`);
|
|
1805
1683
|
}
|
|
1806
1684
|
const result = await this.oneWaitress.startWaitingFor({
|
|
1807
1685
|
target: networkAddress,
|
|
@@ -1820,11 +1698,11 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1820
1698
|
type = 'EndDevice';
|
|
1821
1699
|
break;
|
|
1822
1700
|
}
|
|
1823
|
-
|
|
1701
|
+
/* istanbul ignore else */
|
|
1824
1702
|
if (result.serverMask.stackComplianceResivion < CURRENT_ZIGBEE_SPEC_REVISION) {
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1703
|
+
// always 0 before rev. 21 where field was added
|
|
1704
|
+
const rev = result.serverMask.stackComplianceResivion < 21 ? 'pre-21' : result.serverMask.stackComplianceResivion;
|
|
1705
|
+
logger_1.logger.warning(`[ZDO] Device '${networkAddress}' is only compliant to revision '${rev}' of the ZigBee specification (current revision: ${CURRENT_ZIGBEE_SPEC_REVISION}).`, NS);
|
|
1828
1706
|
}
|
|
1829
1707
|
return { type, manufacturerCode: result.manufacturerCode };
|
|
1830
1708
|
}, networkAddress);
|
|
@@ -1834,9 +1712,9 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1834
1712
|
return this.queue.execute(async () => {
|
|
1835
1713
|
this.checkInterpanLock();
|
|
1836
1714
|
const zdoPayload = buffaloZdo_1.BuffaloZdo.buildActiveEndpointsRequest(networkAddress);
|
|
1837
|
-
const [status, apsFrame] = await this.sendZDORequest(networkAddress, Zdo.ClusterId.ACTIVE_ENDPOINTS_REQUEST, zdoPayload, DEFAULT_APS_OPTIONS);
|
|
1715
|
+
const [status, apsFrame] = await this.sendZDORequest(networkAddress, Zdo.ClusterId.ACTIVE_ENDPOINTS_REQUEST, zdoPayload, exports.DEFAULT_APS_OPTIONS);
|
|
1838
1716
|
if (status !== enums_1.SLStatus.OK) {
|
|
1839
|
-
throw new Error(`[ZDO] Failed active endpoints request for
|
|
1717
|
+
throw new Error(`[ZDO] Failed active endpoints request for '${networkAddress}' with status=${enums_1.SLStatus[status]}.`);
|
|
1840
1718
|
}
|
|
1841
1719
|
const result = await this.oneWaitress.startWaitingFor({
|
|
1842
1720
|
target: networkAddress,
|
|
@@ -1851,9 +1729,9 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1851
1729
|
return this.queue.execute(async () => {
|
|
1852
1730
|
this.checkInterpanLock();
|
|
1853
1731
|
const zdoPayload = buffaloZdo_1.BuffaloZdo.buildSimpleDescriptorRequest(networkAddress, endpointID);
|
|
1854
|
-
const [status, apsFrame] = await this.sendZDORequest(networkAddress, Zdo.ClusterId.SIMPLE_DESCRIPTOR_REQUEST, zdoPayload, DEFAULT_APS_OPTIONS);
|
|
1732
|
+
const [status, apsFrame] = await this.sendZDORequest(networkAddress, Zdo.ClusterId.SIMPLE_DESCRIPTOR_REQUEST, zdoPayload, exports.DEFAULT_APS_OPTIONS);
|
|
1855
1733
|
if (status !== enums_1.SLStatus.OK) {
|
|
1856
|
-
throw new Error(`[ZDO] Failed simple descriptor request for
|
|
1734
|
+
throw new Error(`[ZDO] Failed simple descriptor request for '${networkAddress}' endpoint '${endpointID}' with status=${enums_1.SLStatus[status]}.`);
|
|
1857
1735
|
}
|
|
1858
1736
|
const result = await this.oneWaitress.startWaitingFor({
|
|
1859
1737
|
target: networkAddress,
|
|
@@ -1871,90 +1749,49 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1871
1749
|
}
|
|
1872
1750
|
// queued, non-InterPAN
|
|
1873
1751
|
async bind(destinationNetworkAddress, sourceIeeeAddress, sourceEndpoint, clusterID, destinationAddressOrGroup, type, destinationEndpoint) {
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
}, destinationNetworkAddress);
|
|
1891
|
-
}
|
|
1892
|
-
else if (typeof destinationAddressOrGroup === 'number' && type === 'group') {
|
|
1893
|
-
// dest is group num, so type should always be group (multicast)
|
|
1894
|
-
return this.queue.execute(async () => {
|
|
1895
|
-
this.checkInterpanLock();
|
|
1896
|
-
const zdoPayload = buffaloZdo_1.BuffaloZdo.buildBindRequest(sourceIeeeAddress, sourceEndpoint, clusterID, Zdo.MULTICAST_BINDING, undefined, // not used with MULTICAST_BINDING
|
|
1897
|
-
destinationAddressOrGroup, undefined);
|
|
1898
|
-
const [status, apsFrame] = await this.sendZDORequest(destinationNetworkAddress, Zdo.ClusterId.BIND_REQUEST, zdoPayload, DEFAULT_APS_OPTIONS);
|
|
1899
|
-
if (status !== enums_1.SLStatus.OK) {
|
|
1900
|
-
throw new Error(`[ZDO] Failed bind request for "${destinationNetworkAddress}" group "${destinationAddressOrGroup}" ` +
|
|
1901
|
-
`with status=${enums_1.SLStatus[status]}.`);
|
|
1902
|
-
}
|
|
1903
|
-
await this.oneWaitress.startWaitingFor({
|
|
1904
|
-
target: destinationNetworkAddress,
|
|
1905
|
-
apsFrame,
|
|
1906
|
-
responseClusterId: Zdo.ClusterId.BIND_RESPONSE,
|
|
1907
|
-
}, DEFAULT_ZDO_REQUEST_TIMEOUT);
|
|
1908
|
-
}, destinationNetworkAddress);
|
|
1909
|
-
}
|
|
1752
|
+
return this.queue.execute(async () => {
|
|
1753
|
+
this.checkInterpanLock();
|
|
1754
|
+
const zdoPayload = buffaloZdo_1.BuffaloZdo.buildBindRequest(sourceIeeeAddress, sourceEndpoint, clusterID, type === 'group' ? Zdo.MULTICAST_BINDING : Zdo.UNICAST_BINDING, destinationAddressOrGroup, // not used with MULTICAST_BINDING
|
|
1755
|
+
destinationAddressOrGroup, // not used with UNICAST_BINDING
|
|
1756
|
+
destinationEndpoint);
|
|
1757
|
+
console.log(zdoPayload);
|
|
1758
|
+
const [status, apsFrame] = await this.sendZDORequest(destinationNetworkAddress, Zdo.ClusterId.BIND_REQUEST, zdoPayload, exports.DEFAULT_APS_OPTIONS);
|
|
1759
|
+
if (status !== enums_1.SLStatus.OK) {
|
|
1760
|
+
throw new Error(`[ZDO] Failed bind request for '${destinationNetworkAddress}' destination '${destinationAddressOrGroup}' endpoint '${destinationEndpoint}' with status=${enums_1.SLStatus[status]}.`);
|
|
1761
|
+
}
|
|
1762
|
+
await this.oneWaitress.startWaitingFor({
|
|
1763
|
+
target: destinationNetworkAddress,
|
|
1764
|
+
apsFrame,
|
|
1765
|
+
responseClusterId: Zdo.ClusterId.BIND_RESPONSE,
|
|
1766
|
+
}, DEFAULT_ZDO_REQUEST_TIMEOUT);
|
|
1767
|
+
}, destinationNetworkAddress);
|
|
1910
1768
|
}
|
|
1911
1769
|
// queued, non-InterPAN
|
|
1912
1770
|
async unbind(destinationNetworkAddress, sourceIeeeAddress, sourceEndpoint, clusterID, destinationAddressOrGroup, type, destinationEndpoint) {
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
}, DEFAULT_ZDO_REQUEST_TIMEOUT);
|
|
1929
|
-
}, destinationNetworkAddress);
|
|
1930
|
-
}
|
|
1931
|
-
else if (typeof destinationAddressOrGroup === 'number' && type === 'group') {
|
|
1932
|
-
// dest is group num, so type should always be group (multicast)
|
|
1933
|
-
return this.queue.execute(async () => {
|
|
1934
|
-
this.checkInterpanLock();
|
|
1935
|
-
const zdoPayload = buffaloZdo_1.BuffaloZdo.buildUnbindRequest(sourceIeeeAddress, sourceEndpoint, clusterID, Zdo.MULTICAST_BINDING, undefined, // not used with MULTICAST_BINDING
|
|
1936
|
-
destinationAddressOrGroup, undefined);
|
|
1937
|
-
const [status, apsFrame] = await this.sendZDORequest(destinationNetworkAddress, Zdo.ClusterId.UNBIND_REQUEST, zdoPayload, DEFAULT_APS_OPTIONS);
|
|
1938
|
-
if (status !== enums_1.SLStatus.OK) {
|
|
1939
|
-
throw new Error(`[ZDO] Failed unbind request for "${destinationNetworkAddress}" group "${destinationAddressOrGroup}" ` +
|
|
1940
|
-
`with status=${enums_1.SLStatus[status]}.`);
|
|
1941
|
-
}
|
|
1942
|
-
await this.oneWaitress.startWaitingFor({
|
|
1943
|
-
target: destinationNetworkAddress,
|
|
1944
|
-
apsFrame,
|
|
1945
|
-
responseClusterId: Zdo.ClusterId.UNBIND_RESPONSE,
|
|
1946
|
-
}, DEFAULT_ZDO_REQUEST_TIMEOUT);
|
|
1947
|
-
}, destinationNetworkAddress);
|
|
1948
|
-
}
|
|
1771
|
+
return this.queue.execute(async () => {
|
|
1772
|
+
this.checkInterpanLock();
|
|
1773
|
+
const zdoPayload = buffaloZdo_1.BuffaloZdo.buildUnbindRequest(sourceIeeeAddress, sourceEndpoint, clusterID, type === 'group' ? Zdo.MULTICAST_BINDING : Zdo.UNICAST_BINDING, destinationAddressOrGroup, // not used with MULTICAST_BINDING
|
|
1774
|
+
destinationAddressOrGroup, // not used with UNICAST_BINDING
|
|
1775
|
+
destinationEndpoint);
|
|
1776
|
+
const [status, apsFrame] = await this.sendZDORequest(destinationNetworkAddress, Zdo.ClusterId.UNBIND_REQUEST, zdoPayload, exports.DEFAULT_APS_OPTIONS);
|
|
1777
|
+
if (status !== enums_1.SLStatus.OK) {
|
|
1778
|
+
throw new Error(`[ZDO] Failed unbind request for '${destinationNetworkAddress}' destination '${destinationAddressOrGroup}' endpoint '${destinationEndpoint}' with status=${enums_1.SLStatus[status]}.`);
|
|
1779
|
+
}
|
|
1780
|
+
await this.oneWaitress.startWaitingFor({
|
|
1781
|
+
target: destinationNetworkAddress,
|
|
1782
|
+
apsFrame,
|
|
1783
|
+
responseClusterId: Zdo.ClusterId.UNBIND_RESPONSE,
|
|
1784
|
+
}, DEFAULT_ZDO_REQUEST_TIMEOUT);
|
|
1785
|
+
}, destinationNetworkAddress);
|
|
1949
1786
|
}
|
|
1950
1787
|
// queued, non-InterPAN
|
|
1951
1788
|
async removeDevice(networkAddress, ieeeAddr) {
|
|
1952
1789
|
return this.queue.execute(async () => {
|
|
1953
1790
|
this.checkInterpanLock();
|
|
1954
1791
|
const zdoPayload = buffaloZdo_1.BuffaloZdo.buildLeaveRequest(ieeeAddr, Zdo.LeaveRequestFlags.WITHOUT_REJOIN);
|
|
1955
|
-
const [status, apsFrame] = await this.sendZDORequest(networkAddress, Zdo.ClusterId.LEAVE_REQUEST, zdoPayload, DEFAULT_APS_OPTIONS);
|
|
1792
|
+
const [status, apsFrame] = await this.sendZDORequest(networkAddress, Zdo.ClusterId.LEAVE_REQUEST, zdoPayload, exports.DEFAULT_APS_OPTIONS);
|
|
1956
1793
|
if (status !== enums_1.SLStatus.OK) {
|
|
1957
|
-
throw new Error(`[ZDO] Failed remove device request for
|
|
1794
|
+
throw new Error(`[ZDO] Failed remove device request for '${networkAddress}' target '${ieeeAddr}' with status=${enums_1.SLStatus[status]}.`);
|
|
1958
1795
|
}
|
|
1959
1796
|
await this.oneWaitress.startWaitingFor({
|
|
1960
1797
|
target: networkAddress,
|
|
@@ -1966,10 +1803,10 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1966
1803
|
//---- ZCL
|
|
1967
1804
|
// queued, non-InterPAN
|
|
1968
1805
|
async sendZclFrameToEndpoint(ieeeAddr, networkAddress, endpoint, zclFrame, timeout, disableResponse, disableRecovery, sourceEndpoint) {
|
|
1969
|
-
const sourceEndpointInfo =
|
|
1806
|
+
const sourceEndpointInfo = (sourceEndpoint && endpoints_1.FIXED_ENDPOINTS.find((epi) => epi.endpoint === sourceEndpoint)) || endpoints_1.FIXED_ENDPOINTS[0];
|
|
1970
1807
|
const command = zclFrame.command;
|
|
1971
1808
|
let commandResponseId = null;
|
|
1972
|
-
if (command.
|
|
1809
|
+
if (command.response !== undefined && disableResponse === false) {
|
|
1973
1810
|
commandResponseId = command.response;
|
|
1974
1811
|
}
|
|
1975
1812
|
else if (!zclFrame.header.frameControl.disableDefaultResponse) {
|
|
@@ -1978,9 +1815,9 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1978
1815
|
const apsFrame = {
|
|
1979
1816
|
profileId: sourceEndpointInfo.profileId,
|
|
1980
1817
|
clusterId: zclFrame.cluster.ID,
|
|
1981
|
-
sourceEndpoint:
|
|
1982
|
-
destinationEndpoint:
|
|
1983
|
-
options: DEFAULT_APS_OPTIONS,
|
|
1818
|
+
sourceEndpoint: sourceEndpoint || endpoints_1.FIXED_ENDPOINTS[0].endpoint,
|
|
1819
|
+
destinationEndpoint: endpoint,
|
|
1820
|
+
options: exports.DEFAULT_APS_OPTIONS,
|
|
1984
1821
|
groupId: 0,
|
|
1985
1822
|
sequence: 0, // set by stack
|
|
1986
1823
|
};
|
|
@@ -1999,12 +1836,17 @@ class EmberAdapter extends __1.Adapter {
|
|
|
1999
1836
|
0);
|
|
2000
1837
|
}
|
|
2001
1838
|
catch (error) {
|
|
1839
|
+
/* istanbul ignore else */
|
|
2002
1840
|
if (error instanceof ezspError_1.EzspError) {
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
1841
|
+
switch (error.code) {
|
|
1842
|
+
case enums_1.EzspStatus.NO_TX_SPACE: {
|
|
1843
|
+
status = enums_1.SLStatus.BUSY;
|
|
1844
|
+
break;
|
|
1845
|
+
}
|
|
1846
|
+
case enums_1.EzspStatus.NOT_CONNECTED: {
|
|
1847
|
+
status = enums_1.SLStatus.NETWORK_DOWN;
|
|
1848
|
+
break;
|
|
1849
|
+
}
|
|
2008
1850
|
}
|
|
2009
1851
|
}
|
|
2010
1852
|
}
|
|
@@ -2021,6 +1863,10 @@ class EmberAdapter extends __1.Adapter {
|
|
|
2021
1863
|
else if (status === enums_1.SLStatus.NETWORK_DOWN) {
|
|
2022
1864
|
await (0, utils_1.Wait)(QUEUE_NETWORK_DOWN_DEFER_MSEC);
|
|
2023
1865
|
}
|
|
1866
|
+
else {
|
|
1867
|
+
throw new Error(`~x~> [ZCL to=${networkAddress}] Failed to send request with status=${enums_1.SLStatus[status]}.`);
|
|
1868
|
+
}
|
|
1869
|
+
logger_1.logger.debug(`~x~> [ZCL to=${networkAddress}] Failed to send request attempt ${i}/${QUEUE_MAX_SEND_ATTEMPTS} with status=${enums_1.SLStatus[status]}.`, NS);
|
|
2024
1870
|
}
|
|
2025
1871
|
if (commandResponseId != null) {
|
|
2026
1872
|
// NOTE: aps sequence number will have been set by send function
|
|
@@ -2029,7 +1875,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
2029
1875
|
apsFrame,
|
|
2030
1876
|
zclSequence: zclFrame.header.transactionSequenceNumber,
|
|
2031
1877
|
commandIdentifier: commandResponseId,
|
|
2032
|
-
}, timeout
|
|
1878
|
+
}, timeout);
|
|
2033
1879
|
return result;
|
|
2034
1880
|
}
|
|
2035
1881
|
return null;
|
|
@@ -2037,13 +1883,13 @@ class EmberAdapter extends __1.Adapter {
|
|
|
2037
1883
|
}
|
|
2038
1884
|
// queued, non-InterPAN
|
|
2039
1885
|
async sendZclFrameToGroup(groupID, zclFrame, sourceEndpoint) {
|
|
2040
|
-
const sourceEndpointInfo =
|
|
1886
|
+
const sourceEndpointInfo = (sourceEndpoint && endpoints_1.FIXED_ENDPOINTS.find((epi) => epi.endpoint === sourceEndpoint)) || endpoints_1.FIXED_ENDPOINTS[0];
|
|
2041
1887
|
const apsFrame = {
|
|
2042
1888
|
profileId: sourceEndpointInfo.profileId,
|
|
2043
1889
|
clusterId: zclFrame.cluster.ID,
|
|
2044
|
-
sourceEndpoint:
|
|
2045
|
-
destinationEndpoint:
|
|
2046
|
-
options: DEFAULT_APS_OPTIONS,
|
|
1890
|
+
sourceEndpoint: sourceEndpoint || endpoints_1.FIXED_ENDPOINTS[0].endpoint,
|
|
1891
|
+
destinationEndpoint: 0xff,
|
|
1892
|
+
options: exports.DEFAULT_APS_OPTIONS,
|
|
2047
1893
|
groupId: groupID,
|
|
2048
1894
|
sequence: 0, // set by stack
|
|
2049
1895
|
};
|
|
@@ -2064,13 +1910,13 @@ class EmberAdapter extends __1.Adapter {
|
|
|
2064
1910
|
}
|
|
2065
1911
|
// queued, non-InterPAN
|
|
2066
1912
|
async sendZclFrameToAll(endpoint, zclFrame, sourceEndpoint, destination) {
|
|
2067
|
-
const sourceEndpointInfo =
|
|
1913
|
+
const sourceEndpointInfo = endpoints_1.FIXED_ENDPOINTS.find((epi) => epi.endpoint === sourceEndpoint) ?? endpoints_1.FIXED_ENDPOINTS[0];
|
|
2068
1914
|
const apsFrame = {
|
|
2069
1915
|
profileId: sourceEndpointInfo.profileId,
|
|
2070
1916
|
clusterId: zclFrame.cluster.ID,
|
|
2071
|
-
sourceEndpoint
|
|
2072
|
-
destinationEndpoint:
|
|
2073
|
-
options: DEFAULT_APS_OPTIONS,
|
|
1917
|
+
sourceEndpoint,
|
|
1918
|
+
destinationEndpoint: endpoint,
|
|
1919
|
+
options: exports.DEFAULT_APS_OPTIONS,
|
|
2074
1920
|
groupId: destination,
|
|
2075
1921
|
sequence: 0, // set by stack
|
|
2076
1922
|
};
|
|
@@ -2093,16 +1939,12 @@ class EmberAdapter extends __1.Adapter {
|
|
|
2093
1939
|
// TODO: check all this touchlink/interpan stuff
|
|
2094
1940
|
// queued
|
|
2095
1941
|
async setChannelInterPAN(channel) {
|
|
2096
|
-
if (typeof channel !== 'number') {
|
|
2097
|
-
logger_1.logger.error(`Tried to set channel InterPAN to non-number. Channel ${channel} of type ${typeof channel}.`, NS);
|
|
2098
|
-
return;
|
|
2099
|
-
}
|
|
2100
1942
|
return this.queue.execute(async () => {
|
|
2101
1943
|
this.interpanLock = true;
|
|
2102
1944
|
const status = await this.ezsp.ezspSetLogicalAndRadioChannel(channel);
|
|
2103
1945
|
if (status !== enums_1.SLStatus.OK) {
|
|
2104
1946
|
this.interpanLock = false; // XXX: ok?
|
|
2105
|
-
throw new Error(`Failed to set InterPAN channel to ${channel} with status=${enums_1.SLStatus[status]}.`);
|
|
1947
|
+
throw new Error(`Failed to set InterPAN channel to '${channel}' with status=${enums_1.SLStatus[status]}.`);
|
|
2106
1948
|
}
|
|
2107
1949
|
});
|
|
2108
1950
|
}
|
|
@@ -2134,15 +1976,16 @@ class EmberAdapter extends __1.Adapter {
|
|
|
2134
1976
|
// queued
|
|
2135
1977
|
async sendZclFrameInterPANBroadcast(zclFrame, timeout) {
|
|
2136
1978
|
const command = zclFrame.command;
|
|
2137
|
-
if (
|
|
1979
|
+
if (command.response === undefined) {
|
|
2138
1980
|
throw new Error(`Command '${command.name}' has no response, cannot wait for response.`);
|
|
2139
1981
|
}
|
|
1982
|
+
const endpoint = endpoints_1.FIXED_ENDPOINTS[0].endpoint;
|
|
2140
1983
|
// just for waitress
|
|
2141
1984
|
const apsFrame = {
|
|
2142
1985
|
profileId: ZSpec.TOUCHLINK_PROFILE_ID,
|
|
2143
1986
|
clusterId: zclFrame.cluster.ID,
|
|
2144
|
-
sourceEndpoint:
|
|
2145
|
-
destinationEndpoint:
|
|
1987
|
+
sourceEndpoint: endpoint, // arbitrary since not sent over-the-air
|
|
1988
|
+
destinationEndpoint: endpoint,
|
|
2146
1989
|
options: enums_1.EmberApsOption.NONE,
|
|
2147
1990
|
groupId: ZSpec.BroadcastAddress.SLEEPY,
|
|
2148
1991
|
sequence: 0, // set by stack
|
|
@@ -2174,7 +2017,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
2174
2017
|
apsFrame: apsFrame,
|
|
2175
2018
|
zclSequence: zclFrame.header.transactionSequenceNumber,
|
|
2176
2019
|
commandIdentifier: command.response,
|
|
2177
|
-
}, timeout
|
|
2020
|
+
}, timeout);
|
|
2178
2021
|
return result;
|
|
2179
2022
|
});
|
|
2180
2023
|
}
|
|
@@ -2183,7 +2026,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
2183
2026
|
return this.queue.execute(async () => {
|
|
2184
2027
|
const status = await this.ezsp.ezspSetLogicalAndRadioChannel(this.networkOptions.channelList[0]);
|
|
2185
2028
|
if (status !== enums_1.SLStatus.OK) {
|
|
2186
|
-
throw new Error(`Failed to restore InterPAN channel to ${this.networkOptions.channelList[0]} with status=${enums_1.SLStatus[status]}.`);
|
|
2029
|
+
throw new Error(`Failed to restore InterPAN channel to '${this.networkOptions.channelList[0]}' with status=${enums_1.SLStatus[status]}.`);
|
|
2187
2030
|
}
|
|
2188
2031
|
// let adapter settle down
|
|
2189
2032
|
await (0, utils_1.Wait)(QUEUE_NETWORK_DOWN_DEFER_MSEC);
|
|
@@ -2193,9 +2036,7 @@ class EmberAdapter extends __1.Adapter {
|
|
|
2193
2036
|
//-- END Adapter implementation
|
|
2194
2037
|
checkInterpanLock() {
|
|
2195
2038
|
if (this.interpanLock) {
|
|
2196
|
-
|
|
2197
|
-
// will be caught by request queue and rejected internally.
|
|
2198
|
-
throw new ezspError_1.EzspError(enums_1.EzspStatus.ERROR_INVALID_CALL);
|
|
2039
|
+
throw new Error(`[INTERPAN MODE] Cannot execute non-InterPAN commands.`);
|
|
2199
2040
|
}
|
|
2200
2041
|
}
|
|
2201
2042
|
}
|