@willieee802/zigbee-herdsman 0.49.0 → 0.49.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.
Files changed (114) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +38 -0
  3. package/biome.json +1 -1
  4. package/dist/adapter/adapter.d.ts.map +1 -0
  5. package/dist/adapter/adapterDiscovery.d.ts.map +1 -0
  6. package/dist/adapter/const.d.ts.map +1 -0
  7. package/dist/adapter/deconz/adapter/deconzAdapter.d.ts.map +1 -0
  8. package/dist/adapter/ember/adapter/emberAdapter.d.ts.map +1 -0
  9. package/dist/adapter/ember/adapter/endpoints.d.ts.map +1 -0
  10. package/dist/adapter/ember/adapter/oneWaitress.d.ts.map +1 -0
  11. package/dist/adapter/ember/adapter/tokensManager.d.ts.map +1 -0
  12. package/dist/adapter/ember/ezsp/ezsp.d.ts.map +1 -0
  13. package/dist/adapter/ember/utils/initters.d.ts.map +1 -0
  14. package/dist/adapter/events.d.ts.map +1 -0
  15. package/dist/adapter/ezsp/adapter/backup.d.ts.map +1 -0
  16. package/dist/adapter/ezsp/adapter/ezspAdapter.d.ts.map +1 -0
  17. package/dist/adapter/ezsp/driver/driver.d.ts.map +1 -0
  18. package/dist/adapter/ezsp/driver/index.d.ts.map +1 -0
  19. package/dist/adapter/ezsp/driver/multicast.d.ts.map +1 -0
  20. package/dist/adapter/index.d.ts.map +1 -0
  21. package/dist/adapter/z-stack/adapter/endpoints.d.ts.map +1 -0
  22. package/dist/adapter/z-stack/adapter/manager.d.ts.map +1 -0
  23. package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts.map +1 -0
  24. package/dist/adapter/z-stack/models/startup-options.d.ts.map +1 -0
  25. package/dist/adapter/zboss/adapter/zbossAdapter.d.ts.map +1 -0
  26. package/dist/adapter/zboss/driver.d.ts.map +1 -0
  27. package/dist/adapter/zboss/frame.d.ts.map +1 -0
  28. package/dist/adapter/zboss/frame.js +200 -0
  29. package/dist/adapter/zboss/frame.js.map +1 -0
  30. package/dist/adapter/zboss/uart.d.ts.map +1 -0
  31. package/dist/adapter/zigate/adapter/zigateAdapter.d.ts.map +1 -0
  32. package/dist/adapter/zigate/driver/buffaloZiGate.d.ts.map +1 -0
  33. package/dist/adapter/zigate/driver/buffaloZiGate.js +198 -0
  34. package/dist/adapter/zigate/driver/buffaloZiGate.js.map +1 -0
  35. package/dist/adapter/zigate/driver/ziGateObject.d.ts.map +1 -0
  36. package/dist/adapter/zigate/driver/zigate.d.ts.map +1 -0
  37. package/dist/adapter/zoh/adapter/zohAdapter.d.ts.map +1 -0
  38. package/dist/controller/controller.d.ts.map +1 -0
  39. package/dist/controller/controller.js +874 -0
  40. package/dist/controller/controller.js.map +1 -0
  41. package/dist/controller/database.d.ts.map +1 -0
  42. package/dist/controller/events.d.ts.map +1 -0
  43. package/dist/controller/events.js +3 -0
  44. package/dist/controller/events.js.map +1 -0
  45. package/dist/controller/greenPower.d.ts.map +1 -0
  46. package/dist/controller/greenPower.js +425 -0
  47. package/dist/controller/greenPower.js.map +1 -0
  48. package/dist/controller/helpers/index.d.ts.map +1 -0
  49. package/dist/controller/helpers/ota.d.ts.map +1 -0
  50. package/dist/controller/helpers/ota.js +467 -0
  51. package/dist/controller/helpers/ota.js.map +1 -0
  52. package/dist/controller/helpers/request.d.ts.map +1 -0
  53. package/dist/controller/helpers/requestQueue.d.ts.map +1 -0
  54. package/dist/controller/helpers/zclFrameConverter.d.ts.map +1 -0
  55. package/dist/controller/helpers/zclFrameConverter.js +84 -0
  56. package/dist/controller/helpers/zclFrameConverter.js.map +1 -0
  57. package/dist/controller/index.d.ts.map +1 -0
  58. package/dist/controller/model/device.d.ts.map +1 -0
  59. package/dist/controller/model/device.js +1396 -0
  60. package/dist/controller/model/device.js.map +1 -0
  61. package/dist/controller/model/endpoint.d.ts.map +1 -0
  62. package/dist/controller/model/endpoint.js +822 -0
  63. package/dist/controller/model/endpoint.js.map +1 -0
  64. package/dist/controller/model/entity.d.ts.map +1 -0
  65. package/dist/controller/model/group.d.ts.map +1 -0
  66. package/dist/controller/model/group.js +343 -0
  67. package/dist/controller/model/group.js.map +1 -0
  68. package/dist/controller/model/index.d.ts.map +1 -0
  69. package/dist/controller/model/zigbeeEntity.d.ts.map +1 -0
  70. package/dist/controller/touchlink.d.ts.map +1 -0
  71. package/dist/controller/tstype.d.ts.map +1 -0
  72. package/dist/controller/tstype.js +3 -0
  73. package/dist/controller/tstype.js.map +1 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/utils/timeService.d.ts.map +1 -0
  76. package/dist/utils/timeService.js +127 -0
  77. package/dist/utils/timeService.js.map +1 -0
  78. package/dist/zspec/zcl/buffaloZcl.d.ts.map +1 -0
  79. package/dist/zspec/zcl/buffaloZcl.js +969 -0
  80. package/dist/zspec/zcl/buffaloZcl.js.map +1 -0
  81. package/dist/zspec/zcl/definition/cluster.d.ts.map +1 -0
  82. package/dist/zspec/zcl/definition/cluster.js +7507 -0
  83. package/dist/zspec/zcl/definition/cluster.js.map +1 -0
  84. package/dist/zspec/zcl/definition/clusters-types.d.ts +8135 -0
  85. package/dist/zspec/zcl/definition/clusters-types.d.ts.map +1 -0
  86. package/dist/zspec/zcl/definition/clusters-types.js +3 -0
  87. package/dist/zspec/zcl/definition/clusters-types.js.map +1 -0
  88. package/dist/zspec/zcl/definition/foundation.d.ts.map +1 -0
  89. package/dist/zspec/zcl/definition/foundation.js +312 -0
  90. package/dist/zspec/zcl/definition/foundation.js.map +1 -0
  91. package/dist/zspec/zcl/definition/tstype.d.ts +273 -0
  92. package/dist/zspec/zcl/definition/tstype.d.ts.map +1 -0
  93. package/dist/zspec/zcl/definition/tstype.js +3 -0
  94. package/dist/zspec/zcl/definition/tstype.js.map +1 -0
  95. package/dist/zspec/zcl/index.d.ts.map +1 -0
  96. package/dist/zspec/zcl/index.js +57 -0
  97. package/dist/zspec/zcl/index.js.map +1 -0
  98. package/dist/zspec/zcl/utils.d.ts.map +1 -0
  99. package/dist/zspec/zcl/utils.js +419 -0
  100. package/dist/zspec/zcl/utils.js.map +1 -0
  101. package/dist/zspec/zcl/zclFrame.d.ts.map +1 -0
  102. package/dist/zspec/zcl/zclFrame.js +328 -0
  103. package/dist/zspec/zcl/zclFrame.js.map +1 -0
  104. package/dist/zspec/zcl/zclHeader.d.ts.map +1 -0
  105. package/dist/zspec/zcl/zclHeader.js +88 -0
  106. package/dist/zspec/zcl/zclHeader.js.map +1 -0
  107. package/package.json +90 -83
  108. package/src/controller/helpers/ota.ts +5 -2
  109. package/src/zspec/zcl/definition/cluster.ts +3 -294
  110. package/src/zspec/zcl/definition/clusters-types.ts +6 -355
  111. package/src/zspec/zcl/definition/tstype.ts +0 -14
  112. package/src/zspec/zcl/utils.ts +14 -31
  113. package/test/controller.test.ts +16 -8
  114. package/test/zcl.test.ts +36 -7
