zigbee-herdsman 1.0.0 → 1.1.0

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.
Files changed (132) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +14 -0
  3. package/dist/adapter/adapter.d.ts +6 -0
  4. package/dist/adapter/adapter.d.ts.map +1 -1
  5. package/dist/adapter/adapter.js.map +1 -1
  6. package/dist/adapter/deconz/adapter/deconzAdapter.d.ts +5 -3
  7. package/dist/adapter/deconz/adapter/deconzAdapter.d.ts.map +1 -1
  8. package/dist/adapter/deconz/adapter/deconzAdapter.js +261 -558
  9. package/dist/adapter/deconz/adapter/deconzAdapter.js.map +1 -1
  10. package/dist/adapter/deconz/driver/constants.d.ts +102 -56
  11. package/dist/adapter/deconz/driver/constants.d.ts.map +1 -1
  12. package/dist/adapter/deconz/driver/constants.js +1 -2
  13. package/dist/adapter/deconz/driver/constants.js.map +1 -1
  14. package/dist/adapter/deconz/driver/driver.d.ts.map +1 -1
  15. package/dist/adapter/deconz/driver/driver.js +46 -41
  16. package/dist/adapter/deconz/driver/driver.js.map +1 -1
  17. package/dist/adapter/deconz/driver/frameParser.d.ts.map +1 -1
  18. package/dist/adapter/deconz/driver/frameParser.js +174 -107
  19. package/dist/adapter/deconz/driver/frameParser.js.map +1 -1
  20. package/dist/adapter/ember/adapter/emberAdapter.d.ts +4 -12
  21. package/dist/adapter/ember/adapter/emberAdapter.d.ts.map +1 -1
  22. package/dist/adapter/ember/adapter/emberAdapter.js +215 -249
  23. package/dist/adapter/ember/adapter/emberAdapter.js.map +1 -1
  24. package/dist/adapter/ember/adapter/oneWaitress.d.ts +16 -12
  25. package/dist/adapter/ember/adapter/oneWaitress.d.ts.map +1 -1
  26. package/dist/adapter/ember/adapter/oneWaitress.js +16 -41
  27. package/dist/adapter/ember/adapter/oneWaitress.js.map +1 -1
  28. package/dist/adapter/ezsp/adapter/ezspAdapter.d.ts +10 -8
  29. package/dist/adapter/ezsp/adapter/ezspAdapter.d.ts.map +1 -1
  30. package/dist/adapter/ezsp/adapter/ezspAdapter.js +241 -238
  31. package/dist/adapter/ezsp/adapter/ezspAdapter.js.map +1 -1
  32. package/dist/adapter/ezsp/driver/driver.d.ts +6 -14
  33. package/dist/adapter/ezsp/driver/driver.d.ts.map +1 -1
  34. package/dist/adapter/ezsp/driver/driver.js +56 -37
  35. package/dist/adapter/ezsp/driver/driver.js.map +1 -1
  36. package/dist/adapter/ezsp/driver/ezsp.d.ts.map +1 -1
  37. package/dist/adapter/ezsp/driver/ezsp.js +3 -0
  38. package/dist/adapter/ezsp/driver/ezsp.js.map +1 -1
  39. package/dist/adapter/z-stack/adapter/manager.js +2 -2
  40. package/dist/adapter/z-stack/adapter/manager.js.map +1 -1
  41. package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts +4 -3
  42. package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts.map +1 -1
  43. package/dist/adapter/z-stack/adapter/zStackAdapter.js +258 -197
  44. package/dist/adapter/z-stack/adapter/zStackAdapter.js.map +1 -1
  45. package/dist/adapter/z-stack/znp/definition.d.ts.map +1 -1
  46. package/dist/adapter/z-stack/znp/definition.js +256 -302
  47. package/dist/adapter/z-stack/znp/definition.js.map +1 -1
  48. package/dist/adapter/z-stack/znp/tstype.d.ts +8 -8
  49. package/dist/adapter/z-stack/znp/tstype.d.ts.map +1 -1
  50. package/dist/adapter/z-stack/znp/utils.d.ts +3 -2
  51. package/dist/adapter/z-stack/znp/utils.d.ts.map +1 -1
  52. package/dist/adapter/z-stack/znp/utils.js +8 -7
  53. package/dist/adapter/z-stack/znp/utils.js.map +1 -1
  54. package/dist/adapter/z-stack/znp/znp.d.ts +3 -1
  55. package/dist/adapter/z-stack/znp/znp.d.ts.map +1 -1
  56. package/dist/adapter/z-stack/znp/znp.js +31 -14
  57. package/dist/adapter/z-stack/znp/znp.js.map +1 -1
  58. package/dist/adapter/z-stack/znp/zpiObject.d.ts +5 -6
  59. package/dist/adapter/z-stack/znp/zpiObject.d.ts.map +1 -1
  60. package/dist/adapter/z-stack/znp/zpiObject.js +28 -11
  61. package/dist/adapter/z-stack/znp/zpiObject.js.map +1 -1
  62. package/dist/adapter/zboss/adapter/zbossAdapter.d.ts +7 -5
  63. package/dist/adapter/zboss/adapter/zbossAdapter.d.ts.map +1 -1
  64. package/dist/adapter/zboss/adapter/zbossAdapter.js +284 -138
  65. package/dist/adapter/zboss/adapter/zbossAdapter.js.map +1 -1
  66. package/dist/adapter/zboss/commands.d.ts +3 -0
  67. package/dist/adapter/zboss/commands.d.ts.map +1 -1
  68. package/dist/adapter/zboss/commands.js +248 -205
  69. package/dist/adapter/zboss/commands.js.map +1 -1
  70. package/dist/adapter/zboss/driver.d.ts +4 -14
  71. package/dist/adapter/zboss/driver.d.ts.map +1 -1
  72. package/dist/adapter/zboss/driver.js +63 -89
  73. package/dist/adapter/zboss/driver.js.map +1 -1
  74. package/dist/adapter/zboss/enums.d.ts +24 -2
  75. package/dist/adapter/zboss/enums.d.ts.map +1 -1
  76. package/dist/adapter/zboss/enums.js +35 -3
  77. package/dist/adapter/zboss/enums.js.map +1 -1
  78. package/dist/adapter/zboss/frame.d.ts +6 -1
  79. package/dist/adapter/zboss/frame.d.ts.map +1 -1
  80. package/dist/adapter/zboss/frame.js +56 -11
  81. package/dist/adapter/zboss/frame.js.map +1 -1
  82. package/dist/adapter/zboss/uart.d.ts +1 -0
  83. package/dist/adapter/zboss/uart.d.ts.map +1 -1
  84. package/dist/adapter/zboss/uart.js +4 -2
  85. package/dist/adapter/zboss/uart.js.map +1 -1
  86. package/dist/adapter/zigate/adapter/patchZdoBuffaloBE.d.ts +5 -0
  87. package/dist/adapter/zigate/adapter/patchZdoBuffaloBE.d.ts.map +1 -0
  88. package/dist/adapter/zigate/adapter/patchZdoBuffaloBE.js +44 -0
  89. package/dist/adapter/zigate/adapter/patchZdoBuffaloBE.js.map +1 -0
  90. package/dist/adapter/zigate/adapter/zigateAdapter.d.ts +5 -0
  91. package/dist/adapter/zigate/adapter/zigateAdapter.d.ts.map +1 -1
  92. package/dist/adapter/zigate/adapter/zigateAdapter.js +247 -262
  93. package/dist/adapter/zigate/adapter/zigateAdapter.js.map +1 -1
  94. package/dist/adapter/zigate/driver/buffaloZiGate.d.ts.map +1 -1
  95. package/dist/adapter/zigate/driver/buffaloZiGate.js +2 -18
  96. package/dist/adapter/zigate/driver/buffaloZiGate.js.map +1 -1
  97. package/dist/adapter/zigate/driver/commandType.js +218 -218
  98. package/dist/adapter/zigate/driver/commandType.js.map +1 -1
  99. package/dist/adapter/zigate/driver/constants.d.ts +14 -8
  100. package/dist/adapter/zigate/driver/constants.d.ts.map +1 -1
  101. package/dist/adapter/zigate/driver/constants.js +36 -9
  102. package/dist/adapter/zigate/driver/constants.js.map +1 -1
  103. package/dist/adapter/zigate/driver/messageType.js +42 -42
  104. package/dist/adapter/zigate/driver/messageType.js.map +1 -1
  105. package/dist/adapter/zigate/driver/zigate.d.ts +17 -13
  106. package/dist/adapter/zigate/driver/zigate.d.ts.map +1 -1
  107. package/dist/adapter/zigate/driver/zigate.js +83 -18
  108. package/dist/adapter/zigate/driver/zigate.js.map +1 -1
  109. package/dist/controller/controller.d.ts +1 -0
  110. package/dist/controller/controller.d.ts.map +1 -1
  111. package/dist/controller/controller.js +28 -0
  112. package/dist/controller/controller.js.map +1 -1
  113. package/dist/utils/patchBigIntSerialization.d.ts +2 -0
  114. package/dist/utils/patchBigIntSerialization.d.ts.map +1 -0
  115. package/dist/utils/patchBigIntSerialization.js +9 -0
  116. package/dist/utils/patchBigIntSerialization.js.map +1 -0
  117. package/dist/zspec/zcl/zclFrame.d.ts +1 -0
  118. package/dist/zspec/zcl/zclFrame.d.ts.map +1 -1
  119. package/dist/zspec/zcl/zclFrame.js +1 -0
  120. package/dist/zspec/zcl/zclFrame.js.map +1 -1
  121. package/dist/zspec/zdo/buffaloZdo.d.ts +1 -139
  122. package/dist/zspec/zdo/buffaloZdo.d.ts.map +1 -1
  123. package/dist/zspec/zdo/buffaloZdo.js.map +1 -1
  124. package/dist/zspec/zdo/definition/tstypes.d.ts +168 -1
  125. package/dist/zspec/zdo/definition/tstypes.d.ts.map +1 -1
  126. package/dist/zspec/zdo/definition/tstypes.js +1 -0
  127. package/dist/zspec/zdo/definition/tstypes.js.map +1 -1
  128. package/dist/zspec/zdo/utils.d.ts +2 -3
  129. package/dist/zspec/zdo/utils.d.ts.map +1 -1
  130. package/dist/zspec/zdo/utils.js +3 -4
  131. package/dist/zspec/zdo/utils.js.map +1 -1
  132. package/package.json +1 -1
