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.
@@ -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
- const DEFAULT_STACK_CONFIG = {
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
- const DEFAULT_APS_OPTIONS = enums_1.EmberApsOption.RETRY | enums_1.EmberApsOption.ENABLE_ROUTE_DISCOVERY | enums_1.EmberApsOption.ENABLE_ADDRESS_DISCOVERY;
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
- const delay = typeof this.adapterOptions.delay === 'number' ? Math.min(Math.max(this.adapterOptions.delay, 5), 60) : 5;
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
- if (status === enums_1.SLStatus.ZIGBEE_DELIVERY_FAILED) {
313
- // no ACK was received from the destination
314
- switch (type) {
315
- case enums_1.EmberOutgoingMessageType.BROADCAST:
316
- case enums_1.EmberOutgoingMessageType.BROADCAST_WITH_ALIAS:
317
- case enums_1.EmberOutgoingMessageType.MULTICAST:
318
- case enums_1.EmberOutgoingMessageType.MULTICAST_WITH_ALIAS: {
319
- // BC/MC not checking for message sent, avoid unnecessary waitress lookups
320
- logger_1.logger.error(`Delivery of ${enums_1.EmberOutgoingMessageType[type]} failed for "${indexOrDestination}" ` +
321
- `[apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag}]`, NS);
322
- break;
323
- }
324
- default: {
325
- // reject any waitress early (don't wait for timeout if we know we're gonna get there eventually)
326
- this.oneWaitress.deliveryFailedFor(indexOrDestination, apsFrame);
327
- break;
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
- else if (status === enums_1.SLStatus.OK) {
332
- if (type === enums_1.EmberOutgoingMessageType.MULTICAST &&
333
- apsFrame.destinationEndpoint === 0xff &&
334
- apsFrame.groupId < consts_1.EMBER_MIN_BROADCAST_ADDRESS &&
335
- !this.multicastTable.includes(apsFrame.groupId)) {
336
- // workaround for devices using multicast for state update (coordinator passthrough)
337
- const tableIdx = this.multicastTable.length;
338
- const multicastEntry = {
339
- multicastId: apsFrame.groupId,
340
- endpoint: endpoints_1.FIXED_ENDPOINTS[0].endpoint,
341
- networkIndex: endpoints_1.FIXED_ENDPOINTS[0].networkIndex,
342
- };
343
- // set immediately to avoid potential race
344
- this.multicastTable.push(multicastEntry.multicastId);
345
- try {
346
- await this.queue.execute(async () => {
347
- const status = await this.ezsp.ezspSetMulticastTableEntry(tableIdx, multicastEntry);
348
- if (status !== enums_1.SLStatus.OK) {
349
- throw new Error(`Failed to register group "${multicastEntry.multicastId}" in multicast table with status=${enums_1.SLStatus[status]}.`);
350
- }
351
- logger_1.logger.debug(`Registered multicast table entry (${tableIdx}): ${JSON.stringify(multicastEntry)}.`, NS);
352
- });
353
- }
354
- catch (error) {
355
- // remove to allow retry on next occurrence
356
- this.multicastTable.splice(tableIdx, 1);
357
- throw error;
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: 1, // arbitrary since not sent over-the-air
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: endpoints_1.FIXED_ENDPOINTS[0].endpoint,
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 "${ep.endpoint}" with status=${enums_1.SLStatus[status]}.`);
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 "${ep.endpoint}" already registered.`, NS);
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 "${multicastId}" in multicast table with status=${enums_1.SLStatus[status]}.`);
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(`[BACKUP] Failed to export Network Key with status=${enums_1.SLStatus[nkStatus]}.`);
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.emberNetworkState();
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
- logger_1.logger.error(`Failed to get PAN ID (via network parameters) with status=${enums_1.SLStatus[status]}.`, NS);
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
- logger_1.logger.error(`Failed to get Extended PAN ID (via network parameters) with status=${enums_1.SLStatus[status]}.`, NS);
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
- logger_1.logger.error(`Failed to get radio channel (via network parameters) with status=${enums_1.SLStatus[status]}.`, NS);
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 "${enums_2.EzspConfigId[configId]}" TO "${value}" with status=${enums_1.SLStatus[status]}.`, NS);
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 "${enums_2.EzspConfigId[configId]}" TO "${value}" with status=${enums_1.SLStatus[status]}. ` +
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 "${enums_2.EzspValueId[valueId]}" TO "${value}" with status=${enums_1.SLStatus[status]}.`, NS);
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 "${enums_2.EzspPolicyId[policyId]}" TO "${decisionId}" with status=${enums_1.SLStatus[status]}.`, NS);
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.emberAesMmoHashFinal(context, data);
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
- if (decision == enums_1.EmberJoinDecision.USE_PRECONFIGURED_KEY) {
1300
- policy = enums_2.EzspDecisionBitmask.ALLOW_JOINS | enums_2.EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS;
1301
- }
1302
- else if (decision == enums_1.EmberJoinDecision.SEND_KEY_IN_THE_CLEAR) {
1303
- policy = enums_2.EzspDecisionBitmask.ALLOW_JOINS | enums_2.EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS | enums_2.EzspDecisionBitmask.SEND_KEY_IN_CLEAR;
1304
- }
1305
- else if (decision == enums_1.EmberJoinDecision.ALLOW_REJOINS_ONLY) {
1306
- policy = enums_2.EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS;
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, this.getZDORequestRadius(), messageTag, messageContents);
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
- return Promise.reject(new Error('Not supported'));
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.map((c) => ((2 ** c) & netParams.channels ? c : null)).filter((x) => x),
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((key) => ({
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 "${newChannel}" with status=${enums_1.SLStatus[status]}.`);
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 || DEFAULT_ZCL_REQUEST_TIMEOUT * 3); // XXX: since this is used by OTA...
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 for with status=${enums_1.SLStatus[setJPstatus]}.`);
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 "${networkAddress}" with status=${enums_1.SLStatus[status]}.`);
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 "${networkAddress}" (index "${startIndex}") with status=${enums_1.SLStatus[status]}.`);
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 "${networkAddress}" (index "${startIndex}") with status=${enums_1.SLStatus[status]}.`);
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 "${networkAddress}" with status=${enums_1.SLStatus[status]}.`);
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
- // always 0 before rev. 21 where field was added
1701
+ /* istanbul ignore else */
1824
1702
  if (result.serverMask.stackComplianceResivion < CURRENT_ZIGBEE_SPEC_REVISION) {
1825
- logger_1.logger.warning(`[ZDO] Node descriptor for '${networkAddress}' reports device is only compliant to revision ` +
1826
- `'${result.serverMask.stackComplianceResivion < 21 ? 'pre-21' : result.serverMask.stackComplianceResivion}' ` +
1827
- `of the ZigBee specification (current revision: ${CURRENT_ZIGBEE_SPEC_REVISION}).`, NS);
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 "${networkAddress}" with status=${enums_1.SLStatus[status]}.`);
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 "${networkAddress}" endpoint "${endpointID}" ` + `with status=${enums_1.SLStatus[status]}.`);
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
- if (typeof destinationAddressOrGroup === 'string' && type === 'endpoint') {
1875
- // dest address is EUI64 (str), so type should always be endpoint (unicast)
1876
- return this.queue.execute(async () => {
1877
- this.checkInterpanLock();
1878
- const zdoPayload = buffaloZdo_1.BuffaloZdo.buildBindRequest(sourceIeeeAddress, sourceEndpoint, clusterID, Zdo.UNICAST_BINDING, destinationAddressOrGroup, undefined, // not used with UNICAST_BINDING
1879
- destinationEndpoint);
1880
- const [status, apsFrame] = await this.sendZDORequest(destinationNetworkAddress, Zdo.ClusterId.BIND_REQUEST, zdoPayload, DEFAULT_APS_OPTIONS);
1881
- if (status !== enums_1.SLStatus.OK) {
1882
- throw new Error(`[ZDO] Failed bind request for "${destinationNetworkAddress}" destination "${destinationAddressOrGroup}" ` +
1883
- `endpoint "${destinationEndpoint}" with status=${enums_1.SLStatus[status]}.`);
1884
- }
1885
- await this.oneWaitress.startWaitingFor({
1886
- target: destinationNetworkAddress,
1887
- apsFrame,
1888
- responseClusterId: Zdo.ClusterId.BIND_RESPONSE,
1889
- }, DEFAULT_ZDO_REQUEST_TIMEOUT);
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
- if (typeof destinationAddressOrGroup === 'string' && type === 'endpoint') {
1914
- // dest address is EUI64 (str), so type should always be endpoint (unicast)
1915
- return this.queue.execute(async () => {
1916
- this.checkInterpanLock();
1917
- const zdoPayload = buffaloZdo_1.BuffaloZdo.buildUnbindRequest(sourceIeeeAddress, sourceEndpoint, clusterID, Zdo.UNICAST_BINDING, destinationAddressOrGroup, undefined, // not used with UNICAST_BINDING
1918
- destinationEndpoint);
1919
- const [status, apsFrame] = await this.sendZDORequest(destinationNetworkAddress, Zdo.ClusterId.UNBIND_REQUEST, zdoPayload, DEFAULT_APS_OPTIONS);
1920
- if (status !== enums_1.SLStatus.OK) {
1921
- throw new Error(`[ZDO] Failed unbind request for "${destinationNetworkAddress}" destination "${destinationAddressOrGroup}" ` +
1922
- `endpoint "${destinationEndpoint}" with status=${enums_1.SLStatus[status]}.`);
1923
- }
1924
- await this.oneWaitress.startWaitingFor({
1925
- target: destinationNetworkAddress,
1926
- apsFrame,
1927
- responseClusterId: Zdo.ClusterId.UNBIND_RESPONSE,
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 "${networkAddress}" target "${ieeeAddr}" ` + `with status=${enums_1.SLStatus[status]}.`);
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 = typeof sourceEndpoint === 'number' ? endpoints_1.FIXED_ENDPOINTS.find((epi) => epi.endpoint === sourceEndpoint) : endpoints_1.FIXED_ENDPOINTS[0];
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.hasOwnProperty('response') && disableResponse === false) {
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: sourceEndpointInfo.endpoint,
1982
- destinationEndpoint: typeof endpoint === 'number' ? endpoint : endpoints_1.FIXED_ENDPOINTS[0].endpoint,
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
- if (error.code === enums_1.EzspStatus.NO_TX_SPACE) {
2004
- status === enums_1.SLStatus.BUSY;
2005
- }
2006
- else if (error.code === enums_1.EzspStatus.NOT_CONNECTED) {
2007
- status === enums_1.SLStatus.NETWORK_DOWN;
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 || DEFAULT_ZCL_REQUEST_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 = typeof sourceEndpoint === 'number' ? endpoints_1.FIXED_ENDPOINTS.find((epi) => epi.endpoint === sourceEndpoint) : endpoints_1.FIXED_ENDPOINTS[0];
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: sourceEndpointInfo.endpoint,
2045
- destinationEndpoint: endpoints_1.FIXED_ENDPOINTS[0].endpoint,
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 = typeof sourceEndpoint === 'number' ? endpoints_1.FIXED_ENDPOINTS.find((epi) => epi.endpoint === sourceEndpoint) : endpoints_1.FIXED_ENDPOINTS[0];
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: sourceEndpointInfo.endpoint,
2072
- destinationEndpoint: typeof endpoint === 'number' ? endpoint : endpoints_1.FIXED_ENDPOINTS[0].endpoint,
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 (!command.hasOwnProperty('response')) {
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: 0,
2145
- destinationEndpoint: 0,
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 || DEFAULT_ZCL_REQUEST_TIMEOUT * 2); // XXX: touchlink 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
- logger_1.logger.error(`[INTERPAN MODE] Cannot execute non-InterPAN commands.`, NS);
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
  }