@@ -0,0 +1,969 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.BuffaloZcl = void 0;
37
+ const buffalo_1 = require("../../buffalo");
38
+ const logger_1 = require("../../utils/logger");
39
+ const utils_1 = require("../../utils/utils");
40
+ const datatypes_1 = require("./definition/datatypes");
41
+ const enums_1 = require("./definition/enums");
42
+ const Utils = __importStar(require("./utils"));
43
+ const NS = "zh:zcl:buffalo";
44
+ const UINT8_NON_VALUE = datatypes_1.ZCL_TYPE_INVALID_BY_TYPE[datatypes_1.ZclType.Uint8];
45
+ const UINT16_NON_VALUE = datatypes_1.ZCL_TYPE_INVALID_BY_TYPE[datatypes_1.ZclType.Uint16];
46
+ const SEC_KEY_LENGTH = 16;
47
+ const EXTENSION_FIELD_SETS_DATA_TYPE = {
48
+ 6: [enums_1.DataType.UINT8],
49
+ 8: [enums_1.DataType.UINT8],
50
+ 258: [enums_1.DataType.UINT8, enums_1.DataType.UINT8],
51
+ 768: [enums_1.DataType.UINT16, enums_1.DataType.UINT16, enums_1.DataType.UINT16, enums_1.DataType.UINT8, enums_1.DataType.UINT8, enums_1.DataType.UINT8, enums_1.DataType.UINT16, enums_1.DataType.UINT16],
52
+ };
53
+ class BuffaloZcl extends buffalo_1.Buffalo {
54
+ readLengthUInt8() {
55
+ const value = this.readUInt8();
56
+ return value === UINT8_NON_VALUE ? Number.NaN : value;
57
+ }
58
+ readLengthUInt16() {
59
+ const value = this.readUInt16();
60
+ return value === UINT16_NON_VALUE ? Number.NaN : value;
61
+ }
62
+ writeOctetStr(value) {
63
+ if (value) {
64
+ this.writeUInt8(value.length);
65
+ this.writeBuffer(value, value.length);
66
+ }
67
+ else {
68
+ this.writeUInt8(UINT8_NON_VALUE);
69
+ }
70
+ }
71
+ readOctetStr() {
72
+ const length = this.readLengthUInt8();
73
+ return Number.isNaN(length) ? Buffer.from([]) : this.readBuffer(length);
74
+ }
75
+ // TODO: support read/write with specific `length` from attribute metadata (CHAR_STR & LONG_CHAR_STR)
76
+ writeCharStr(value) {
77
+ // In case of an empty string, send 0 length, from the spec:
78
+ // "Setting this sub-field to 0x00 represents a character string with no character data (an “empty string”). Setting
79
+ // this sub-field to 0xff represents the non-value. In both cases the character data sub-field has zero length."
80
+ if (value != null) {
81
+ if (typeof value === "string") {
82
+ this.writeUInt8(Buffer.byteLength(value, "utf8"));
83
+ this.writeUtf8String(value);
84
+ }
85
+ else {
86
+ // XXX: value.length not written?
87
+ this.writeBuffer(value, value.length);
88
+ }
89
+ }
90
+ else {
91
+ this.writeUInt8(UINT8_NON_VALUE);
92
+ }
93
+ }
94
+ readCharStr() {
95
+ const length = this.readLengthUInt8();
96
+ return Number.isNaN(length) ? "" : this.readUtf8String(length);
97
+ }
98
+ writeLongOctetStr(value) {
99
+ if (value) {
100
+ this.writeUInt16(value.length);
101
+ this.writeBuffer(value, value.length);
102
+ }
103
+ else {
104
+ this.writeUInt16(UINT16_NON_VALUE);
105
+ }
106
+ }
107
+ readLongOctetStr() {
108
+ const length = this.readLengthUInt16();
109
+ return Number.isNaN(length) ? Buffer.from([]) : this.readBuffer(length);
110
+ }
111
+ writeLongCharStr(value) {
112
+ // In case of an empty string, send 0 length, from the spec:
113
+ // "Setting this sub-field to 0x0000 represents a long character string with no character data (an “empty string”).
114
+ // Setting this sub-field to 0xffff represents an non-value long character string value. In both cases the character
115
+ // data sub-field has zero length"
116
+ if (value != null) {
117
+ this.writeUInt16(Buffer.byteLength(value, "utf8"));
118
+ this.writeUtf8String(value);
119
+ }
120
+ else {
121
+ this.writeUInt16(UINT16_NON_VALUE);
122
+ }
123
+ }
124
+ readLongCharStr() {
125
+ const length = this.readLengthUInt16();
126
+ return Number.isNaN(length) ? "" : this.readUtf8String(length);
127
+ }
128
+ writeArray(value) {
129
+ if (value) {
130
+ const elTypeNumeric = typeof value.elementType === "number" ? value.elementType : enums_1.DataType[value.elementType];
131
+ this.writeUInt8(elTypeNumeric);
132
+ this.writeUInt16(value.elements.length);
133
+ for (const element of value.elements) {
134
+ this.write(elTypeNumeric, element, {});
135
+ }
136
+ }
137
+ else {
138
+ this.writeUInt8(enums_1.DataType.NO_DATA); // XXX: correct value?
139
+ this.writeUInt16(UINT16_NON_VALUE);
140
+ }
141
+ }
142
+ // TODO: support read [] with specific length (`arrayLengthSize` & `arrayLengthField`) from parameter metadata
143
+ readArray() {
144
+ const values = [];
145
+ const elementType = this.readUInt8();
146
+ const numberOfElements = this.readLengthUInt16();
147
+ if (!Number.isNaN(numberOfElements)) {
148
+ // not non-value
149
+ for (let i = 0; i < numberOfElements; i++) {
150
+ const value = this.read(elementType, {});
151
+ values.push(value);
152
+ }
153
+ }
154
+ return values;
155
+ }
156
+ writeStruct(value) {
157
+ if (value) {
158
+ this.writeUInt16(value.length);
159
+ for (const v of value) {
160
+ this.writeUInt8(v.elmType);
161
+ this.write(v.elmType, v.elmVal, {});
162
+ }
163
+ }
164
+ else {
165
+ this.writeUInt16(UINT16_NON_VALUE);
166
+ }
167
+ }
168
+ readStruct() {
169
+ const values = [];
170
+ const numberOfElements = this.readLengthUInt16();
171
+ if (!Number.isNaN(numberOfElements)) {
172
+ // not non-value
173
+ for (let i = 0; i < numberOfElements; i++) {
174
+ const elementType = this.readUInt8();
175
+ const value = this.read(elementType, {});
176
+ values.push({ elmType: elementType, elmVal: value });
177
+ }
178
+ }
179
+ return values;
180
+ }
181
+ writeToD(value) {
182
+ this.writeUInt8(value.hours == null || Number.isNaN(value.hours) ? UINT8_NON_VALUE : value.hours);
183
+ this.writeUInt8(value.minutes == null || Number.isNaN(value.minutes) ? UINT8_NON_VALUE : value.minutes);
184
+ this.writeUInt8(value.seconds == null || Number.isNaN(value.seconds) ? UINT8_NON_VALUE : value.seconds);
185
+ this.writeUInt8(value.hundredths == null || Number.isNaN(value.hundredths) ? UINT8_NON_VALUE : value.hundredths);
186
+ }
187
+ readToD() {
188
+ const hours = this.readLengthUInt8();
189
+ const minutes = this.readLengthUInt8();
190
+ const seconds = this.readLengthUInt8();
191
+ const hundredths = this.readLengthUInt8();
192
+ return {
193
+ hours,
194
+ minutes,
195
+ seconds,
196
+ hundredths,
197
+ };
198
+ }
199
+ writeDate(value) {
200
+ this.writeUInt8(value.year == null || Number.isNaN(value.year) ? UINT8_NON_VALUE : value.year - 1900);
201
+ this.writeUInt8(value.month == null || Number.isNaN(value.month) ? UINT8_NON_VALUE : value.month);
202
+ this.writeUInt8(value.dayOfMonth == null || Number.isNaN(value.dayOfMonth) ? UINT8_NON_VALUE : value.dayOfMonth);
203
+ this.writeUInt8(value.dayOfWeek == null || Number.isNaN(value.dayOfWeek) ? UINT8_NON_VALUE : value.dayOfWeek);
204
+ }
205
+ readDate() {
206
+ const year = this.readLengthUInt8();
207
+ const month = this.readLengthUInt8();
208
+ const dayOfMonth = this.readLengthUInt8();
209
+ const dayOfWeek = this.readLengthUInt8();
210
+ return {
211
+ year: year + 1900, // remains NaN if year is NaN
212
+ month,
213
+ dayOfMonth,
214
+ dayOfWeek,
215
+ };
216
+ }
217
+ //--- BuffaloZclDataType
218
+ writeListZoneInfo(values) {
219
+ for (const value of values) {
220
+ this.writeUInt8(value.zoneID);
221
+ this.writeUInt16(value.zoneStatus);
222
+ }
223
+ }
224
+ readListZoneInfo(length) {
225
+ const value = [];
226
+ for (let i = 0; i < length; i++) {
227
+ value.push({
228
+ zoneID: this.readUInt8(),
229
+ zoneStatus: this.readUInt16(),
230
+ });
231
+ }
232
+ return value;
233
+ }
234
+ writeExtensionFieldSets(values) {
235
+ for (const value of values) {
236
+ this.writeUInt16(value.clstId);
237
+ this.writeUInt8(value.len);
238
+ let index = 0;
239
+ for (const entry of value.extField) {
240
+ this.write(EXTENSION_FIELD_SETS_DATA_TYPE[value.clstId][index], entry, {});
241
+ index++;
242
+ }
243
+ }
244
+ }
245
+ readExtensionFieldSets() {
246
+ const value = [];
247
+ // XXX: doesn't work if buffer has more unrelated fields after this one
248
+ while (this.isMore()) {
249
+ const clstId = this.readUInt16();
250
+ const len = this.readUInt8();
251
+ const end = this.getPosition() + len;
252
+ let index = 0;
253
+ const extField = [];
254
+ while (this.getPosition() < end) {
255
+ extField.push(this.read(EXTENSION_FIELD_SETS_DATA_TYPE[clstId][index], {}));
256
+ index++;
257
+ }
258
+ value.push({ extField, clstId, len });
259
+ }
260
+ return value;
261
+ }
262
+ writeListThermoTransitions(value) {
263
+ for (const entry of value) {
264
+ this.writeUInt16(entry.transitionTime);
265
+ if (entry.heatSetpoint != null) {
266
+ this.writeInt16(entry.heatSetpoint);
267
+ }
268
+ if (entry.coolSetpoint != null) {
269
+ this.writeInt16(entry.coolSetpoint);
270
+ }
271
+ }
272
+ }
273
+ readListThermoTransitions(options) {
274
+ if (options.payload == null || options.payload.mode == null || options.payload.numoftrans == null) {
275
+ throw new Error("Cannot read LIST_THERMO_TRANSITIONS without required payload options specified");
276
+ }
277
+ const heat = options.payload.mode & 1;
278
+ const cool = options.payload.mode & 2;
279
+ const result = [];
280
+ for (let i = 0; i < options.payload.numoftrans; i++) {
281
+ const entry = {
282
+ transitionTime: this.readUInt16(),
283
+ };
284
+ if (heat) {
285
+ entry.heatSetpoint = this.readInt16();
286
+ }
287
+ if (cool) {
288
+ entry.coolSetpoint = this.readInt16();
289
+ }
290
+ result.push(entry);
291
+ }
292
+ return result;
293
+ }
294
+ writeGpdFrame(value) {
295
+ if (value.commandID === 0xf0) {
296
+ // Commissioning Reply
297
+ const v = value;
298
+ const panIDPresent = Boolean(v.options & 0x1);
299
+ const gpdSecurityKeyPresent = Boolean(v.options & 0x2);
300
+ const gpdKeyEncryption = Boolean((v.options >> 2) & 0x1);
301
+ const securityLevel = (v.options >> 3) & 0x3;
302
+ const hasGPDKeyMIC = gpdKeyEncryption && gpdSecurityKeyPresent;
303
+ const hasFrameCounter = gpdSecurityKeyPresent && gpdKeyEncryption && (securityLevel === 0b10 || securityLevel === 0b11);
304
+ this.writeUInt8(1 + (panIDPresent ? 2 : 0) + (gpdSecurityKeyPresent ? 16 : 0) + (hasGPDKeyMIC ? 4 : 0) + (hasFrameCounter ? 4 : 0)); // Length
305
+ this.writeUInt8(v.options);
306
+ if (panIDPresent) {
307
+ // biome-ignore lint/style/noNonNullAssertion: ignored using `--suppress`
308
+ this.writeUInt16(v.panID);
309
+ }
310
+ if (gpdSecurityKeyPresent) {
311
+ // biome-ignore lint/style/noNonNullAssertion: ignored using `--suppress`
312
+ this.writeBuffer(v.securityKey, 16);
313
+ }
314
+ if (hasGPDKeyMIC) {
315
+ // biome-ignore lint/style/noNonNullAssertion: ignored using `--suppress`
316
+ this.writeUInt32(v.keyMic);
317
+ }
318
+ if (hasFrameCounter) {
319
+ // biome-ignore lint/style/noNonNullAssertion: ignored using `--suppress`
320
+ this.writeUInt32(v.frameCounter);
321
+ }
322
+ }
323
+ else if (value.commandID === 0xf3) {
324
+ // Channel configuration
325
+ const v = value;
326
+ this.writeUInt8(1);
327
+ this.writeUInt8((v.operationalChannel & 0xf) | ((v.basic ? 1 : 0) << 4));
328
+ }
329
+ else if (value.commandID === 0xf4 || value.commandID === 0xf5 || (value.commandID >= 0xf7 && value.commandID <= 0xff)) {
330
+ // Other commands sent to GPD
331
+ const v = value;
332
+ this.writeUInt8(v.buffer.length);
333
+ this.writeBuffer(v.buffer, v.buffer.length);
334
+ }
335
+ // 0xf1: Write Attributes
336
+ // 0xf2: Read Attributes
337
+ // 0xf6: ZCL Tunneling
338
+ }
339
+ readGpdFrame(options) {
340
+ if (options.payload?.payloadSize === undefined) {
341
+ throw new Error("Cannot read GPD_FRAME without required payload options specified");
342
+ }
343
+ if (Number.isNaN(options.payload.payloadSize) || options.payload.payloadSize === 0) {
344
+ return {}; // non-value, don't move position
345
+ }
346
+ // ensure offset by options.payload.payloadSize (if any) at end of parsing to not cause issues with spec changes (until supported)
347
+ const startPosition = this.position;
348
+ if (options.payload.commandID === 0xe0) {
349
+ // Commisioning
350
+ const frame = {
351
+ deviceID: this.readUInt8(),
352
+ options: this.readUInt8(),
353
+ extendedOptions: 0,
354
+ securityKey: Buffer.alloc(16),
355
+ keyMic: 0,
356
+ outgoingCounter: 0,
357
+ applicationInfo: 0,
358
+ manufacturerID: 0,
359
+ modelID: 0,
360
+ numGpdCommands: 0,
361
+ gpdCommandIdList: Buffer.alloc(0),
362
+ numServerClusters: 0,
363
+ numClientClusters: 0,
364
+ gpdServerClusters: Buffer.alloc(0),
365
+ gpdClientClusters: Buffer.alloc(0),
366
+ genericSwitchConfig: 0,
367
+ currentContactStatus: 0,
368
+ };
369
+ if (frame.options & 0x80) {
370
+ frame.extendedOptions = this.readUInt8();
371
+ }
372
+ if (frame.extendedOptions & 0x20) {
373
+ frame.securityKey = this.readBuffer(16);
374
+ }
375
+ if (frame.extendedOptions & 0x40) {
376
+ frame.keyMic = this.readUInt32();
377
+ }
378
+ if (frame.extendedOptions & 0x80) {
379
+ frame.outgoingCounter = this.readUInt32();
380
+ }
381
+ if (frame.options & 0x04) {
382
+ frame.applicationInfo = this.readUInt8();
383
+ }
384
+ if (frame.applicationInfo & 0x01) {
385
+ frame.manufacturerID = this.readUInt16();
386
+ }
387
+ if (frame.applicationInfo & 0x02) {
388
+ frame.modelID = this.readUInt16();
389
+ }
390
+ if (frame.applicationInfo & 0x04) {
391
+ frame.numGpdCommands = this.readUInt8();
392
+ frame.gpdCommandIdList = this.readBuffer(frame.numGpdCommands);
393
+ }
394
+ if (frame.applicationInfo & 0x08) {
395
+ const len = this.readUInt8();
396
+ frame.numServerClusters = len & 0xf;
397
+ frame.numClientClusters = (len >> 4) & 0xf;
398
+ frame.gpdServerClusters = this.readBuffer(2 * frame.numServerClusters);
399
+ frame.gpdClientClusters = this.readBuffer(2 * frame.numClientClusters);
400
+ }
401
+ if (frame.applicationInfo & 0x10) {
402
+ const len = this.readUInt8();
403
+ if (len >= 1) {
404
+ frame.genericSwitchConfig = this.readUInt8();
405
+ }
406
+ if (len >= 2) {
407
+ frame.currentContactStatus = this.readUInt8();
408
+ }
409
+ }
410
+ this.setPosition(startPosition + options.payload.payloadSize);
411
+ return frame;
412
+ }
413
+ if (options.payload.commandID === 0xe3) {
414
+ // Channel Request
415
+ const channelOpts = this.readUInt8();
416
+ this.setPosition(startPosition + options.payload.payloadSize);
417
+ return {
418
+ nextChannel: channelOpts & 0xf,
419
+ nextNextChannel: channelOpts >> 4,
420
+ };
421
+ }
422
+ if (options.payload.commandID === 0xa1) {
423
+ // Manufacturer-specific Attribute Reporting
424
+ const start = this.position;
425
+ const frame = {
426
+ manufacturerCode: this.readUInt16(),
427
+ clusterID: this.readUInt16(),
428
+ attributes: {},
429
+ };
430
+ const cluster = Utils.getCluster(frame.clusterID, frame.manufacturerCode, {});
431
+ while (this.position - start < options.payload.payloadSize) {
432
+ const attributeID = this.readUInt16();
433
+ const type = this.readUInt8();
434
+ /* v8 ignore next */
435
+ let attribute = cluster.getAttribute(attributeID)?.name;
436
+ // number type is only used when going into this if
437
+ if (!attribute) {
438
+ // this is spammy because of the many manufacturer-specific attributes not currently used
439
+ logger_1.logger.debug(`Unknown attribute ${attributeID} in cluster ${cluster.name}`, NS);
440
+ attribute = attributeID;
441
+ }
442
+ frame.attributes[attribute] = this.read(type, options);
443
+ }
444
+ this.setPosition(startPosition + options.payload.payloadSize);
445
+ return frame;
446
+ }
447
+ // might contain `gppNwkAddr`, `gppGpdLink` & `mic` from ZCL cluster after this, so limit by `payloadSize`
448
+ return { raw: this.readBuffer(options.payload.payloadSize) };
449
+ }
450
+ writeStructuredSelector(value) {
451
+ if (value != null) {
452
+ const indexes = value.indexes || [];
453
+ const indicatorType = value.indicatorType || enums_1.StructuredIndicatorType.Whole;
454
+ const indicator = indexes.length + indicatorType;
455
+ this.writeUInt8(indicator);
456
+ for (const index of indexes) {
457
+ this.writeUInt16(index);
458
+ }
459
+ }
460
+ }
461
+ readStructuredSelector() {
462
+ /** [0-15] range */
463
+ const indicator = this.readUInt8();
464
+ if (indicator === 0) {
465
+ // no indexes, whole attribute value is to be read
466
+ return { indicatorType: enums_1.StructuredIndicatorType.Whole };
467
+ }
468
+ if (indicator < enums_1.StructuredIndicatorType.WriteAdd) {
469
+ const indexes = [];
470
+ for (let i = 0; i < indicator; i++) {
471
+ const index = this.readUInt16();
472
+ indexes.push(index);
473
+ }
474
+ return { indexes };
475
+ }
476
+ throw new Error("Read structured selector was outside [0-15] range.");
477
+ }
478
+ writeListTuyaDataPointValues(dpValues) {
479
+ for (const dpValue of dpValues) {
480
+ this.writeUInt8(dpValue.dp);
481
+ this.writeUInt8(dpValue.datatype);
482
+ const dataLen = dpValue.data.length;
483
+ // UInt16BE
484
+ this.writeUInt8((dataLen >> 8) & 0xff);
485
+ this.writeUInt8(dataLen & 0xff);
486
+ this.writeBuffer(dpValue.data, dataLen);
487
+ }
488
+ }
489
+ readListTuyaDataPointValues() {
490
+ const value = [];
491
+ // XXX: doesn't work if buffer has more unrelated fields after this one
492
+ while (this.isMore()) {
493
+ try {
494
+ const dp = this.readUInt8();
495
+ const datatype = this.readUInt8();
496
+ const len_hi = this.readUInt8();
497
+ const len_lo = this.readUInt8();
498
+ const data = this.readBuffer(len_lo + (len_hi << 8));
499
+ value.push({ dp, datatype, data });
500
+ }
501
+ catch {
502
+ break;
503
+ }
504
+ }
505
+ return value;
506
+ }
507
+ writeListMiboxerZones(values) {
508
+ this.writeUInt8(values.length);
509
+ for (const value of values) {
510
+ this.writeUInt16(value.groupId);
511
+ this.writeUInt8(value.zoneNum);
512
+ }
513
+ }
514
+ readListMiboxerZones() {
515
+ const value = [];
516
+ const len = this.readUInt8();
517
+ for (let i = 0; i < len; i++) {
518
+ const groupId = this.readUInt16();
519
+ const zoneNum = this.readUInt8();
520
+ value.push({ groupId, zoneNum });
521
+ }
522
+ return value;
523
+ }
524
+ writeBigEndianUInt24(value) {
525
+ this.position = this.buffer.writeUIntBE(value, this.position, 3);
526
+ }
527
+ readBigEndianUInt24() {
528
+ const value = this.buffer.readUIntBE(this.position, 3);
529
+ this.position += 3;
530
+ return value;
531
+ }
532
+ // NOTE: writeMiStruct is not supported.
533
+ readMiStruct() {
534
+ const length = this.readUInt8();
535
+ const value = {};
536
+ if (length === UINT8_NON_VALUE) {
537
+ return value;
538
+ }
539
+ for (let i = 0; i < length; i++) {
540
+ const index = this.readUInt8();
541
+ const dataType = this.readUInt8();
542
+ value[index] = this.read(dataType, {});
543
+ const remaining = this.buffer.length - this.position;
544
+ if (remaining <= 1) {
545
+ if (remaining === 1) {
546
+ // Some Xiaomi structs have a trailing byte, skip it.
547
+ this.position += 1;
548
+ }
549
+ break;
550
+ }
551
+ }
552
+ return value;
553
+ }
554
+ // biome-ignore lint/suspicious/noExplicitAny: API
555
+ write(type, value, options) {
556
+ switch (type) {
557
+ case enums_1.DataType.NO_DATA:
558
+ case enums_1.DataType.UNKNOWN: {
559
+ return; // nothing to write
560
+ }
561
+ case enums_1.DataType.BOOLEAN:
562
+ case enums_1.DataType.DATA8:
563
+ case enums_1.DataType.BITMAP8:
564
+ case enums_1.DataType.UINT8:
565
+ case enums_1.DataType.ENUM8: {
566
+ this.writeUInt8(value);
567
+ break;
568
+ }
569
+ case enums_1.DataType.DATA16:
570
+ case enums_1.DataType.BITMAP16:
571
+ case enums_1.DataType.UINT16:
572
+ case enums_1.DataType.ENUM16:
573
+ case enums_1.DataType.CLUSTER_ID:
574
+ case enums_1.DataType.ATTR_ID: {
575
+ this.writeUInt16(value);
576
+ break;
577
+ }
578
+ case enums_1.DataType.DATA24:
579
+ case enums_1.DataType.BITMAP24:
580
+ case enums_1.DataType.UINT24: {
581
+ this.writeUInt24(value);
582
+ break;
583
+ }
584
+ case enums_1.DataType.DATA32:
585
+ case enums_1.DataType.BITMAP32:
586
+ case enums_1.DataType.UINT32:
587
+ case enums_1.DataType.UTC:
588
+ case enums_1.DataType.BAC_OID: {
589
+ this.writeUInt32(value);
590
+ break;
591
+ }
592
+ case enums_1.DataType.DATA40:
593
+ case enums_1.DataType.BITMAP40:
594
+ case enums_1.DataType.UINT40: {
595
+ this.writeUInt40(value);
596
+ break;
597
+ }
598
+ case enums_1.DataType.DATA48:
599
+ case enums_1.DataType.BITMAP48:
600
+ case enums_1.DataType.UINT48: {
601
+ this.writeUInt48(value);
602
+ break;
603
+ }
604
+ case enums_1.DataType.DATA56:
605
+ case enums_1.DataType.BITMAP56:
606
+ case enums_1.DataType.UINT56: {
607
+ this.writeUInt56(value);
608
+ break;
609
+ }
610
+ case enums_1.DataType.DATA64:
611
+ case enums_1.DataType.BITMAP64:
612
+ case enums_1.DataType.UINT64: {
613
+ this.writeUInt64(value);
614
+ break;
615
+ }
616
+ case enums_1.DataType.INT8: {
617
+ this.writeInt8(value);
618
+ break;
619
+ }
620
+ case enums_1.DataType.INT16: {
621
+ this.writeInt16(value);
622
+ break;
623
+ }
624
+ case enums_1.DataType.INT24: {
625
+ this.writeInt24(value);
626
+ break;
627
+ }
628
+ case enums_1.DataType.INT32: {
629
+ this.writeInt32(value);
630
+ break;
631
+ }
632
+ case enums_1.DataType.INT40: {
633
+ this.writeInt40(value);
634
+ break;
635
+ }
636
+ case enums_1.DataType.INT48: {
637
+ this.writeInt48(value);
638
+ break;
639
+ }
640
+ case enums_1.DataType.INT56: {
641
+ this.writeInt56(value);
642
+ break;
643
+ }
644
+ case enums_1.DataType.INT64: {
645
+ this.writeInt64(value);
646
+ break;
647
+ }
648
+ // case DataType.SEMI_PREC: {
649
+ // // https://tc39.es/proposal-float16array/
650
+ // // not currently used
651
+ // this.writeSemiFloatLE(value);
652
+ // break;
653
+ // }
654
+ case enums_1.DataType.SINGLE_PREC: {
655
+ this.writeFloatLE(value);
656
+ break;
657
+ }
658
+ case enums_1.DataType.DOUBLE_PREC: {
659
+ this.writeDoubleLE(value);
660
+ break;
661
+ }
662
+ case enums_1.DataType.OCTET_STR: {
663
+ this.writeOctetStr(value);
664
+ break;
665
+ }
666
+ case enums_1.DataType.CHAR_STR: {
667
+ this.writeCharStr(value);
668
+ break;
669
+ }
670
+ case enums_1.DataType.LONG_OCTET_STR: {
671
+ this.writeLongOctetStr(value);
672
+ break;
673
+ }
674
+ case enums_1.DataType.LONG_CHAR_STR: {
675
+ this.writeLongCharStr(value);
676
+ break;
677
+ }
678
+ case enums_1.DataType.ARRAY:
679
+ case enums_1.DataType.SET:
680
+ case enums_1.DataType.BAG: {
681
+ this.writeArray(value);
682
+ break;
683
+ }
684
+ case enums_1.DataType.STRUCT: {
685
+ this.writeStruct(value);
686
+ break;
687
+ }
688
+ case enums_1.DataType.TOD: {
689
+ this.writeToD(value);
690
+ break;
691
+ }
692
+ case enums_1.DataType.DATE: {
693
+ this.writeDate(value);
694
+ break;
695
+ }
696
+ case enums_1.DataType.IEEE_ADDR: {
697
+ this.writeIeeeAddr(value);
698
+ break;
699
+ }
700
+ case enums_1.DataType.SEC_KEY: {
701
+ this.writeBuffer(value, SEC_KEY_LENGTH);
702
+ break;
703
+ }
704
+ case enums_1.BuffaloZclDataType.USE_DATA_TYPE: {
705
+ if (options.dataType == null) {
706
+ if (Buffer.isBuffer(value) || (0, utils_1.isNumberArray)(value)) {
707
+ this.writeBuffer(value, value.length);
708
+ break;
709
+ }
710
+ throw new Error("Cannot write USE_DATA_TYPE without dataType option specified");
711
+ }
712
+ this.write(options.dataType, value, options);
713
+ break;
714
+ }
715
+ case enums_1.BuffaloZclDataType.LIST_UINT8: {
716
+ this.writeListUInt8(value);
717
+ break;
718
+ }
719
+ case enums_1.BuffaloZclDataType.LIST_UINT16: {
720
+ this.writeListUInt16(value);
721
+ break;
722
+ }
723
+ case enums_1.BuffaloZclDataType.LIST_UINT24: {
724
+ this.writeListUInt24(value);
725
+ break;
726
+ }
727
+ case enums_1.BuffaloZclDataType.LIST_UINT32: {
728
+ this.writeListUInt32(value);
729
+ break;
730
+ }
731
+ case enums_1.BuffaloZclDataType.LIST_ZONEINFO: {
732
+ this.writeListZoneInfo(value);
733
+ break;
734
+ }
735
+ case enums_1.BuffaloZclDataType.EXTENSION_FIELD_SETS: {
736
+ this.writeExtensionFieldSets(value);
737
+ break;
738
+ }
739
+ case enums_1.BuffaloZclDataType.LIST_THERMO_TRANSITIONS: {
740
+ this.writeListThermoTransitions(value);
741
+ break;
742
+ }
743
+ case enums_1.BuffaloZclDataType.BUFFER: {
744
+ // XXX: inconsistent with read that allows partial with options.length, here always "whole"
745
+ this.writeBuffer(value, value.length);
746
+ break;
747
+ }
748
+ case enums_1.BuffaloZclDataType.GPD_FRAME: {
749
+ this.writeGpdFrame(value);
750
+ break;
751
+ }
752
+ case enums_1.BuffaloZclDataType.STRUCTURED_SELECTOR: {
753
+ this.writeStructuredSelector(value);
754
+ break;
755
+ }
756
+ case enums_1.BuffaloZclDataType.LIST_TUYA_DATAPOINT_VALUES: {
757
+ this.writeListTuyaDataPointValues(value);
758
+ break;
759
+ }
760
+ case enums_1.BuffaloZclDataType.LIST_MIBOXER_ZONES: {
761
+ this.writeListMiboxerZones(value);
762
+ break;
763
+ }
764
+ case enums_1.BuffaloZclDataType.BIG_ENDIAN_UINT24: {
765
+ this.writeBigEndianUInt24(value);
766
+ break;
767
+ }
768
+ default: {
769
+ // In case the type is undefined, write it as a buffer to easily allow for custom types
770
+ // e.g. for https://github.com/Koenkk/zigbee-herdsman/issues/127
771
+ if (Buffer.isBuffer(value) || (0, utils_1.isNumberArray)(value)) {
772
+ this.writeBuffer(value, value.length);
773
+ break;
774
+ }
775
+ throw new Error(`Write for '${type}' not available`);
776
+ }
777
+ }
778
+ }
779
+ // biome-ignore lint/suspicious/noExplicitAny: API
780
+ read(type, options) {
781
+ switch (type) {
782
+ case enums_1.DataType.NO_DATA:
783
+ case enums_1.DataType.UNKNOWN: {
784
+ return; // nothing to write
785
+ }
786
+ case enums_1.DataType.BOOLEAN:
787
+ case enums_1.DataType.DATA8:
788
+ case enums_1.DataType.BITMAP8:
789
+ case enums_1.DataType.UINT8:
790
+ case enums_1.DataType.ENUM8: {
791
+ return this.readUInt8();
792
+ }
793
+ case enums_1.DataType.DATA16:
794
+ case enums_1.DataType.BITMAP16:
795
+ case enums_1.DataType.UINT16:
796
+ case enums_1.DataType.ENUM16:
797
+ case enums_1.DataType.CLUSTER_ID:
798
+ case enums_1.DataType.ATTR_ID: {
799
+ return this.readUInt16();
800
+ }
801
+ case enums_1.DataType.DATA24:
802
+ case enums_1.DataType.BITMAP24:
803
+ case enums_1.DataType.UINT24: {
804
+ return this.readUInt24();
805
+ }
806
+ case enums_1.DataType.DATA32:
807
+ case enums_1.DataType.BITMAP32:
808
+ case enums_1.DataType.UINT32:
809
+ case enums_1.DataType.UTC:
810
+ case enums_1.DataType.BAC_OID: {
811
+ return this.readUInt32();
812
+ }
813
+ case enums_1.DataType.DATA40:
814
+ case enums_1.DataType.BITMAP40:
815
+ case enums_1.DataType.UINT40: {
816
+ return this.readUInt40();
817
+ }
818
+ case enums_1.DataType.DATA48:
819
+ case enums_1.DataType.BITMAP48:
820
+ case enums_1.DataType.UINT48: {
821
+ return this.readUInt48();
822
+ }
823
+ case enums_1.DataType.DATA56:
824
+ case enums_1.DataType.BITMAP56:
825
+ case enums_1.DataType.UINT56: {
826
+ return this.readUInt56();
827
+ }
828
+ case enums_1.DataType.DATA64:
829
+ case enums_1.DataType.BITMAP64:
830
+ case enums_1.DataType.UINT64: {
831
+ return this.readUInt64();
832
+ }
833
+ case enums_1.DataType.INT8: {
834
+ return this.readInt8();
835
+ }
836
+ case enums_1.DataType.INT16: {
837
+ return this.readInt16();
838
+ }
839
+ case enums_1.DataType.INT24: {
840
+ return this.readInt24();
841
+ }
842
+ case enums_1.DataType.INT32: {
843
+ return this.readInt32();
844
+ }
845
+ case enums_1.DataType.INT40: {
846
+ return this.readInt40();
847
+ }
848
+ case enums_1.DataType.INT48: {
849
+ return this.readInt48();
850
+ }
851
+ case enums_1.DataType.INT56: {
852
+ return this.readInt56();
853
+ }
854
+ case enums_1.DataType.INT64: {
855
+ return this.readInt64();
856
+ }
857
+ // case DataType.SEMI_PREC: {
858
+ // // https://tc39.es/proposal-float16array/
859
+ // // not currently used
860
+ // return this.readSemiFloatLE();
861
+ // }
862
+ case enums_1.DataType.SINGLE_PREC: {
863
+ return this.readFloatLE();
864
+ }
865
+ case enums_1.DataType.DOUBLE_PREC: {
866
+ return this.readDoubleLE();
867
+ }
868
+ case enums_1.DataType.OCTET_STR: {
869
+ return this.readOctetStr();
870
+ }
871
+ case enums_1.DataType.CHAR_STR: {
872
+ return this.readCharStr();
873
+ }
874
+ case enums_1.DataType.LONG_OCTET_STR: {
875
+ return this.readLongOctetStr();
876
+ }
877
+ case enums_1.DataType.LONG_CHAR_STR: {
878
+ return this.readLongCharStr();
879
+ }
880
+ case enums_1.DataType.ARRAY:
881
+ case enums_1.DataType.SET:
882
+ case enums_1.DataType.BAG: {
883
+ return this.readArray();
884
+ }
885
+ case enums_1.DataType.STRUCT: {
886
+ return this.readStruct();
887
+ }
888
+ case enums_1.DataType.TOD: {
889
+ return this.readToD();
890
+ }
891
+ case enums_1.DataType.DATE: {
892
+ return this.readDate();
893
+ }
894
+ case enums_1.DataType.IEEE_ADDR: {
895
+ return this.readIeeeAddr();
896
+ }
897
+ case enums_1.DataType.SEC_KEY: {
898
+ return this.readBuffer(SEC_KEY_LENGTH);
899
+ }
900
+ case enums_1.BuffaloZclDataType.USE_DATA_TYPE: {
901
+ if (options.dataType == null) {
902
+ return this.readBuffer(options.length ?? this.buffer.length);
903
+ }
904
+ return this.read(options.dataType, options);
905
+ }
906
+ case enums_1.BuffaloZclDataType.LIST_UINT8: {
907
+ if (options.length == null) {
908
+ throw new Error("Cannot read LIST_UINT8 without length option specified");
909
+ }
910
+ return this.readListUInt8(options.length);
911
+ }
912
+ case enums_1.BuffaloZclDataType.LIST_UINT16: {
913
+ if (options.length == null) {
914
+ throw new Error("Cannot read LIST_UINT16 without length option specified");
915
+ }
916
+ return this.readListUInt16(options.length);
917
+ }
918
+ case enums_1.BuffaloZclDataType.LIST_UINT24: {
919
+ if (options.length == null) {
920
+ throw new Error("Cannot read LIST_UINT24 without length option specified");
921
+ }
922
+ return this.readListUInt24(options.length);
923
+ }
924
+ case enums_1.BuffaloZclDataType.LIST_UINT32: {
925
+ if (options.length == null) {
926
+ throw new Error("Cannot read LIST_UINT32 without length option specified");
927
+ }
928
+ return this.readListUInt32(options.length);
929
+ }
930
+ case enums_1.BuffaloZclDataType.LIST_ZONEINFO: {
931
+ if (options.length == null) {
932
+ throw new Error("Cannot read LIST_ZONEINFO without length option specified");
933
+ }
934
+ return this.readListZoneInfo(options.length);
935
+ }
936
+ case enums_1.BuffaloZclDataType.EXTENSION_FIELD_SETS: {
937
+ return this.readExtensionFieldSets();
938
+ }
939
+ case enums_1.BuffaloZclDataType.LIST_THERMO_TRANSITIONS: {
940
+ return this.readListThermoTransitions(options);
941
+ }
942
+ case enums_1.BuffaloZclDataType.BUFFER: {
943
+ // if length option not specified, read the whole buffer
944
+ return this.readBuffer(options.length ?? this.buffer.length);
945
+ }
946
+ case enums_1.BuffaloZclDataType.GPD_FRAME: {
947
+ return this.readGpdFrame(options);
948
+ }
949
+ case enums_1.BuffaloZclDataType.STRUCTURED_SELECTOR: {
950
+ return this.readStructuredSelector();
951
+ }
952
+ case enums_1.BuffaloZclDataType.LIST_TUYA_DATAPOINT_VALUES: {
953
+ return this.readListTuyaDataPointValues();
954
+ }
955
+ case enums_1.BuffaloZclDataType.LIST_MIBOXER_ZONES: {
956
+ return this.readListMiboxerZones();
957
+ }
958
+ case enums_1.BuffaloZclDataType.BIG_ENDIAN_UINT24: {
959
+ return this.readBigEndianUInt24();
960
+ }
961
+ case enums_1.BuffaloZclDataType.MI_STRUCT: {
962
+ return this.readMiStruct();
963
+ }
964
+ }
965
+ throw new Error(`Read for '${type}' not available`);
966
+ }
967
+ }
968
+ exports.BuffaloZcl = BuffaloZcl;
969
+ //# sourceMappingURL=buffaloZcl.js.map