@@ -27,16 +27,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  return (mod && mod.__esModule) ? mod : { "default": mod };
28
28
  };
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
- const buffalo_1 = require("../../../buffalo");
31
30
  const utils_1 = require("../../../utils");
32
31
  const logger_1 = require("../../../utils/logger");
32
+ const ZSpec = __importStar(require("../../../zspec"));
33
33
  const Zcl = __importStar(require("../../../zspec/zcl"));
34
+ const Zdo = __importStar(require("../../../zspec/zdo"));
34
35
  const adapter_1 = __importDefault(require("../../adapter"));
35
36
  const constants_1 = require("../driver/constants");
36
37
  const zigate_1 = __importDefault(require("../driver/zigate"));
38
+ const patchZdoBuffaloBE_1 = require("./patchZdoBuffaloBE");
37
39
  const NS = 'zh:zigate';
38
40
  const default_bind_group = 901; // https://github.com/Koenkk/zigbee-herdsman-converters/blob/master/lib/constants.js#L3
39
- const channelsToMask = (channels) => channels.map((x) => 2 ** x).reduce((acc, x) => acc + x, 0);
40
41
  class ZiGateAdapter extends adapter_1.default {
41
42
  driver;
42
43
  joinPermitted;
@@ -44,8 +45,9 @@ class ZiGateAdapter extends adapter_1.default {
44
45
  closing;
45
46
  queue;
46
47
  constructor(networkOptions, serialPortOptions, backupPath, adapterOptions) {
48
+ (0, patchZdoBuffaloBE_1.patchZdoBuffaloBE)();
47
49
  super(networkOptions, serialPortOptions, backupPath, adapterOptions);
48
- this.hasZdoMessageOverhead = false;
50
+ this.hasZdoMessageOverhead = false; // false for requests, true for responses
49
51
  this.joinPermitted = false;
50
52
  this.closing = false;
51
53
  const concurrent = this.adapterOptions && this.adapterOptions.concurrent ? this.adapterOptions.concurrent : 2;
@@ -57,6 +59,7 @@ class ZiGateAdapter extends adapter_1.default {
57
59
  this.driver.on('LeaveIndication', this.leaveIndicationListener.bind(this));
58
60
  this.driver.on('DeviceAnnounce', this.deviceAnnounceListener.bind(this));
59
61
  this.driver.on('close', this.onZiGateClose.bind(this));
62
+ this.driver.on('zdoResponse', this.onZdoResponse.bind(this));
60
63
  }
61
64
  /**
62
65
  * Adapter methods
@@ -81,9 +84,9 @@ class ZiGateAdapter extends adapter_1.default {
81
84
  await this.initNetwork();
82
85
  await this.driver.sendCommand(constants_1.ZiGateCommandCode.AddGroup, {
83
86
  addressMode: constants_1.ADDRESS_MODE.short,
84
- shortAddress: 0x0000,
85
- sourceEndpoint: 0x01,
86
- destinationEndpoint: 0x01,
87
+ shortAddress: ZSpec.COORDINATOR_ADDRESS,
88
+ sourceEndpoint: ZSpec.HA_ENDPOINT,
89
+ destinationEndpoint: ZSpec.HA_ENDPOINT,
87
90
  groupAddress: default_bind_group,
88
91
  });
89
92
  }
@@ -101,7 +104,7 @@ class ZiGateAdapter extends adapter_1.default {
101
104
  // @TODO deal hardcoded endpoints, made by analogy with deconz
102
105
  // polling the coordinator on some firmware went into a memory leak, so we don't ask this info
103
106
  const response = {
104
- networkAddress: 0,
107
+ networkAddress: ZSpec.COORDINATOR_ADDRESS,
105
108
  manufacturerID: 0,
106
109
  ieeeAddr: networkResponse.payload.extendedAddress,
107
110
  endpoints: constants_1.coordinatorEndpoints.slice(), // copy
@@ -125,14 +128,25 @@ class ZiGateAdapter extends adapter_1.default {
125
128
  };
126
129
  }
127
130
  async permitJoin(seconds, networkAddress) {
128
- const result = await this.driver.sendCommand(constants_1.ZiGateCommandCode.PermitJoin, {
129
- targetShortAddress: networkAddress || 0xfffc,
130
- interval: seconds,
131
- TCsignificance: 0,
132
- });
133
- // const result = await this.driver.sendCommand(ZiGateCommandCode.PermitJoinStatus, {});
134
- // Suitable only for the coordinator, not the entire network or point-to-point for routers
135
- this.joinPermitted = result.payload.status === 0;
131
+ const clusterId = Zdo.ClusterId.PERMIT_JOINING_REQUEST;
132
+ if (networkAddress !== undefined) {
133
+ // specific device that is not `Coordinator`
134
+ // `authentication`: TC significance always 1 (zb specs)
135
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, seconds, 1, []);
136
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, networkAddress, clusterId, zdoPayload, false);
137
+ /* istanbul ignore next */
138
+ if (!Zdo.Buffalo.checkStatus(result)) {
139
+ // TODO: will disappear once moved upstream
140
+ throw new Zdo.StatusError(result[0]);
141
+ }
142
+ }
143
+ else {
144
+ // broadcast permit joining ZDO
145
+ // `authentication`: TC significance always 1 (zb specs)
146
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, seconds, 1, []);
147
+ await this.sendZdo(ZSpec.BLANK_EUI64, ZSpec.BroadcastAddress.DEFAULT, clusterId, zdoPayload, true);
148
+ }
149
+ this.joinPermitted = seconds !== 0;
136
150
  }
