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
@@ -30,7 +30,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
30
30
  const assert_1 = __importDefault(require("assert"));
31
31
  const utils_1 = require("../../../utils");
32
32
  const logger_1 = require("../../../utils/logger");
33
+ const ZSpec = __importStar(require("../../../zspec"));
33
34
  const Zcl = __importStar(require("../../../zspec/zcl"));
35
+ const Zdo = __importStar(require("../../../zspec/zdo"));
34
36
  const adapter_1 = __importDefault(require("../../adapter"));
35
37
  const serialPortUtils_1 = __importDefault(require("../../serialPortUtils"));
36
38
  const socketPortUtils_1 = __importDefault(require("../../socketPortUtils"));
@@ -47,7 +49,6 @@ class EZSPAdapter extends adapter_1.default {
47
49
  interpanLock;
48
50
  queue;
49
51
  closing;
50
- deprecatedTimer;
51
52
  constructor(networkOptions, serialPortOptions, backupPath, adapterOptions) {
52
53
  super(networkOptions, serialPortOptions, backupPath, adapterOptions);
53
54
  this.hasZdoMessageOverhead = true;
@@ -65,18 +66,12 @@ class EZSPAdapter extends adapter_1.default {
65
66
  }
66
67
  async processMessage(frame) {
67
68
  logger_1.logger.debug(() => `processMessage: ${JSON.stringify(frame)}`, NS);
68
- if (frame.apsFrame.profileId == 0) {
69
- if (frame.apsFrame.clusterId == types_1.EmberZDOCmd.Device_annce && frame.apsFrame.destinationEndpoint == 0) {
70
- let nwk, rst, ieee;
71
- // eslint-disable-next-line prefer-const
72
- [nwk, rst] = types_1.uint16_t.deserialize(types_1.uint16_t, frame.message.subarray(1));
73
- [ieee, rst] = types_1.EmberEUI64.deserialize(types_1.EmberEUI64, rst);
74
- ieee = new types_1.EmberEUI64(ieee);
75
- logger_1.logger.debug(`ZDO Device announce: ${nwk}, ${ieee.toString()}`, NS);
76
- this.driver.handleNodeJoined(nwk, ieee);
69
+ if (frame.apsFrame.profileId == Zdo.ZDO_PROFILE_ID) {
70
+ if (frame.apsFrame.clusterId >= 0x8000 /* response only */) {
71
+ this.emit('zdoResponse', frame.apsFrame.clusterId, frame.zdoResponse);
77
72
  }
78
73
  }
79
- else if (frame.apsFrame.profileId == 260 || frame.apsFrame.profileId == 0xffff) {
74
+ else if (frame.apsFrame.profileId == ZSpec.HA_PROFILE_ID || frame.apsFrame.profileId == 0xffff) {
80
75
  const payload = {
81
76
  clusterID: frame.apsFrame.clusterId,
82
77
  header: Zcl.Header.fromBuffer(frame.message),
@@ -91,7 +86,7 @@ class EZSPAdapter extends adapter_1.default {
91
86
  this.waitress.resolve(payload);
92
87
  this.emit('zclPayload', payload);
93
88
  }
94
- else if (frame.apsFrame.profileId == 0xc05e && frame.senderEui64) {
89
+ else if (frame.apsFrame.profileId == ZSpec.TOUCHLINK_PROFILE_ID && frame.senderEui64) {
95
90
  // ZLL Frame
96
91
  const payload = {
97
92
  clusterID: frame.apsFrame.clusterId,
@@ -107,12 +102,12 @@ class EZSPAdapter extends adapter_1.default {
107
102
  this.waitress.resolve(payload);
108
103
  this.emit('zclPayload', payload);
109
104
  }
110
- else if (frame.apsFrame.profileId == 0xa1e0) {
105
+ else if (frame.apsFrame.profileId == ZSpec.GP_PROFILE_ID) {
111
106
  // GP Frame
112
107
  // Only handle when clusterId == 33 (greenPower), some devices send messages with this profileId
113
108
  // while the cluster is not greenPower
114
109
  // https://github.com/Koenkk/zigbee2mqtt/issues/20838
115
- if (frame.apsFrame.clusterId === 33) {
110
+ if (frame.apsFrame.clusterId === Zcl.Clusters.greenPower.ID) {
116
111
  const payload = {
117
112
  header: Zcl.Header.fromBuffer(frame.message),
118
113
  clusterID: frame.apsFrame.clusterId,
@@ -132,47 +127,29 @@ class EZSPAdapter extends adapter_1.default {
132
127
  }
133
128
  }
134
129
  }
135
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
- async handleDeviceJoin(arr) {
137
- const [nwk, ieee] = arr;
138
- logger_1.logger.debug(`Device join request received: ${nwk} ${ieee.toString('hex')}`, NS);
139
- const payload = {
130
+ async handleDeviceJoin(nwk, ieee) {
131
+ logger_1.logger.debug(() => `Device join request received: ${nwk} ${ieee.toString()}`, NS);
132
+ this.emit('deviceJoined', {
140
133
  networkAddress: nwk,
141
- ieeeAddr: `0x${ieee.toString('hex')}`,
142
- };
143
- if (nwk == 0) {
144
- await this.nodeDescriptor(nwk);
145
- }
146
- else {
147
- this.emit('deviceJoined', payload);
148
- }
134
+ ieeeAddr: `0x${ieee.toString()}`,
135
+ });
149
136
  }
150
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
151
- handleDeviceLeft(arr) {
152
- const [nwk, ieee] = arr;
153
- logger_1.logger.debug(`Device left network request received: ${nwk} ${ieee}`, NS);
154
- const payload = {
137
+ handleDeviceLeft(nwk, ieee) {
138
+ logger_1.logger.debug(() => `Device left network request received: ${nwk} ${ieee.toString()}`, NS);
139
+ this.emit('deviceLeave', {
155
140
  networkAddress: nwk,
156
- ieeeAddr: `0x${ieee.toString('hex')}`,
157
- };
158
- this.emit('deviceLeave', payload);
141
+ ieeeAddr: `0x${ieee.toString()}`,
142
+ });
159
143
  }
160
144
  /**
161
145
  * Adapter methods
162
146
  */
163
147
  async start() {
164
- const logEzspDeprecated = () => {
165
- const message = `Deprecated driver 'ezsp' currently in use, 'ember' will become the officially supported EmberZNet ` +
166
- `driver in next release. If using Zigbee2MQTT see https://github.com/Koenkk/zigbee2mqtt/discussions/21462`;
167
- logger_1.logger.warning(message, NS);
168
- };
169
- logEzspDeprecated();
170
- this.deprecatedTimer = setInterval(logEzspDeprecated, 60 * 60 * 1000); // Every 60 mins
148
+ logger_1.logger.warning(`'ezsp' driver is deprecated and will only remain to provide support for older firmware (pre 7.4.x). Migration to 'ember' is recommended. If using Zigbee2MQTT see https://github.com/Koenkk/zigbee2mqtt/discussions/21462`, NS);
171
149
  return await this.driver.startup();
172
150
  }
173
151
  async stop() {
174
152
  this.closing = true;
175
- clearInterval(this.deprecatedTimer);
176
153
  await this.driver.stop();
177
154
  }
178
155
  async onDriverClose() {
@@ -202,27 +179,20 @@ class EZSPAdapter extends adapter_1.default {
202
179
  async getCoordinator() {
203
180
  return await this.queue.execute(async () => {
204
181
  this.checkInterpanLock();
205
- const networkAddress = 0x0000;
206
- const message = await this.driver.zdoRequest(networkAddress, types_1.EmberZDOCmd.Active_EP_req, types_1.EmberZDOCmd.Active_EP_rsp, {
207
- dstaddr: networkAddress,
208
- });
209
- const activeEndpoints = message.activeeplist;
182
+ const message = await this.activeEndpoints(ZSpec.COORDINATOR_ADDRESS);
210
183
  const endpoints = [];
211
- for (const endpoint of activeEndpoints) {
212
- const descriptor = await this.driver.zdoRequest(networkAddress, types_1.EmberZDOCmd.Simple_Desc_req, types_1.EmberZDOCmd.Simple_Desc_rsp, {
213
- dstaddr: networkAddress,
214
- targetEp: endpoint,
215
- });
184
+ for (const endpoint of message.endpoints) {
185
+ const descriptor = await this.simpleDescriptor(ZSpec.COORDINATOR_ADDRESS, endpoint);
216
186
  endpoints.push({
217
- profileID: descriptor.descriptor.profileid,
218
- ID: descriptor.descriptor.endpoint,
219
- deviceID: descriptor.descriptor.deviceid,
220
- inputClusters: descriptor.descriptor.inclusterlist,
221
- outputClusters: descriptor.descriptor.outclusterlist,
187
+ profileID: descriptor.profileID,
188
+ ID: descriptor.endpointID,
189
+ deviceID: descriptor.deviceID,
190
+ inputClusters: descriptor.inputClusters,
191
+ outputClusters: descriptor.outputClusters,
222
192
  });
223
193
  }
224
194
  return {
225
- networkAddress: networkAddress,
195
+ networkAddress: ZSpec.COORDINATOR_ADDRESS,
226
196
  manufacturerID: 0,
227
197
  ieeeAddr: `0x${this.driver.ieee.toString()}`,
228
198
  endpoints,
@@ -230,20 +200,42 @@ class EZSPAdapter extends adapter_1.default {
230
200
  });
231
201
  }
232
202
  async permitJoin(seconds, networkAddress) {
233
- if (this.driver.ezsp.isInitialized()) {
234
- return await this.queue.execute(async () => {
203
+ if (!this.driver.ezsp.isInitialized()) {
204
+ return;
205
+ }
206
+ const clusterId = Zdo.ClusterId.PERMIT_JOINING_REQUEST;
207
+ if (networkAddress) {
208
+ // specific device that is not `Coordinator`
209
+ await this.queue.execute(async () => {
235
210
  this.checkInterpanLock();
236
211
  await this.driver.preJoining(seconds);
237
- if (networkAddress) {
238
- const result = await this.driver.zdoRequest(networkAddress, types_1.EmberZDOCmd.Mgmt_Permit_Joining_req, types_1.EmberZDOCmd.Mgmt_Permit_Joining_rsp, { duration: seconds, tcSignificant: false });
239
- if (result.status !== types_1.EmberStatus.SUCCESS) {
240
- throw new Error(`permitJoin for '${networkAddress}' failed`);
241
- }
242
- }
243
- else {
244
- await this.driver.permitJoining(seconds);
245
- }
246
212
  });
213
+ // `authentication`: TC significance always 1 (zb specs)
214
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, seconds, 1, []);
215
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, networkAddress, clusterId, zdoPayload, false);
216
+ /* istanbul ignore next */
217
+ if (!Zdo.Buffalo.checkStatus(result)) {
218
+ // TODO: will disappear once moved upstream
219
+ throw new Zdo.StatusError(result[0]);
220
+ }
221
+ }
222
+ else {
223
+ // coordinator-only (0), or all
224
+ await this.queue.execute(async () => {
225
+ this.checkInterpanLock();
226
+ await this.driver.preJoining(seconds);
227
+ });
228
+ const result = await this.driver.permitJoining(seconds);
229
+ if (result.status !== types_1.EmberStatus.SUCCESS) {
230
+ throw new Error(`[ZDO] Failed coordinator permit joining request with status=${result.status}.`);
231
+ }
232
+ logger_1.logger.debug(`Permit joining on coordinator for ${seconds} sec.`, NS);
233
+ // broadcast permit joining ZDO
234
+ if (networkAddress === undefined) {
235
+ // `authentication`: TC significance always 1 (zb specs)
236
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, seconds, 1, []);
237
+ await this.sendZdo(ZSpec.BLANK_EUI64, ZSpec.BroadcastAddress.DEFAULT, clusterId, zdoPayload, true);
238
+ }
247
239
  }
248
240
  }
249
241
  async getCoordinatorVersion() {
@@ -260,127 +252,169 @@ class EZSPAdapter extends adapter_1.default {
260
252
  return await Promise.reject(new Error('Not supported'));
261
253
  }
262
254
  async lqi(networkAddress) {
263
- return await this.queue.execute(async () => {
264
- this.checkInterpanLock();
265
- const neighbors = [];
266
- const request = async (startIndex) => {
267
- const result = await this.driver.zdoRequest(networkAddress, types_1.EmberZDOCmd.Mgmt_Lqi_req, types_1.EmberZDOCmd.Mgmt_Lqi_rsp, {
268
- startindex: startIndex,
269
- });
270
- if (result.status !== types_1.EmberStatus.SUCCESS) {
271
- throw new Error(`LQI for '${networkAddress}' failed with with status code ${result.status}`);
272
- }
273
- return result;
274
- };
275
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
276
- const add = (list) => {
277
- for (const entry of list) {
278
- this.driver.setNode(entry.nodeid, entry.ieee);
255
+ const clusterId = Zdo.ClusterId.LQI_TABLE_REQUEST;
256
+ const neighbors = [];
257
+ const request = async (startIndex) => {
258
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, startIndex);
259
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, networkAddress, clusterId, zdoPayload, false);
260
+ /* istanbul ignore else */
261
+ if (Zdo.Buffalo.checkStatus(result)) {
262
+ const payload = result[1];
263
+ for (const entry of payload.entryList) {
279
264
  neighbors.push({
265
+ ieeeAddr: entry.eui64,
266
+ networkAddress: entry.nwkAddress,
280
267
  linkquality: entry.lqi,
281
- networkAddress: entry.nodeid,
282
- ieeeAddr: `0x${new types_1.EmberEUI64(entry.ieee).toString()}`,
283
- relationship: (entry.packed >> 4) & 0x7,
268
+ relationship: entry.relationship,
284
269
  depth: entry.depth,
285
270
  });
286
271
  }
287
- };
288
- let response = await request(0);
289
- add(response.neighborlqilist.neighbors);
290
- const size = response.neighborlqilist.entries;
291
- let nextStartIndex = response.neighborlqilist.neighbors.length;
292
- while (neighbors.length < size) {
293
- response = await request(nextStartIndex);
294
- add(response.neighborlqilist.neighbors);
295
- nextStartIndex += response.neighborlqilist.neighbors.length;
272
+ return [payload.neighborTableEntries, payload.entryList.length];
296
273
  }
297
- return { neighbors };
298
- }, networkAddress);
274
+ else {
275
+ // TODO: will disappear once moved upstream
276
+ throw new Zdo.StatusError(result[0]);
277
+ }
278
+ };
279
+ let [tableEntries, entryCount] = await request(0);
280
+ const size = tableEntries;
281
+ let nextStartIndex = entryCount;
282
+ while (neighbors.length < size) {
283
+ [tableEntries, entryCount] = await request(nextStartIndex);
284
+ nextStartIndex += entryCount;
285
+ }
286
+ return { neighbors };
299
287
  }
300
288
  async routingTable(networkAddress) {
301
- return await this.queue.execute(async () => {
302
- this.checkInterpanLock();
303
- const table = [];
304
- const request = async (startIndex) => {
305
- const result = await this.driver.zdoRequest(networkAddress, types_1.EmberZDOCmd.Mgmt_Rtg_req, types_1.EmberZDOCmd.Mgmt_Rtg_rsp, {
306
- startindex: startIndex,
307
- });
308
- if (result.status !== types_1.EmberStatus.SUCCESS) {
309
- throw new Error(`Routing table for '${networkAddress}' failed with status code ${result.status}`);
310
- }
311
- return result;
312
- };
313
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
314
- const add = (list) => {
315
- for (const entry of list) {
289
+ const clusterId = Zdo.ClusterId.ROUTING_TABLE_REQUEST;
290
+ const table = [];
291
+ const request = async (startIndex) => {
292
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, startIndex);
293
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, networkAddress, clusterId, zdoPayload, false);
294
+ /* istanbul ignore else */
295
+ if (Zdo.Buffalo.checkStatus(result)) {
296
+ const payload = result[1];
297
+ for (const entry of payload.entryList) {
316
298
  table.push({
317
- destinationAddress: entry.destination,
299
+ destinationAddress: entry.destinationAddress,
318
300
  status: entry.status,
319
- nextHop: entry.nexthop,
301
+ nextHop: entry.nextHopAddress,
320
302
  });
321
303
  }
322
- };
323
- let response = await request(0);
324
- add(response.routingtablelist.table);
325
- const size = response.routingtablelist.entries;
326
- let nextStartIndex = response.routingtablelist.table.length;
327
- while (table.length < size) {
328
- response = await request(nextStartIndex);
329
- add(response.routingtablelist.table);
330
- nextStartIndex += response.routingtablelist.table.length;
304
+ return [payload.routingTableEntries, payload.entryList.length];
331
305
  }
332
- return { table };
333
- }, networkAddress);
306
+ else {
307
+ // TODO: will disappear once moved upstream
308
+ throw new Zdo.StatusError(result[0]);
309
+ }
310
+ };
311
+ let [tableEntries, entryCount] = await request(0);
312
+ const size = tableEntries;
313
+ let nextStartIndex = entryCount;
314
+ while (table.length < size) {
315
+ [tableEntries, entryCount] = await request(nextStartIndex);
316
+ nextStartIndex += entryCount;
317
+ }
318
+ return { table };
334
319
  }
335
320
  async nodeDescriptor(networkAddress) {
336
- return await this.queue.execute(async () => {
337
- this.checkInterpanLock();
338
- try {
339
- logger_1.logger.debug(`Requesting 'Node Descriptor' for '${networkAddress}'`, NS);
340
- const result = await this.nodeDescriptorInternal(networkAddress);
341
- return result;
342
- }
343
- catch (error) {
344
- logger_1.logger.debug(`Node descriptor request for '${networkAddress}' failed (${error}), retry`, NS);
345
- throw error;
321
+ const clusterId = Zdo.ClusterId.NODE_DESCRIPTOR_REQUEST;
322
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, networkAddress);
323
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, networkAddress, clusterId, zdoPayload, false);
324
+ /* istanbul ignore else */
325
+ if (Zdo.Buffalo.checkStatus(result)) {
326
+ const payload = result[1];
327
+ let type = 'Unknown';
328
+ switch (payload.logicalType) {
329
+ case 0x0:
330
+ type = 'Coordinator';
331
+ break;
332
+ case 0x1:
333
+ type = 'Router';
334
+ break;
335
+ case 0x2:
336
+ type = 'EndDevice';
337
+ break;
346
338
  }
347
- });
348
- }
349
- async nodeDescriptorInternal(networkAddress) {
350
- const descriptor = await this.driver.zdoRequest(networkAddress, types_1.EmberZDOCmd.Node_Desc_req, types_1.EmberZDOCmd.Node_Desc_rsp, {
351
- dstaddr: networkAddress,
352
- });
353
- const logicaltype = descriptor.descriptor.byte1 & 0x07;
354
- return {
355
- manufacturerCode: descriptor.descriptor.manufacturer_code,
356
- type: logicaltype == 0 ? 'Coordinator' : logicaltype == 1 ? 'Router' : 'EndDevice',
357
- };
339
+ return { type, manufacturerCode: payload.manufacturerCode };
340
+ }
341
+ else {
342
+ // TODO: will disappear once moved upstream
343
+ throw new Zdo.StatusError(result[0]);
344
+ }
358
345
  }
359
346
  async activeEndpoints(networkAddress) {
360
- logger_1.logger.debug(`Requesting 'Active endpoints' for '${networkAddress}'`, NS);
361
- return await this.queue.execute(async () => {
362
- const endpoints = await this.driver.zdoRequest(networkAddress, types_1.EmberZDOCmd.Active_EP_req, types_1.EmberZDOCmd.Active_EP_rsp, {
363
- dstaddr: networkAddress,
364
- });
365
- return { endpoints: [...endpoints.activeeplist] };
366
- }, networkAddress);
347
+ const clusterId = Zdo.ClusterId.ACTIVE_ENDPOINTS_REQUEST;
348
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, networkAddress);
349
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, networkAddress, clusterId, zdoPayload, false);
350
+ /* istanbul ignore else */
351
+ if (Zdo.Buffalo.checkStatus(result)) {
352
+ const payload = result[1];
353
+ return { endpoints: payload.endpointList };
354
+ }
355
+ else {
356
+ // TODO: will disappear once moved upstream
357
+ throw new Zdo.StatusError(result[0]);
358
+ }
367
359
  }
368
360
  async simpleDescriptor(networkAddress, endpointID) {
369
- logger_1.logger.debug(`Requesting 'Simple Descriptor' for '${networkAddress}' endpoint ${endpointID}`, NS);
370
- return await this.queue.execute(async () => {
371
- this.checkInterpanLock();
372
- const descriptor = await this.driver.zdoRequest(networkAddress, types_1.EmberZDOCmd.Simple_Desc_req, types_1.EmberZDOCmd.Simple_Desc_rsp, {
373
- dstaddr: networkAddress,
374
- targetEp: endpointID,
375
- });
361
+ const clusterId = Zdo.ClusterId.SIMPLE_DESCRIPTOR_REQUEST;
362
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, networkAddress, endpointID);
363
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, networkAddress, clusterId, zdoPayload, false);
364
+ /* istanbul ignore else */
365
+ if (Zdo.Buffalo.checkStatus(result)) {
366
+ const payload = result[1];
376
367
  return {
377
- profileID: descriptor.descriptor.profileid,
378
- endpointID: descriptor.descriptor.endpoint,
379
- deviceID: descriptor.descriptor.deviceid,
380
- inputClusters: descriptor.descriptor.inclusterlist,
381
- outputClusters: descriptor.descriptor.outclusterlist,
368
+ profileID: payload.profileId,
369
+ endpointID: payload.endpoint,
370
+ deviceID: payload.deviceId,
371
+ inputClusters: payload.inClusterList,
372
+ outputClusters: payload.outClusterList,
382
373
  };
383
- }, networkAddress);
374
+ }
375
+ else {
376
+ // TODO: will disappear once moved upstream
377
+ throw new Zdo.StatusError(result[0]);
378
+ }
379
+ }
380
+ async sendZdo(ieeeAddress, networkAddress, clusterId, payload, disableResponse) {
381
+ return await this.queue.execute(async () => {
382
+ this.checkInterpanLock();
383
+ const clusterName = Zdo.ClusterId[clusterId];
384
+ const frame = this.driver.makeApsFrame(clusterId, disableResponse);
385
+ payload[0] = frame.sequence;
386
+ let waiter;
387
+ let responseClusterId;
388
+ if (!disableResponse) {
389
+ responseClusterId = Zdo.Utils.getResponseClusterId(clusterId);
390
+ if (responseClusterId) {
391
+ waiter = this.driver.waitFor(responseClusterId === Zdo.ClusterId.NETWORK_ADDRESS_RESPONSE ? ieeeAddress : networkAddress, responseClusterId, frame.sequence);
392
+ }
393
+ }
394
+ if (ZSpec.Utils.isBroadcastAddress(networkAddress)) {
395
+ logger_1.logger.debug(() => `~~~> [ZDO ${clusterName} BROADCAST to=${networkAddress} payload=${payload.toString('hex')}]`, NS);
396
+ const req = await this.driver.brequest(networkAddress, frame, payload);
397
+ logger_1.logger.debug(`~~~> [SENT ZDO BROADCAST]`, NS);
398
+ if (!req) {
399
+ waiter?.cancel();
400
+ throw new Error(`~x~> [ZDO ${clusterName} BROADCAST to=${networkAddress}] Failed to send request.`);
401
+ }
402
+ }
403
+ else {
404
+ logger_1.logger.debug(() => `~~~> [ZDO ${clusterName} UNICAST to=${ieeeAddress}:${networkAddress} payload=${payload.toString('hex')}]`, NS);
405
+ const req = await this.driver.request(networkAddress, frame, payload);
406
+ logger_1.logger.debug(`~~~> [SENT ZDO UNICAST]`, NS);
407
+ if (!req) {
408
+ waiter?.cancel();
409
+ throw new Error(`~x~> [ZDO ${clusterName} UNICAST to=${ieeeAddress}:${networkAddress}] Failed to send request.`);
410
+ }
411
+ }
412
+ if (waiter && responseClusterId !== undefined) {
413
+ const response = await waiter.start().promise;
414
+ logger_1.logger.debug(() => `<~~ [ZDO ${Zdo.ClusterId[responseClusterId]} ${JSON.stringify(response.zdoResponse)}]`, NS);
415
+ return response.zdoResponse;
416
+ }
417
+ }, networkAddress /* TODO: replace with ieeeAddress once zdo moved upstream */);
384
418
  }
385
419
  async sendZclFrameToEndpoint(ieeeAddr, networkAddress, endpoint, zclFrame, timeout, disableResponse, disableRecovery, sourceEndpoint) {
386
420
  return await this.queue.execute(async () => {
@@ -403,7 +437,7 @@ class EZSPAdapter extends adapter_1.default {
403
437
  response = this.waitForInternal(networkAddress, endpoint, zclFrame.header.transactionSequenceNumber, zclFrame.cluster.ID, Zcl.Foundation.defaultRsp.ID, timeout);
404
438
  }
405
439
  const frame = this.driver.makeApsFrame(zclFrame.cluster.ID, disableResponse || zclFrame.header.frameControl.disableDefaultResponse);
406
- frame.profileId = 0x0104;
440
+ frame.profileId = ZSpec.HA_PROFILE_ID;
407
441
  frame.sourceEndpoint = sourceEndpoint || 0x01;
408
442
  frame.destinationEndpoint = endpoint;
409
443
  frame.groupId = 0;
@@ -435,7 +469,7 @@ class EZSPAdapter extends adapter_1.default {
435
469
  return await this.queue.execute(async () => {
436
470
  this.checkInterpanLock();
437
471
  const frame = this.driver.makeApsFrame(zclFrame.cluster.ID, false);
438
- frame.profileId = 0x0104;
472
+ frame.profileId = ZSpec.HA_PROFILE_ID;
439
473
  frame.sourceEndpoint = 0x01;
440
474
  frame.destinationEndpoint = 0x01;
441
475
  frame.groupId = groupID;
@@ -452,10 +486,12 @@ class EZSPAdapter extends adapter_1.default {
452
486
  return await this.queue.execute(async () => {
453
487
  this.checkInterpanLock();
454
488
  const frame = this.driver.makeApsFrame(zclFrame.cluster.ID, false);
455
- frame.profileId = sourceEndpoint === 242 && endpoint === 242 ? 0xa1e0 : 0x0104;
489
+ frame.profileId = sourceEndpoint === ZSpec.GP_ENDPOINT && endpoint === ZSpec.GP_ENDPOINT ? ZSpec.GP_PROFILE_ID : ZSpec.HA_PROFILE_ID;
456
490
  frame.sourceEndpoint = sourceEndpoint;
457
491
  frame.destinationEndpoint = endpoint;
458
492
  frame.groupId = destination;
493
+ // XXX: should be:
494
+ // await this.driver.brequest(destination, frame, zclFrame.toBuffer())
459
495
  await this.driver.mrequest(frame, zclFrame.toBuffer());
460
496
  /**
461
497
  * As a broadcast command is not confirmed and thus immidiately returns
@@ -466,73 +502,38 @@ class EZSPAdapter extends adapter_1.default {
466
502
  });
467
503
  }
468
504
  async bind(destinationNetworkAddress, sourceIeeeAddress, sourceEndpoint, clusterID, destinationAddressOrGroup, type, destinationEndpoint) {
469
- return await this.queue.execute(async () => {
470
- this.checkInterpanLock();
471
- const ieee = new types_1.EmberEUI64(sourceIeeeAddress);
472
- let destAddr;
473
- if (type === 'group') {
474
- // 0x01 = 16-bit group address for DstAddr and DstEndpoint not present
475
- destAddr = {
476
- addrmode: 0x01,
477
- nwk: destinationAddressOrGroup,
478
- };
479
- }
480
- else {
481
- // 0x03 = 64-bit extended address for DstAddr and DstEndpoint present
482
- destAddr = {
483
- addrmode: 0x03,
484
- ieee: new types_1.EmberEUI64(destinationAddressOrGroup),
485
- endpoint: destinationEndpoint,
486
- };
487
- this.driver.setNode(destinationNetworkAddress, destAddr.ieee);
488
- }
489
- await this.driver.zdoRequest(destinationNetworkAddress, types_1.EmberZDOCmd.Bind_req, types_1.EmberZDOCmd.Bind_rsp, {
490
- sourceEui: ieee,
491
- sourceEp: sourceEndpoint,
492
- clusterId: clusterID,
493
- destAddr: destAddr,
494
- });
495
- }, destinationNetworkAddress);
505
+ const clusterId = Zdo.ClusterId.BIND_REQUEST;
506
+ 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
507
+ destinationAddressOrGroup, // not used with UNICAST_BINDING
508
+ destinationEndpoint ?? 0);
509
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, destinationNetworkAddress, clusterId, zdoPayload, false);
510
+ /* istanbul ignore next */
511
+ if (!Zdo.Buffalo.checkStatus(result)) {
512
+ // TODO: will disappear once moved upstream
513
+ throw new Zdo.StatusError(result[0]);
514
+ }
496
515
  }
497
516
  async unbind(destinationNetworkAddress, sourceIeeeAddress, sourceEndpoint, clusterID, destinationAddressOrGroup, type, destinationEndpoint) {
498
- return await this.queue.execute(async () => {
499
- this.checkInterpanLock();
500
- const ieee = new types_1.EmberEUI64(sourceIeeeAddress);
501
- let destAddr;
502
- if (type === 'group') {
503
- // 0x01 = 16-bit group address for DstAddr and DstEndpoint not present
504
- destAddr = {
505
- addrmode: 0x01,
506
- nwk: destinationAddressOrGroup,
507
- };
508
- }
509
- else {
510
- // 0x03 = 64-bit extended address for DstAddr and DstEndpoint present
511
- destAddr = {
512
- addrmode: 0x03,
513
- ieee: new types_1.EmberEUI64(destinationAddressOrGroup),
514
- endpoint: destinationEndpoint,
515
- };
516
- this.driver.setNode(destinationNetworkAddress, destAddr.ieee);
517
- }
518
- await this.driver.zdoRequest(destinationNetworkAddress, types_1.EmberZDOCmd.Unbind_req, types_1.EmberZDOCmd.Unbind_rsp, {
519
- sourceEui: ieee,
520
- sourceEp: sourceEndpoint,
521
- clusterId: clusterID,
522
- destAddr: destAddr,
523
- });
524
- }, destinationNetworkAddress);
517
+ const clusterId = Zdo.ClusterId.UNBIND_REQUEST;
518
+ 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
519
+ destinationAddressOrGroup, // not used with UNICAST_BINDING
520
+ destinationEndpoint ?? 0);
521
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, destinationNetworkAddress, clusterId, zdoPayload, false);
522
+ /* istanbul ignore next */
523
+ if (!Zdo.Buffalo.checkStatus(result)) {
524
+ // TODO: will disappear once moved upstream
525
+ throw new Zdo.StatusError(result[0]);
526
+ }
525
527
  }
526
- removeDevice(networkAddress, ieeeAddr) {
527
- return this.queue.execute(async () => {
528
- this.checkInterpanLock();
529
- const ieee = new types_1.EmberEUI64(ieeeAddr);
530
- this.driver.setNode(networkAddress, ieee);
531
- await this.driver.zdoRequest(networkAddress, types_1.EmberZDOCmd.Mgmt_Leave_req, types_1.EmberZDOCmd.Mgmt_Leave_rsp, {
532
- destAddr: ieee,
533
- removechildrenRejoin: 0x00,
534
- });
535
- }, networkAddress);
528
+ async removeDevice(networkAddress, ieeeAddr) {
529
+ const clusterId = Zdo.ClusterId.LEAVE_REQUEST;
530
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, ieeeAddr, Zdo.LeaveRequestFlags.WITHOUT_REJOIN);
531
+ const result = await this.sendZdo(ZSpec.BLANK_EUI64, networkAddress, clusterId, zdoPayload, false);
532
+ /* istanbul ignore next */
533
+ if (!Zdo.Buffalo.checkStatus(result)) {
534
+ // TODO: will disappear once moved upstream
535
+ throw new Zdo.StatusError(result[0]);
536
+ }
536
537
  }
537
538
  async getNetworkParameters() {
538
539
  return {
@@ -606,9 +607,11 @@ class EZSPAdapter extends adapter_1.default {
606
607
  return await response.start().promise;
607
608
  });
608
609
  }
609
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
610
610
  async changeChannel(newChannel) {
611
- throw new Error(`Channel change is not supported for 'ezsp'`);
611
+ const clusterId = Zdo.ClusterId.NWK_UPDATE_REQUEST;
612
+ const zdoPayload = Zdo.Buffalo.buildRequest(this.hasZdoMessageOverhead, clusterId, [newChannel], 0xfe, undefined, undefined, undefined);
613
+ await this.sendZdo(ZSpec.BLANK_EUI64, ZSpec.BroadcastAddress.SLEEPY, clusterId, zdoPayload, true /* handled below */);
614
+ await (0, utils_1.Wait)(12000);
612
615
  }
613
616
  async setTransmitPower(value) {
614
617
  logger_1.logger.debug(`setTransmitPower to ${value}`, NS);