137
151
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
138
152
  async addInstallCode(ieeeAddress, key) {
@@ -169,9 +183,11 @@ class ZiGateAdapter extends adapter_1.default {
169
183
  async backup() {
170
184
  throw new Error('This adapter does not support backup');
171
185
  }
172
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
173
186
  async changeChannel(newChannel) {
174
- throw new Error(`Channel change is not supported for 'zigate'`);
187
+ const clusterId = Zdo.ClusterId.NWK_UPDATE_REQUEST;
188
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, [newChannel], 0xfe, undefined, undefined, undefined);
189
+ await this.sendZdo(ZSpec.BLANK_EUI64, ZSpec.BroadcastAddress.SLEEPY, clusterId, zdoPayload, true /* handled below */);
190
+ await (0, utils_1.Wait)(12000);
175
191
  }
176
192
  async setTransmitPower(value) {
177
193
  try {
@@ -182,252 +198,215 @@ class ZiGateAdapter extends adapter_1.default {
182
198
  }
183
199
  }
184
200
  async lqi(networkAddress) {
185
- return await this.queue.execute(async () => {
186
- const neighbors = [];
187
- const add = (list) => {
188
- for (const entry of list) {
189
- const relationByte = entry.readUInt8(18);
190
- const extAddr = entry.subarray(8, 16);
201
+ const clusterId = Zdo.ClusterId.LQI_TABLE_REQUEST;
202
+ const neighbors = [];
203
+ const request = async (startIndex) => {
204
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, startIndex);
205
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, networkAddress, clusterId, zdoPayload, false);
206
+ /* istanbul ignore else */
207
+ if (Zdo.Buffalo.checkStatus(result)) {
208
+ const payload = result[1];
209
+ for (const entry of payload.entryList) {
191
210
  neighbors.push({
192
- linkquality: entry.readUInt8(21),
193
- networkAddress: entry.readUInt16LE(16),
194
- ieeeAddr: new buffalo_1.Buffalo(extAddr).readIeeeAddr(),
195
- relationship: (relationByte >> 1) & ((1 << 3) - 1),
196
- depth: entry.readUInt8(20),
211
+ ieeeAddr: entry.eui64,
212
+ networkAddress: entry.nwkAddress,
213
+ linkquality: entry.lqi,
214
+ relationship: entry.relationship,
215
+ depth: entry.depth,
197
216
  });
198
217
  }
199
- };
200
- const request = async (startIndex) => {
201
- try {
202
- const resultPayload = await this.driver.sendCommand(constants_1.ZiGateCommandCode.ManagementLQI, {
203
- targetAddress: networkAddress,
204
- startIndex: startIndex,
218
+ return [payload.neighborTableEntries, payload.entryList.length];
219
+ }
220
+ else {
221
+ // TODO: will disappear once moved upstream
222
+ throw new Zdo.StatusError(result[0]);
223
+ }
224
+ };
225
+ let [tableEntries, entryCount] = await request(0);
226
+ const size = tableEntries;
227
+ let nextStartIndex = entryCount;
228
+ while (neighbors.length < size) {
229
+ [tableEntries, entryCount] = await request(nextStartIndex);
230
+ nextStartIndex += entryCount;
231
+ }
232
+ return { neighbors };
233
+ }
234
+ async routingTable(networkAddress) {
235
+ const clusterId = Zdo.ClusterId.ROUTING_TABLE_REQUEST;
236
+ const table = [];
237
+ const request = async (startIndex) => {
238
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, startIndex);
239
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, networkAddress, clusterId, zdoPayload, false);
240
+ /* istanbul ignore else */
241
+ if (Zdo.Buffalo.checkStatus(result)) {
242
+ const payload = result[1];
243
+ for (const entry of payload.entryList) {
244
+ table.push({
245
+ destinationAddress: entry.destinationAddress,
246
+ status: entry.status,
247
+ nextHop: entry.nextHopAddress,
205
248
  });
206
- const data = resultPayload.payload.payload;
207
- if (data[1] !== 0) {
208
- // status
209
- throw new Error(`LQI for '${networkAddress}' failed`);
210
- }
211
- const tableList = [];
212
- const response = {
213
- status: data[1],
214
- tableEntrys: data[2],
215
- startIndex: data[3],
216
- tableListCount: data[4],
217
- tableList: tableList,
218
- };
219
- let tableEntry = [];
220
- let counter = 0;
221
- for (let i = 5; i < response.tableListCount * 22 + 5; i++) {
222
- // one tableentry = 22 bytes
223
- tableEntry.push(data[i]);
224
- counter++;
225
- if (counter === 22) {
226
- response.tableList.push(Buffer.from(tableEntry));
227
- tableEntry = [];
228
- counter = 0;
229
- }
230
- }
231
- logger_1.logger.debug('LQI RESPONSE - addr: ' +
232
- networkAddress.toString(16) +
233
- ' status: ' +
234
- response.status +
235
- ' read ' +
236
- (response.tableListCount + response.startIndex) +
237
- '/' +
238
- response.tableEntrys +
239
- ' entrys', NS);
240
- return response;
241
- }
242
- catch (error) {
243
- const msg = 'LQI REQUEST FAILED - addr: 0x' + networkAddress.toString(16) + ' ' + error;
244
- logger_1.logger.error(msg, NS);
245
- throw new Error(msg);
246
249
  }
247
- };
248
- let response = await request(0);
249
- add(response.tableList);
250
- let nextStartIndex = response.tableListCount;
251
- while (neighbors.length < response.tableEntrys) {
252
- response = await request(nextStartIndex);
253
- add(response.tableList);
254
- nextStartIndex += response.tableListCount;
250
+ return [payload.routingTableEntries, payload.entryList.length];
255
251
  }
256
- return { neighbors };
257
- }, networkAddress);
258
- }
259
- // @TODO
260
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
261
- routingTable(networkAddress) {
262
- return Promise.resolve({ table: [] });
252
+ else {
253
+ // TODO: will disappear once moved upstream
254
+ throw new Zdo.StatusError(result[0]);
255
+ }
256
+ };
257
+ let [tableEntries, entryCount] = await request(0);
258
+ const size = tableEntries;
259
+ let nextStartIndex = entryCount;
260
+ while (table.length < size) {
261
+ [tableEntries, entryCount] = await request(nextStartIndex);
262
+ nextStartIndex += entryCount;
263
+ }
264
+ return { table };
263
265
  }
264
266
  async nodeDescriptor(networkAddress) {
265
- return await this.queue.execute(async () => {
266
- try {
267
- const nodeDescriptorResponse = await this.driver.sendCommand(constants_1.ZiGateCommandCode.NodeDescriptor, {
268
- targetShortAddress: networkAddress,
269
- });
270
- const data = nodeDescriptorResponse.payload.payload;
271
- const buf = data;
272
- const logicaltype = data[4] & 7;
273
- let type = 'Unknown';
274
- switch (logicaltype) {
275
- case 1:
276
- type = 'Router';
277
- break;
278
- case 2:
279
- type = 'EndDevice';
280
- break;
281
- case 0:
282
- type = 'Coordinator';
283
- break;
284
- }
285
- const manufacturer = buf.readUInt16LE(7);
286
- logger_1.logger.debug('RECEIVING NODE_DESCRIPTOR - addr: 0x' +
287
- networkAddress.toString(16) +
288
- ' type: ' +
289
- type +
290
- ' manufacturer: 0x' +
291
- manufacturer.toString(16), NS);
292
- return { manufacturerCode: manufacturer, type };
267
+ const clusterId = Zdo.ClusterId.NODE_DESCRIPTOR_REQUEST;
268
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, networkAddress);
269
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, networkAddress, clusterId, zdoPayload, false);
270
+ /* istanbul ignore else */
271
+ if (Zdo.Buffalo.checkStatus(result)) {
272
+ const payload = result[1];
273
+ let type = 'Unknown';
274
+ switch (payload.logicalType) {
275
+ case 0x0:
276
+ type = 'Coordinator';
277
+ break;
278
+ case 0x1:
279
+ type = 'Router';
280
+ break;
281
+ case 0x2:
282
+ type = 'EndDevice';
283
+ break;
293
284
  }
294
- catch (error) {
295
- const msg = 'RECEIVING NODE_DESCRIPTOR FAILED - addr: 0x' + networkAddress.toString(16) + ' ' + error;
296
- logger_1.logger.error(msg, NS);
297
- throw new Error(msg);
298
- }
299
- }, networkAddress);
285
+ return { type, manufacturerCode: payload.manufacturerCode };
286
+ }
287
+ else {
288
+ // TODO: will disappear once moved upstream
289
+ throw new Zdo.StatusError(result[0]);
290
+ }
300
291
  }
301
292
  async activeEndpoints(networkAddress) {
302
- return await this.queue.execute(async () => {
303
- const payload = {
304
- targetShortAddress: networkAddress,
305
- };
306
- try {
307
- const result = await this.driver.sendCommand(constants_1.ZiGateCommandCode.ActiveEndpoint, payload);
308
- const buf = Buffer.from(result.payload.payload);
309
- const epCount = buf.readUInt8(4);
310
- const epList = [];
311
- for (let i = 5; i < epCount + 5; i++) {
312
- epList.push(buf.readUInt8(i));
313
- }
314
- const payloadAE = {
315
- endpoints: epList,
316
- };
317
- logger_1.logger.debug(() => `ActiveEndpoints response: ${JSON.stringify(payloadAE)}`, NS);
318
- return payloadAE;
319
- }
320
- catch (error) {
321
- logger_1.logger.error(`RECEIVING ActiveEndpoints FAILED, ${error}`, NS);
322
- throw new Error('RECEIVING ActiveEndpoints FAILED ' + error);
323
- }
324
- }, networkAddress);
293
+ const clusterId = Zdo.ClusterId.ACTIVE_ENDPOINTS_REQUEST;
294
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, networkAddress);
295
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, networkAddress, clusterId, zdoPayload, false);
296
+ /* istanbul ignore else */
297
+ if (Zdo.Buffalo.checkStatus(result)) {
298
+ const payload = result[1];
299
+ return { endpoints: payload.endpointList };
300
+ }
301
+ else {
302
+ // TODO: will disappear once moved upstream
303
+ throw new Zdo.StatusError(result[0]);
304
+ }
325
305
  }
326
306
  async simpleDescriptor(networkAddress, endpointID) {
327
- return await this.queue.execute(async () => {
328
- try {
329
- const payload = {
330
- targetShortAddress: networkAddress,
331
- endpoint: endpointID,
332
- };
333
- const result = await this.driver.sendCommand(constants_1.ZiGateCommandCode.SimpleDescriptor, payload);
334
- const buf = result.payload.payload;
335
- if (buf.length > 11) {
336
- const inCount = buf.readUInt8(11);
337
- const inClusters = [];
338
- let cIndex = 12;
339
- for (let i = 0; i < inCount; i++) {
340
- inClusters[i] = buf.readUInt16LE(cIndex);
341
- cIndex += 2;
342
- }
343
- const outCount = buf.readUInt8(12 + inCount * 2);
344
- const outClusters = [];
345
- cIndex = 13 + inCount * 2;
346
- for (let l = 0; l < outCount; l++) {
347
- outClusters[l] = buf.readUInt16LE(cIndex);
348
- cIndex += 2;
349
- }
350
- const resultPayload = {
351
- profileID: buf.readUInt16LE(6),
352
- endpointID: buf.readUInt8(5),
353
- deviceID: buf.readUInt16LE(8),
354
- inputClusters: inClusters,
355
- outputClusters: outClusters,
356
- };
357
- return resultPayload;
358
- }
359
- throw new Error(`Invalid buffer length ${buf.length}.`);
360
- }
361
- catch (error) {
362
- const msg = 'RECEIVING SIMPLE_DESCRIPTOR FAILED - addr: 0x' + networkAddress.toString(16) + ' EP:' + endpointID + ' ' + error;
363
- logger_1.logger.error(msg, NS);
364
- throw new Error(msg);
365
- }
366
- }, networkAddress);
307
+ const clusterId = Zdo.ClusterId.SIMPLE_DESCRIPTOR_REQUEST;
308
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, networkAddress, endpointID);
309
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, networkAddress, clusterId, zdoPayload, false);
310
+ /* istanbul ignore else */
311
+ if (Zdo.Buffalo.checkStatus(result)) {
312
+ const payload = result[1];
313
+ return {
314
+ profileID: payload.profileId,
315
+ endpointID: payload.endpoint,
316
+ deviceID: payload.deviceId,
317
+ inputClusters: payload.inClusterList,
318
+ outputClusters: payload.outClusterList,
319
+ };
320
+ }
321
+ else {
322
+ // TODO: will disappear once moved upstream
323
+ throw new Zdo.StatusError(result[0]);
324
+ }
367
325
  }
368
326
  async bind(destinationNetworkAddress, sourceIeeeAddress, sourceEndpoint, clusterID, destinationAddressOrGroup, type, destinationEndpoint) {
369
- return await this.queue.execute(async () => {
370
- const payload = {
371
- targetExtendedAddress: sourceIeeeAddress,
372
- targetEndpoint: sourceEndpoint,
373
- clusterID: clusterID,
374
- destinationAddressMode: type === 'group' ? constants_1.ADDRESS_MODE.group : constants_1.ADDRESS_MODE.ieee,
375
- destinationAddress: destinationAddressOrGroup,
376
- };
377
- if (destinationEndpoint != undefined) {
378
- payload.destinationEndpoint = destinationEndpoint;
379
- }
380
- const result = await this.driver.sendCommand(constants_1.ZiGateCommandCode.Bind, payload, undefined, { destinationNetworkAddress });
381
- const data = result.payload.payload;
382
- if (data[1] === 0) {
383
- logger_1.logger.debug(`Bind ${sourceIeeeAddress} success`, NS);
384
- }
385
- else {
386
- const msg = `Bind ${sourceIeeeAddress} failed`;
387
- logger_1.logger.error(msg, NS);
388
- throw new Error(msg);
389
- }
390
- }, destinationNetworkAddress);
327
+ const clusterId = Zdo.ClusterId.BIND_REQUEST;
328
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, sourceIeeeAddress, sourceEndpoint, clusterID, type === 'group' ? Zdo.MULTICAST_BINDING : Zdo.UNICAST_BINDING, destinationAddressOrGroup, // not used with MULTICAST_BINDING
329
+ destinationAddressOrGroup, // not used with UNICAST_BINDING
330
+ destinationEndpoint ?? 0);
331
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, destinationNetworkAddress, clusterId, zdoPayload, false);
332
+ /* istanbul ignore next */
333
+ if (!Zdo.Buffalo.checkStatus(result)) {
334
+ // TODO: will disappear once moved upstream
335
+ throw new Zdo.StatusError(result[0]);
336
+ }
391
337
  }
392
338
  async unbind(destinationNetworkAddress, sourceIeeeAddress, sourceEndpoint, clusterID, destinationAddressOrGroup, type, destinationEndpoint) {
393
- return await this.queue.execute(async () => {
394
- const payload = {
395
- targetExtendedAddress: sourceIeeeAddress,
396
- targetEndpoint: sourceEndpoint,
397
- clusterID: clusterID,
398
- destinationAddressMode: type === 'group' ? constants_1.ADDRESS_MODE.group : constants_1.ADDRESS_MODE.ieee,
399
- destinationAddress: destinationAddressOrGroup,
400
- };
401
- if (destinationEndpoint != undefined) {
402
- payload.destinationEndpoint = destinationEndpoint;
403
- }
404
- const result = await this.driver.sendCommand(constants_1.ZiGateCommandCode.UnBind, payload, undefined, { destinationNetworkAddress });
405
- const data = result.payload.payload;
406
- if (data[1] === 0) {
407
- logger_1.logger.debug(`Unbind ${sourceIeeeAddress} success`, NS);
408
- }
409
- else {
410
- const msg = `Unbind ${sourceIeeeAddress} failed`;
411
- logger_1.logger.error(msg, NS);
412
- throw new Error(msg);
413
- }
414
- }, destinationNetworkAddress);
339
+ const clusterId = Zdo.ClusterId.UNBIND_REQUEST;
340
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, sourceIeeeAddress, sourceEndpoint, clusterID, type === 'group' ? Zdo.MULTICAST_BINDING : Zdo.UNICAST_BINDING, destinationAddressOrGroup, // not used with MULTICAST_BINDING
341
+ destinationAddressOrGroup, // not used with UNICAST_BINDING
342
+ destinationEndpoint ?? 0);
343
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, destinationNetworkAddress, clusterId, zdoPayload, false);
344
+ /* istanbul ignore next */
345
+ if (!Zdo.Buffalo.checkStatus(result)) {
346
+ // TODO: will disappear once moved upstream
347
+ throw new Zdo.StatusError(result[0]);
348
+ }
415
349
  }
416
350
  async removeDevice(networkAddress, ieeeAddr) {
351
+ const clusterId = Zdo.ClusterId.LEAVE_REQUEST;
352
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, ieeeAddr, Zdo.LeaveRequestFlags.WITHOUT_REJOIN);
353
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, networkAddress, clusterId, zdoPayload, false);
354
+ /* istanbul ignore next */
355
+ if (!Zdo.Buffalo.checkStatus(result)) {
356
+ // TODO: will disappear once moved upstream
357
+ throw new Zdo.StatusError(result[0]);
358
+ }
359
+ }
360
+ async sendZdo(ieeeAddress, networkAddress, clusterId, payload, disableResponse) {
417
361
  return await this.queue.execute(async () => {
418
- const payload = {
419
- shortAddress: networkAddress,
420
- extendedAddress: ieeeAddr,
421
- rejoin: 0,
422
- removeChildren: 0,
423
- };
424
- try {
425
- await this.driver.sendCommand(constants_1.ZiGateCommandCode.ManagementLeaveRequest, payload);
362
+ // stack-specific requirements
363
+ // https://zigate.fr/documentation/commandes-zigate/
364
+ switch (clusterId) {
365
+ case Zdo.ClusterId.LEAVE_REQUEST: {
366
+ const prefixedPayload = Buffer.alloc(payload.length + 3); // extra zero for `removeChildren`
367
+ prefixedPayload.writeUInt16BE(networkAddress, 0);
368
+ prefixedPayload.set(payload, 2);
369
+ payload = prefixedPayload;
370
+ break;
371
+ }
372
+ case Zdo.ClusterId.BIND_REQUEST:
373
+ case Zdo.ClusterId.UNBIND_REQUEST: {
374
+ // extra zeroes for endpoint XXX: not needed?
375
+ const zeroes = 15 - payload.length;
376
+ const prefixedPayload = Buffer.alloc(payload.length + zeroes);
377
+ prefixedPayload.set(payload, 0);
378
+ payload = prefixedPayload;
379
+ break;
380
+ }
381
+ case Zdo.ClusterId.PERMIT_JOINING_REQUEST:
382
+ case Zdo.ClusterId.SYSTEM_SERVER_DISCOVERY_REQUEST:
383
+ case Zdo.ClusterId.LQI_TABLE_REQUEST:
384
+ case Zdo.ClusterId.ROUTING_TABLE_REQUEST:
385
+ case Zdo.ClusterId.BINDING_TABLE_REQUEST:
386
+ case Zdo.ClusterId.NWK_UPDATE_REQUEST: {
387
+ const prefixedPayload = Buffer.alloc(payload.length + 2);
388
+ prefixedPayload.writeUInt16BE(networkAddress, 0);
389
+ prefixedPayload.set(payload, 2);
390
+ payload = prefixedPayload;
391
+ break;
392
+ }
426
393
  }
427
- catch (error) {
428
- new Error(`ManagementLeaveRequest failed ${error}`);
394
+ let waiter;
395
+ if (!disableResponse) {
396
+ const responseClusterId = Zdo.Utils.getResponseClusterId(clusterId);
397
+ if (responseClusterId) {
398
+ waiter = this.driver.zdoWaitFor({
399
+ clusterId: responseClusterId,
400
+ target: responseClusterId === Zdo.ClusterId.NETWORK_ADDRESS_RESPONSE ? ieeeAddress : networkAddress,
401
+ });
402
+ }
429
403
  }
430
- }, networkAddress);
404
+ await this.driver.requestZdo(clusterId, payload);
405
+ if (waiter) {
406
+ const result = await waiter.start().promise;
407
+ return result.zdo;
408
+ }
409
+ }, networkAddress /* TODO: replace with ieeeAddress once zdo moved upstream */);
431
410
  }
432
411
  async sendZclFrameToEndpoint(ieeeAddr, networkAddress, endpoint, zclFrame, timeout, disableResponse, disableRecovery, sourceEndpoint) {
433
412
  return await this.queue.execute(async () => {
@@ -442,9 +421,9 @@ class ZiGateAdapter extends adapter_1.default {
442
421
  const payload = {
443
422
  addressMode: constants_1.ADDRESS_MODE.short, //nwk
444
423
  targetShortAddress: networkAddress,
445
- sourceEndpoint: sourceEndpoint || 0x01,
424
+ sourceEndpoint: sourceEndpoint || ZSpec.HA_ENDPOINT,
446
425
  destinationEndpoint: endpoint,
447
- profileID: 0x0104,
426
+ profileID: ZSpec.HA_PROFILE_ID,
448
427
  clusterID: zclFrame.cluster.ID,
449
428
  securityMode: 0x02,
450
429
  radius: 30,
@@ -497,7 +476,7 @@ class ZiGateAdapter extends adapter_1.default {
497
476
  targetShortAddress: destination,
498
477
  sourceEndpoint: sourceEndpoint,
499
478
  destinationEndpoint: endpoint,
500
- profileID: /*sourceEndpoint === 242 ? 0xa1e0 :*/ 0x0104,
479
+ profileID: /*sourceEndpoint === ZSpec.GP_ENDPOINT ? ZSpec.GP_PROFILE_ID :*/ ZSpec.HA_PROFILE_ID,
501
480
  clusterID: zclFrame.cluster.ID,
502
481
  securityMode: 0x02,
503
482
  radius: 30,
@@ -515,9 +494,9 @@ class ZiGateAdapter extends adapter_1.default {
515
494
  const payload = {
516
495
  addressMode: constants_1.ADDRESS_MODE.group, //nwk
517
496
  targetShortAddress: groupID,
518
- sourceEndpoint: sourceEndpoint || 0x01,
497
+ sourceEndpoint: sourceEndpoint || ZSpec.HA_ENDPOINT,
519
498
  destinationEndpoint: 0xff,
520
- profileID: 0x0104,
499
+ profileID: ZSpec.HA_PROFILE_ID,
521
500
  clusterID: zclFrame.cluster.ID,
522
501
  securityMode: 0x02,
523
502
  radius: 30,
@@ -533,7 +512,9 @@ class ZiGateAdapter extends adapter_1.default {
533
512
  */
534
513
  async initNetwork() {
535
514
  logger_1.logger.debug(`Set channel mask ${this.networkOptions.channelList} key`, NS);
536
- await this.driver.sendCommand(constants_1.ZiGateCommandCode.SetChannelMask, { channelMask: channelsToMask(this.networkOptions.channelList) });
515
+ await this.driver.sendCommand(constants_1.ZiGateCommandCode.SetChannelMask, {
516
+ channelMask: ZSpec.Utils.channelsToUInt32Mask(this.networkOptions.channelList),
517
+ });
537
518
  logger_1.logger.debug(`Set security key`, NS);
538
519
  await this.driver.sendCommand(constants_1.ZiGateCommandCode.SetSecurityStateKey, {
539
520
  keyType: this.networkOptions.networkKeyDistribute
@@ -591,36 +572,40 @@ class ZiGateAdapter extends adapter_1.default {
591
572
  restoreChannelInterPAN() {
592
573
  throw new Error('Not supported');
593
574
  }
594
- deviceAnnounceListener(networkAddress, ieeeAddr) {
575
+ deviceAnnounceListener(response) {
595
576
  // @todo debounce
596
- const payload = { networkAddress, ieeeAddr };
577
+ const payload = { networkAddress: response.nwkAddress, ieeeAddr: response.eui64 };
597
578
  if (this.joinPermitted === true) {
598
579
  this.emit('deviceJoined', payload);
599
580
  }
600
581
  else {
601
- this.emit('deviceAnnounce', payload);
582
+ // convert to `zdoResponse` to avoid needing extra event upstream
583
+ this.emit('zdoResponse', Zdo.ClusterId.END_DEVICE_ANNOUNCE, [Zdo.Status.SUCCESS, response]);
602
584
  }
603
585
  }
604
- dataListener(data) {
586
+ onZdoResponse(clusterId, response) {
587
+ this.emit('zdoResponse', clusterId, response);
588
+ }
589
+ dataListener(ziGateObject) {
605
590
  const payload = {
606
- address: data.ziGateObject.payload.sourceAddress,
607
- clusterID: data.ziGateObject.payload.clusterID,
608
- data: data.ziGateObject.payload.payload,
609
- header: Zcl.Header.fromBuffer(data.ziGateObject.payload.payload),
610
- endpoint: data.ziGateObject.payload.sourceEndpoint,
611
- linkquality: data.ziGateObject.frame.readRSSI(), // read: frame valid
591
+ address: ziGateObject.payload.sourceAddress,
592
+ clusterID: ziGateObject.payload.clusterID,
593
+ data: ziGateObject.payload.payload,
594
+ header: Zcl.Header.fromBuffer(ziGateObject.payload.payload),
595
+ endpoint: ziGateObject.payload.sourceEndpoint,
596
+ linkquality: ziGateObject.frame.readRSSI(), // read: frame valid
612
597
  groupID: 0, // @todo
613
598
  wasBroadcast: false, // TODO
614
- destinationEndpoint: data.ziGateObject.payload.destinationEndpoint,
599
+ destinationEndpoint: ziGateObject.payload.destinationEndpoint,
615
600
  };
616
601
  this.waitress.resolve(payload);
617
602
  this.emit('zclPayload', payload);
618
603
  }
619
- leaveIndicationListener(data) {
620
- logger_1.logger.debug(() => `LeaveIndication ${JSON.stringify(data)}`, NS);
604
+ leaveIndicationListener(ziGateObject) {
605
+ logger_1.logger.debug(() => `LeaveIndication ${JSON.stringify(ziGateObject)}`, NS);
621
606
  const payload = {
622
- networkAddress: data.ziGateObject.payload.extendedAddress,
623
- ieeeAddr: data.ziGateObject.payload.extendedAddress,
607
+ networkAddress: ziGateObject.payload.extendedAddress,
608
+ ieeeAddr: ziGateObject.payload.extendedAddress,
624
609
  };
625
610
  this.emit('deviceLeave', payload);
626
611
  }