zigbee-herdsman 5.0.4 → 6.0.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 (122) hide show
  1. package/.github/dependabot.yml +3 -0
  2. package/.github/workflows/ci.yml +1 -1
  3. package/.github/workflows/typedoc.yaml +1 -1
  4. package/.release-please-manifest.json +1 -1
  5. package/CHANGELOG.md +29 -0
  6. package/biome.json +1 -1
  7. package/dist/adapter/ember/ezsp/buffalo.d.ts +0 -2
  8. package/dist/adapter/ember/ezsp/buffalo.d.ts.map +1 -1
  9. package/dist/adapter/ember/ezsp/buffalo.js +0 -4
  10. package/dist/adapter/ember/ezsp/buffalo.js.map +1 -1
  11. package/dist/adapter/ember/uart/ash.d.ts.map +1 -1
  12. package/dist/adapter/ember/uart/ash.js +0 -2
  13. package/dist/adapter/ember/uart/ash.js.map +1 -1
  14. package/dist/buffalo/buffalo.d.ts +5 -0
  15. package/dist/buffalo/buffalo.d.ts.map +1 -1
  16. package/dist/buffalo/buffalo.js +7 -0
  17. package/dist/buffalo/buffalo.js.map +1 -1
  18. package/dist/controller/controller.d.ts.map +1 -1
  19. package/dist/controller/controller.js +8 -11
  20. package/dist/controller/controller.js.map +1 -1
  21. package/dist/controller/events.d.ts +2 -1
  22. package/dist/controller/events.d.ts.map +1 -1
  23. package/dist/controller/helpers/request.d.ts.map +1 -1
  24. package/dist/controller/helpers/request.js +2 -1
  25. package/dist/controller/helpers/request.js.map +1 -1
  26. package/dist/controller/helpers/zclFrameConverter.d.ts +2 -4
  27. package/dist/controller/helpers/zclFrameConverter.d.ts.map +1 -1
  28. package/dist/controller/helpers/zclFrameConverter.js +2 -0
  29. package/dist/controller/helpers/zclFrameConverter.js.map +1 -1
  30. package/dist/controller/model/device.d.ts +13 -24
  31. package/dist/controller/model/device.d.ts.map +1 -1
  32. package/dist/controller/model/device.js +88 -129
  33. package/dist/controller/model/device.js.map +1 -1
  34. package/dist/controller/model/endpoint.d.ts +17 -16
  35. package/dist/controller/model/endpoint.d.ts.map +1 -1
  36. package/dist/controller/model/endpoint.js +31 -16
  37. package/dist/controller/model/endpoint.js.map +1 -1
  38. package/dist/controller/model/group.d.ts +6 -6
  39. package/dist/controller/model/group.d.ts.map +1 -1
  40. package/dist/controller/model/group.js +5 -3
  41. package/dist/controller/model/group.js.map +1 -1
  42. package/dist/controller/model/index.d.ts +1 -0
  43. package/dist/controller/model/index.d.ts.map +1 -1
  44. package/dist/controller/model/index.js +3 -1
  45. package/dist/controller/model/index.js.map +1 -1
  46. package/dist/controller/model/zigbeeEntity.d.ts +8 -0
  47. package/dist/controller/model/zigbeeEntity.d.ts.map +1 -0
  48. package/dist/controller/model/zigbeeEntity.js +11 -0
  49. package/dist/controller/model/zigbeeEntity.js.map +1 -0
  50. package/dist/controller/tstype.d.ts +39 -0
  51. package/dist/controller/tstype.d.ts.map +1 -1
  52. package/dist/zspec/zcl/buffaloZcl.d.ts +32 -17
  53. package/dist/zspec/zcl/buffaloZcl.d.ts.map +1 -1
  54. package/dist/zspec/zcl/buffaloZcl.js +257 -121
  55. package/dist/zspec/zcl/buffaloZcl.js.map +1 -1
  56. package/dist/zspec/zcl/definition/cluster.d.ts.map +1 -1
  57. package/dist/zspec/zcl/definition/cluster.js +156 -33
  58. package/dist/zspec/zcl/definition/cluster.js.map +1 -1
  59. package/dist/zspec/zcl/definition/clusters-typegen.d.ts +2 -0
  60. package/dist/zspec/zcl/definition/clusters-typegen.d.ts.map +1 -0
  61. package/dist/zspec/zcl/definition/clusters-typegen.js +348 -0
  62. package/dist/zspec/zcl/definition/clusters-typegen.js.map +1 -0
  63. package/dist/zspec/zcl/definition/clusters-types.d.ts +7238 -0
  64. package/dist/zspec/zcl/definition/clusters-types.d.ts.map +1 -0
  65. package/dist/zspec/zcl/definition/clusters-types.js +3 -0
  66. package/dist/zspec/zcl/definition/clusters-types.js.map +1 -0
  67. package/dist/zspec/zcl/definition/enums.d.ts +14 -6
  68. package/dist/zspec/zcl/definition/enums.d.ts.map +1 -1
  69. package/dist/zspec/zcl/definition/enums.js +15 -6
  70. package/dist/zspec/zcl/definition/enums.js.map +1 -1
  71. package/dist/zspec/zcl/definition/foundation.d.ts.map +1 -1
  72. package/dist/zspec/zcl/definition/foundation.js +43 -15
  73. package/dist/zspec/zcl/definition/foundation.js.map +1 -1
  74. package/dist/zspec/zcl/definition/tstype.d.ts +105 -11
  75. package/dist/zspec/zcl/definition/tstype.d.ts.map +1 -1
  76. package/dist/zspec/zcl/index.d.ts +1 -0
  77. package/dist/zspec/zcl/index.d.ts.map +1 -1
  78. package/dist/zspec/zcl/index.js.map +1 -1
  79. package/dist/zspec/zcl/utils.d.ts +1 -1
  80. package/dist/zspec/zcl/utils.d.ts.map +1 -1
  81. package/dist/zspec/zcl/utils.js +1 -1
  82. package/dist/zspec/zcl/utils.js.map +1 -1
  83. package/dist/zspec/zcl/zclFrame.d.ts.map +1 -1
  84. package/dist/zspec/zcl/zclFrame.js +32 -20
  85. package/dist/zspec/zcl/zclFrame.js.map +1 -1
  86. package/dist/zspec/zdo/buffaloZdo.d.ts +0 -6
  87. package/dist/zspec/zdo/buffaloZdo.d.ts.map +1 -1
  88. package/dist/zspec/zdo/buffaloZdo.js +0 -8
  89. package/dist/zspec/zdo/buffaloZdo.js.map +1 -1
  90. package/package.json +3 -3
  91. package/src/adapter/ember/ezsp/buffalo.ts +0 -5
  92. package/src/adapter/ember/uart/ash.ts +0 -2
  93. package/src/adapter/ezsp/driver/driver.ts +1 -1
  94. package/src/buffalo/buffalo.ts +8 -0
  95. package/src/controller/controller.ts +13 -16
  96. package/src/controller/events.ts +2 -1
  97. package/src/controller/greenPower.ts +4 -4
  98. package/src/controller/helpers/request.ts +3 -1
  99. package/src/controller/helpers/zclFrameConverter.ts +13 -17
  100. package/src/controller/model/device.ts +103 -148
  101. package/src/controller/model/endpoint.ts +112 -64
  102. package/src/controller/model/group.ts +33 -9
  103. package/src/controller/model/index.ts +1 -0
  104. package/src/controller/model/zigbeeEntity.ts +30 -0
  105. package/src/controller/tstype.ts +251 -16
  106. package/src/zspec/zcl/buffaloZcl.ts +323 -238
  107. package/src/zspec/zcl/definition/cluster.ts +156 -33
  108. package/src/zspec/zcl/definition/clusters-typegen.ts +588 -0
  109. package/src/zspec/zcl/definition/clusters-types.ts +7331 -0
  110. package/src/zspec/zcl/definition/enums.ts +14 -5
  111. package/src/zspec/zcl/definition/foundation.ts +43 -15
  112. package/src/zspec/zcl/definition/tstype.ts +118 -8
  113. package/src/zspec/zcl/index.ts +1 -0
  114. package/src/zspec/zcl/utils.ts +1 -1
  115. package/src/zspec/zcl/zclFrame.ts +37 -19
  116. package/src/zspec/zdo/buffaloZdo.ts +0 -9
  117. package/test/controller.test.ts +356 -896
  118. package/test/greenpower.test.ts +0 -12
  119. package/test/zcl.test.ts +13 -11
  120. package/test/zspec/zcl/buffalo.test.ts +216 -74
  121. package/test/zspec/zcl/frame.test.ts +62 -28
  122. package/test/zspec/zcl/utils.test.ts +4 -4
@@ -2,15 +2,30 @@ import {Buffalo} from "../../buffalo";
2
2
  import {logger} from "../../utils/logger";
3
3
  import {isNumberArray} from "../../utils/utils";
4
4
  import {BuffaloZclDataType, DataType, StructuredIndicatorType} from "./definition/enums";
5
- import type {BuffaloZclOptions, StructuredSelector, ZclArray} from "./definition/tstype";
5
+ import type {
6
+ BuffaloZclOptions,
7
+ ExtensionFieldSet,
8
+ Gpd,
9
+ GpdAttributeReport,
10
+ GpdChannelConfiguration,
11
+ GpdChannelRequest,
12
+ GpdCommissioningReply,
13
+ GpdCustomReply,
14
+ KeyZclValue,
15
+ MiboxerZone,
16
+ Struct,
17
+ StructuredSelector,
18
+ ThermoTransition,
19
+ TuyaDataPointValue,
20
+ ZclArray,
21
+ ZclDate,
22
+ ZclTimeOfDay,
23
+ ZoneInfo,
24
+ } from "./definition/tstype";
6
25
  import * as Utils from "./utils";
7
26
 
8
27
  const NS = "zh:zcl:buffalo";
9
28
 
10
- interface KeyValue {
11
- [s: string | number]: number | string;
12
- }
13
-
14
29
  const SEC_KEY_LENGTH = 16;
15
30
 
16
31
  const EXTENSION_FIELD_SETS_DATA_TYPE: {[key: number]: DataType[]} = {
@@ -20,175 +35,252 @@ const EXTENSION_FIELD_SETS_DATA_TYPE: {[key: number]: DataType[]} = {
20
35
  768: [DataType.UINT16, DataType.UINT16, DataType.UINT16, DataType.UINT8, DataType.UINT8, DataType.UINT8, DataType.UINT16, DataType.UINT16],
21
36
  };
22
37
 
23
- interface Struct {
24
- elmType: DataType;
25
- elmVal: unknown;
26
- }
38
+ export class BuffaloZcl extends Buffalo {
39
+ // TODO: shuffles quite a lot of code (ZH is mostly typed `string | number` and ZHC requires lots of scrutiny)
40
+ // private writeZclBoolean(value: boolean | undefined): void {
41
+ // this.writeUInt8(value === undefined ? 0xff : value ? 1 : 0);
42
+ // }
27
43
 
28
- interface ZclTimeOfDay {
29
- /** [0-23] */
30
- hours?: number;
31
- /** [0-59] */
32
- minutes?: number;
33
- /** [0-59] */
34
- seconds?: number;
35
- /** [0-99] */
36
- hundredths?: number;
37
- }
44
+ // private readZclBoolean(): boolean | undefined {
45
+ // const value = this.readUInt8();
38
46
 
39
- interface ZclDate {
40
- /** [1900-2155], converted to/from [0-255] => value+1900=year */
41
- year?: number;
42
- /** [1-12] */
43
- month?: number;
44
- /** [1-31] */
45
- dayOfMonth?: number;
46
- /** [1-7] */
47
- dayOfWeek?: number;
48
- }
47
+ // return value === 0xff ? undefined : !!value;
48
+ // }
49
49
 
50
- interface ZoneInfo {
51
- zoneID: number;
52
- zoneStatus: number;
53
- }
50
+ private writeZclUInt8(value: number): void {
51
+ this.writeUInt8(Number.isNaN(value) ? 0xff : value);
52
+ }
54
53
 
55
- interface ExtensionFieldSet {
56
- clstId: number;
57
- len: number;
58
- extField: unknown[];
59
- }
54
+ private readZclUInt8(): number {
55
+ const value = this.readUInt8();
60
56
 
61
- interface ThermoTransition {
62
- transitionTime: number;
63
- heatSetpoint?: number;
64
- coolSetpoint?: number;
65
- }
57
+ return value === 0xff ? Number.NaN : value;
58
+ }
66
59
 
67
- interface Gpd {
68
- deviceID: number;
69
- options: number;
70
- extendedOptions: number;
71
- securityKey: Buffer;
72
- keyMic: number;
73
- outgoingCounter: number;
74
- applicationInfo: number;
75
- manufacturerID: number;
76
- modelID: number;
77
- numGpdCommands: number;
78
- gpdCommandIdList: Buffer;
79
- numServerClusters: number;
80
- numClientClusters: number;
81
- gpdServerClusters: Buffer;
82
- gpdClientClusters: Buffer;
83
- genericSwitchConfig: number;
84
- currentContactStatus: number;
85
- }
60
+ private writeZclUInt16(value: number): void {
61
+ this.writeUInt16(Number.isNaN(value) ? 0xffff : value);
62
+ }
86
63
 
87
- interface GPDChannelRequest {
88
- nextChannel: number;
89
- nextNextChannel: number;
90
- }
64
+ private readZclUInt16(): number {
65
+ const value = this.readUInt16();
91
66
 
92
- export interface GPDChannelConfiguration {
93
- commandID: number;
94
- operationalChannel: number;
95
- basic: boolean;
96
- }
67
+ return value === 0xffff ? Number.NaN : value;
68
+ }
97
69
 
98
- export interface GPDCommissioningReply {
99
- commandID: number;
100
- options: number;
101
- /** expected valid if corresponding `options` bits set */
102
- panID?: number;
103
- /** expected valid if corresponding `options` bits set */
104
- securityKey?: Buffer;
105
- /** expected valid if corresponding `options` bits set */
106
- keyMic?: number;
107
- /** expected valid if corresponding `options` bits set */
108
- frameCounter?: number;
109
- }
70
+ private writeZclUInt24(value: number): void {
71
+ this.writeUInt24(Number.isNaN(value) ? 0xffffff : value);
72
+ }
110
73
 
111
- interface GPDCustomReply {
112
- commandID: number;
113
- buffer: Buffer;
114
- }
74
+ private readZclUInt24(): number {
75
+ const value = this.readUInt24();
115
76
 
116
- interface GPDAttributeReport {
117
- manufacturerCode: number;
118
- clusterID: number;
119
- attributes: KeyValue;
120
- }
77
+ return value === 0xffffff ? Number.NaN : value;
78
+ }
121
79
 
122
- interface TuyaDataPointValue {
123
- dp: number;
124
- datatype: number;
125
- data: Buffer;
126
- }
80
+ private writeZclUInt32(value: number): void {
81
+ this.writeUInt32(Number.isNaN(value) ? 0xffffffff : value);
82
+ }
127
83
 
128
- interface MiboxerZone {
129
- zoneNum: number;
130
- groupId: number;
131
- }
84
+ private readZclUInt32(): number {
85
+ const value = this.readUInt32();
132
86
 
133
- export class BuffaloZcl extends Buffalo {
134
- private writeOctetStr(value: number[]): void {
135
- // TODO: this does not allow "non-value" 0xFF
136
- this.writeUInt8(value.length);
137
- this.writeBuffer(value, value.length);
87
+ return value === 0xffffffff ? Number.NaN : value;
88
+ }
89
+
90
+ private writeZclUInt40(value: number): void {
91
+ this.writeUInt40(Number.isNaN(value) ? 0xffffffffff : value);
92
+ }
93
+
94
+ private readZclUInt40(): number {
95
+ const value = this.readUInt40();
96
+
97
+ return value === 0xffffffffff ? Number.NaN : value;
98
+ }
99
+
100
+ private writeZclUInt48(value: number): void {
101
+ this.writeUInt48(Number.isNaN(value) ? 0xffffffffffff : value);
102
+ }
103
+
104
+ private readZclUInt48(): number {
105
+ const value = this.readUInt48();
106
+
107
+ return value === 0xffffffffffff ? Number.NaN : value;
108
+ }
109
+
110
+ private writeZclUInt56(value: bigint | undefined): void {
111
+ this.writeUInt56(value === undefined ? 0xffffffffffffffn : value);
112
+ }
113
+
114
+ private readZclUInt56(): bigint | undefined {
115
+ const value = this.readUInt56();
116
+
117
+ return value === 0xffffffffffffffn ? undefined : value;
118
+ }
119
+
120
+ private writeZclUInt64(value: bigint | undefined): void {
121
+ this.writeUInt64(value === undefined ? 0xffffffffffffffffn : value);
122
+ }
123
+
124
+ private readZclUInt64(): bigint | undefined {
125
+ const value = this.readUInt64();
126
+
127
+ return value === 0xffffffffffffffffn ? undefined : value;
128
+ }
129
+
130
+ private writeZclInt8(value: number): void {
131
+ this.writeInt8(Number.isNaN(value) ? -0x80 : value);
132
+ }
133
+
134
+ private readZclInt8(): number {
135
+ const value = this.readInt8();
136
+
137
+ return value === -0x80 ? Number.NaN : value;
138
+ }
139
+
140
+ private writeZclInt16(value: number): void {
141
+ this.writeInt16(Number.isNaN(value) ? -0x8000 : value);
142
+ }
143
+
144
+ private readZclInt16(): number {
145
+ const value = this.readInt16();
146
+
147
+ return value === -0x8000 ? Number.NaN : value;
148
+ }
149
+
150
+ private writeZclInt24(value: number): void {
151
+ this.writeInt24(Number.isNaN(value) ? -0x800000 : value);
152
+ }
153
+
154
+ private readZclInt24(): number {
155
+ const value = this.readInt24();
156
+
157
+ return value === -0x800000 ? Number.NaN : value;
158
+ }
159
+
160
+ private writeZclInt32(value: number): void {
161
+ this.writeInt32(Number.isNaN(value) ? -0x80000000 : value);
162
+ }
163
+
164
+ private readZclInt32(): number {
165
+ const value = this.readInt32();
166
+
167
+ return value === -0x80000000 ? Number.NaN : value;
168
+ }
169
+
170
+ private writeZclInt40(value: number): void {
171
+ this.writeInt40(Number.isNaN(value) ? -0x8000000000 : value);
172
+ }
173
+
174
+ private readZclInt40(): number {
175
+ const value = this.readInt40();
176
+
177
+ return value === -0x8000000000 ? Number.NaN : value;
178
+ }
179
+
180
+ private writeZclInt48(value: number): void {
181
+ this.writeInt48(Number.isNaN(value) ? -0x800000000000 : value);
182
+ }
183
+
184
+ private readZclInt48(): number {
185
+ const value = this.readInt48();
186
+
187
+ return value === -0x800000000000 ? Number.NaN : value;
188
+ }
189
+
190
+ private writeZclInt56(value: bigint | undefined): void {
191
+ this.writeInt56(value === undefined ? -0x80000000000000n : value);
192
+ }
193
+
194
+ private readZclInt56(): bigint | undefined {
195
+ const value = this.readInt56();
196
+
197
+ return value === -0x80000000000000n ? undefined : value;
198
+ }
199
+
200
+ private writeZclInt64(value: bigint | undefined): void {
201
+ this.writeInt64(value === undefined ? -0x8000000000000000n : value);
202
+ }
203
+
204
+ private readZclInt64(): bigint | undefined {
205
+ const value = this.readInt64();
206
+
207
+ return value === -0x8000000000000000n ? undefined : value;
208
+ }
209
+
210
+ private writeOctetStr(value?: number[]): void {
211
+ if (value) {
212
+ this.writeUInt8(value.length);
213
+ this.writeBuffer(value, value.length);
214
+ } else {
215
+ this.writeUInt8(0xff); // non-value
216
+ }
138
217
  }
139
218
 
140
219
  private readOctetStr(): Buffer {
141
- const length = this.readUInt8();
142
- return length < 0xff ? this.readBuffer(length) : Buffer.from([]); // non-value
220
+ const length = this.readZclUInt8();
221
+ return Number.isNaN(length) ? Buffer.from([]) : this.readBuffer(length);
143
222
  }
144
223
 
145
- private writeCharStr(value: string | number[]): void {
146
- // TODO: this does not allow "non-value" 0xFF
147
- if (typeof value === "string") {
148
- this.writeUInt8(Buffer.byteLength(value, "utf8"));
149
- this.writeUtf8String(value);
224
+ private writeCharStr(value?: string | number[]): void {
225
+ if (value) {
226
+ if (typeof value === "string") {
227
+ this.writeUInt8(Buffer.byteLength(value, "utf8"));
228
+ this.writeUtf8String(value);
229
+ } else {
230
+ // XXX: value.length not written?
231
+ this.writeBuffer(value, value.length);
232
+ }
150
233
  } else {
151
- // XXX: value.length not written?
152
- this.writeBuffer(value, value.length);
234
+ this.writeUInt8(0xff); // non-value
153
235
  }
154
236
  }
155
237
 
156
238
  private readCharStr(): string {
157
- const length = this.readUInt8();
239
+ const length = this.readZclUInt8();
158
240
 
159
- return length < 0xff ? this.readUtf8String(length) : "";
241
+ return Number.isNaN(length) ? "" : this.readUtf8String(length);
160
242
  }
161
243
 
162
- private writeLongOctetStr(value: number[]): void {
163
- // TODO: this does not allow "non-value" 0xFF
164
- this.writeUInt16(value.length);
165
- this.writeBuffer(value, value.length);
244
+ private writeLongOctetStr(value?: number[]): void {
245
+ if (value) {
246
+ this.writeUInt16(value.length);
247
+ this.writeBuffer(value, value.length);
248
+ } else {
249
+ this.writeUInt16(0xffff); // non-value
250
+ }
166
251
  }
167
252
 
168
253
  private readLongOctetStr(): Buffer {
169
- const length = this.readUInt16();
170
- return length < 0xffff ? this.readBuffer(length) : Buffer.from([]); // non-value
254
+ const length = this.readZclUInt16();
255
+ return Number.isNaN(length) ? Buffer.from([]) : this.readBuffer(length);
171
256
  }
172
257
 
173
- private writeLongCharStr(value: string): void {
174
- // TODO: this does not allow "non-value" 0xFF
175
- this.writeUInt16(Buffer.byteLength(value, "utf8"));
176
- this.writeUtf8String(value);
258
+ private writeLongCharStr(value?: string): void {
259
+ if (value) {
260
+ this.writeUInt16(Buffer.byteLength(value, "utf8"));
261
+ this.writeUtf8String(value);
262
+ } else {
263
+ this.writeUInt16(0xffff); // non-value
264
+ }
177
265
  }
178
266
 
179
267
  private readLongCharStr(): string {
180
- const length = this.readUInt16();
181
- return length < 0xffff ? this.readUtf8String(length) : ""; // non-value
268
+ const length = this.readZclUInt16();
269
+ return Number.isNaN(length) ? "" : this.readUtf8String(length);
182
270
  }
183
271
 
184
- private writeArray(value: ZclArray): void {
185
- const elTypeNumeric = typeof value.elementType === "number" ? value.elementType : DataType[value.elementType];
186
- this.writeUInt8(elTypeNumeric);
187
- // TODO: this does not allow writing "non-value" 0xFFFF
188
- this.writeUInt16(value.elements.length);
272
+ private writeArray(value?: ZclArray): void {
273
+ if (value) {
274
+ const elTypeNumeric = typeof value.elementType === "number" ? value.elementType : DataType[value.elementType];
275
+ this.writeUInt8(elTypeNumeric);
276
+ this.writeUInt16(value.elements.length);
189
277
 
190
- for (const element of value.elements) {
191
- this.write(elTypeNumeric, element, {});
278
+ for (const element of value.elements) {
279
+ this.write(elTypeNumeric, element, {});
280
+ }
281
+ } else {
282
+ this.writeUInt8(DataType.NO_DATA); // XXX: correct value?
283
+ this.writeUInt16(0xffff); // non-value
192
284
  }
193
285
  }
194
286
 
@@ -196,9 +288,10 @@ export class BuffaloZcl extends Buffalo {
196
288
  const values: unknown[] = [];
197
289
 
198
290
  const elementType = this.readUInt8();
199
- const numberOfElements = this.readUInt16();
291
+ const numberOfElements = this.readZclUInt16();
200
292
 
201
- if (numberOfElements < 0xffff) {
293
+ if (!Number.isNaN(numberOfElements)) {
294
+ // not non-value
202
295
  for (let i = 0; i < numberOfElements; i++) {
203
296
  const value = this.read(elementType, {});
204
297
  values.push(value);
@@ -208,23 +301,25 @@ export class BuffaloZcl extends Buffalo {
208
301
  return values;
209
302
  }
210
303
 
211
- private writeStruct(value: Struct[]): void {
212
- // XXX: from ZCL spec: "The zeroth element may not be written to."
213
- // how does this translates to writing here?
214
- // TODO: this does not allow writing "non-value" 0xFFFF
215
- this.writeUInt16(value.length);
304
+ private writeStruct(value?: Struct[]): void {
305
+ if (value) {
306
+ this.writeUInt16(value.length);
216
307
 
217
- for (const v of value) {
218
- this.writeUInt8(v.elmType);
219
- this.write(v.elmType, v.elmVal, {});
308
+ for (const v of value) {
309
+ this.writeUInt8(v.elmType);
310
+ this.write(v.elmType, v.elmVal, {});
311
+ }
312
+ } else {
313
+ this.writeUInt16(0xffff); // non-value
220
314
  }
221
315
  }
222
316
 
223
317
  private readStruct(): Struct[] {
224
318
  const values: Struct[] = [];
225
- const numberOfElements = this.readUInt16();
319
+ const numberOfElements = this.readZclUInt16();
226
320
 
227
- if (numberOfElements < 0xffff) {
321
+ if (!Number.isNaN(numberOfElements)) {
322
+ // not non-value
228
323
  for (let i = 0; i < numberOfElements; i++) {
229
324
  const elementType = this.readUInt8();
230
325
  const value = this.read(elementType, {});
@@ -236,44 +331,44 @@ export class BuffaloZcl extends Buffalo {
236
331
  }
237
332
 
238
333
  private writeToD(value: ZclTimeOfDay): void {
239
- this.writeUInt8(value.hours ?? 0xff);
240
- this.writeUInt8(value.minutes ?? 0xff);
241
- this.writeUInt8(value.seconds ?? 0xff);
242
- this.writeUInt8(value.hundredths ?? 0xff);
334
+ this.writeUInt8(value.hours == null || Number.isNaN(value.hours) ? 0xff : value.hours);
335
+ this.writeUInt8(value.minutes == null || Number.isNaN(value.minutes) ? 0xff : value.minutes);
336
+ this.writeUInt8(value.seconds == null || Number.isNaN(value.seconds) ? 0xff : value.seconds);
337
+ this.writeUInt8(value.hundredths == null || Number.isNaN(value.hundredths) ? 0xff : value.hundredths);
243
338
  }
244
339
 
245
340
  private readToD(): ZclTimeOfDay {
246
- const hours = this.readUInt8();
247
- const minutes = this.readUInt8();
248
- const seconds = this.readUInt8();
249
- const hundredths = this.readUInt8();
341
+ const hours = this.readZclUInt8();
342
+ const minutes = this.readZclUInt8();
343
+ const seconds = this.readZclUInt8();
344
+ const hundredths = this.readZclUInt8();
250
345
 
251
346
  return {
252
- hours: hours < 0xff ? hours : undefined,
253
- minutes: minutes < 0xff ? minutes : undefined,
254
- seconds: seconds < 0xff ? seconds : undefined,
255
- hundredths: hundredths < 0xff ? hundredths : undefined,
347
+ hours,
348
+ minutes,
349
+ seconds,
350
+ hundredths,
256
351
  };
257
352
  }
258
353
 
259
354
  private writeDate(value: ZclDate): void {
260
- this.writeUInt8(value.year !== undefined ? value.year - 1900 : 0xff);
261
- this.writeUInt8(value.month ?? 0xff);
262
- this.writeUInt8(value.dayOfMonth ?? 0xff);
263
- this.writeUInt8(value.dayOfWeek ?? 0xff);
355
+ this.writeUInt8(value.year == null || Number.isNaN(value.year) ? 0xff : value.year - 1900);
356
+ this.writeUInt8(value.month == null || Number.isNaN(value.month) ? 0xff : value.month);
357
+ this.writeUInt8(value.dayOfMonth == null || Number.isNaN(value.dayOfMonth) ? 0xff : value.dayOfMonth);
358
+ this.writeUInt8(value.dayOfWeek == null || Number.isNaN(value.dayOfWeek) ? 0xff : value.dayOfWeek);
264
359
  }
265
360
 
266
361
  private readDate(): ZclDate {
267
- const year = this.readUInt8();
268
- const month = this.readUInt8();
269
- const dayOfMonth = this.readUInt8();
270
- const dayOfWeek = this.readUInt8();
362
+ const year = this.readZclUInt8();
363
+ const month = this.readZclUInt8();
364
+ const dayOfMonth = this.readZclUInt8();
365
+ const dayOfWeek = this.readZclUInt8();
271
366
 
272
367
  return {
273
- year: year < 0xff ? year + 1900 : undefined,
274
- month: month < 0xff ? month : undefined,
275
- dayOfMonth: dayOfMonth < 0xff ? dayOfMonth : undefined,
276
- dayOfWeek: dayOfWeek < 0xff ? dayOfWeek : undefined,
368
+ year: year + 1900, // remains NaN if year is NaN
369
+ month,
370
+ dayOfMonth,
371
+ dayOfWeek,
277
372
  };
278
373
  }
279
374
 
@@ -375,10 +470,10 @@ export class BuffaloZcl extends Buffalo {
375
470
  return result;
376
471
  }
377
472
 
378
- private writeGpdFrame(value: GPDCommissioningReply | GPDChannelConfiguration | GPDCustomReply): void {
473
+ private writeGpdFrame(value: GpdCommissioningReply | GpdChannelConfiguration | GpdCustomReply): void {
379
474
  if (value.commandID === 0xf0) {
380
475
  // Commissioning Reply
381
- const v = value as GPDCommissioningReply;
476
+ const v = value as GpdCommissioningReply;
382
477
 
383
478
  const panIDPresent = Boolean(v.options & 0x1);
384
479
  const gpdSecurityKeyPresent = Boolean(v.options & 0x2);
@@ -411,13 +506,13 @@ export class BuffaloZcl extends Buffalo {
411
506
  }
412
507
  } else if (value.commandID === 0xf3) {
413
508
  // Channel configuration
414
- const v = value as GPDChannelConfiguration;
509
+ const v = value as GpdChannelConfiguration;
415
510
 
416
511
  this.writeUInt8(1);
417
512
  this.writeUInt8((v.operationalChannel & 0xf) | ((v.basic ? 1 : 0) << 4));
418
513
  } else if (value.commandID === 0xf4 || value.commandID === 0xf5 || (value.commandID >= 0xf7 && value.commandID <= 0xff)) {
419
514
  // Other commands sent to GPD
420
- const v = value as GPDCustomReply;
515
+ const v = value as GpdCustomReply;
421
516
 
422
517
  this.writeUInt8(v.buffer.length);
423
518
  this.writeBuffer(v.buffer, v.buffer.length);
@@ -427,11 +522,19 @@ export class BuffaloZcl extends Buffalo {
427
522
  // 0xf6: ZCL Tunneling
428
523
  }
429
524
 
430
- private readGpdFrame(options: BuffaloZclOptions): Gpd | GPDChannelRequest | GPDAttributeReport | {raw: Buffer} | Record<string, never> {
525
+ private readGpdFrame(options: BuffaloZclOptions): Gpd | GpdChannelRequest | GpdAttributeReport | {raw: Buffer} | Record<string, never> {
526
+ if (options.payload?.payloadSize === undefined) {
527
+ throw new Error("Cannot read GPD_FRAME without required payload options specified");
528
+ }
529
+
530
+ if (Number.isNaN(options.payload.payloadSize) || options.payload.payloadSize === 0) {
531
+ return {}; // non-value, don't move position
532
+ }
533
+
431
534
  // ensure offset by options.payload.payloadSize (if any) at end of parsing to not cause issues with spec changes (until supported)
432
535
  const startPosition = this.position;
433
536
 
434
- if (options.payload?.commandID === 0xe0) {
537
+ if (options.payload.commandID === 0xe0) {
435
538
  // Commisioning
436
539
  const frame = {
437
540
  deviceID: this.readUInt8(),
@@ -507,22 +610,16 @@ export class BuffaloZcl extends Buffalo {
507
610
  }
508
611
  }
509
612
 
510
- if (options.payload.payloadSize) {
511
- this.position = startPosition + options.payload.payloadSize;
512
- }
613
+ this.setPosition(startPosition + options.payload.payloadSize);
513
614
 
514
615
  return frame;
515
616
  }
516
617
 
517
- if (options.payload?.commandID === 0xe3) {
618
+ if (options.payload.commandID === 0xe3) {
518
619
  // Channel Request
519
620
  const channelOpts = this.readUInt8();
520
621
 
521
- /* v8 ignore start */
522
- if (options.payload?.payloadSize) {
523
- this.position = startPosition + options.payload.payloadSize;
524
- }
525
- /* v8 ignore stop */
622
+ this.setPosition(startPosition + options.payload.payloadSize);
526
623
 
527
624
  return {
528
625
  nextChannel: channelOpts & 0xf,
@@ -530,17 +627,13 @@ export class BuffaloZcl extends Buffalo {
530
627
  };
531
628
  }
532
629
 
533
- if (options.payload?.commandID === 0xa1) {
630
+ if (options.payload.commandID === 0xa1) {
534
631
  // Manufacturer-specific Attribute Reporting
535
- if (options.payload.payloadSize === undefined) {
536
- throw new Error("Cannot read GPD_FRAME with commandID=0xA1 without payloadSize options specified");
537
- }
538
-
539
632
  const start = this.position;
540
633
  const frame = {
541
634
  manufacturerCode: this.readUInt16(),
542
635
  clusterID: this.readUInt16(),
543
- attributes: {} as KeyValue,
636
+ attributes: {} as KeyZclValue,
544
637
  };
545
638
 
546
639
  const cluster = Utils.getCluster(frame.clusterID, frame.manufacturerCode, {});
@@ -562,21 +655,13 @@ export class BuffaloZcl extends Buffalo {
562
655
  frame.attributes[attribute] = this.read(type, options);
563
656
  }
564
657
 
565
- this.position = startPosition + options.payload.payloadSize;
658
+ this.setPosition(startPosition + options.payload.payloadSize);
566
659
 
567
660
  return frame;
568
661
  }
569
662
 
570
- if (options.payload?.payloadSize && this.isMore()) {
571
- // might contain `gppNwkAddr`, `gppGpdLink` & `mic` from ZCL cluster, so limit by `payloadSize`
572
- return {raw: this.readBuffer(options.payload.payloadSize)};
573
- }
574
-
575
- if (options.payload?.payloadSize) {
576
- this.position = startPosition + options.payload.payloadSize;
577
- }
578
-
579
- return {};
663
+ // might contain `gppNwkAddr`, `gppGpdLink` & `mic` from ZCL cluster after this, so limit by `payloadSize`
664
+ return {raw: this.readBuffer(options.payload.payloadSize)};
580
665
  }
581
666
 
582
667
  private writeStructuredSelector(value: StructuredSelector): void {
@@ -715,12 +800,12 @@ export class BuffaloZcl extends Buffalo {
715
800
  case DataType.UNKNOWN: {
716
801
  return; // nothing to write
717
802
  }
718
- case DataType.DATA8:
719
803
  case DataType.BOOLEAN:
804
+ case DataType.DATA8:
720
805
  case DataType.BITMAP8:
721
806
  case DataType.UINT8:
722
807
  case DataType.ENUM8: {
723
- this.writeUInt8(value);
808
+ this.writeZclUInt8(value);
724
809
  break;
725
810
  }
726
811
  case DataType.DATA16:
@@ -729,13 +814,13 @@ export class BuffaloZcl extends Buffalo {
729
814
  case DataType.ENUM16:
730
815
  case DataType.CLUSTER_ID:
731
816
  case DataType.ATTR_ID: {
732
- this.writeUInt16(value);
817
+ this.writeZclUInt16(value);
733
818
  break;
734
819
  }
735
820
  case DataType.DATA24:
736
821
  case DataType.BITMAP24:
737
822
  case DataType.UINT24: {
738
- this.writeUInt24(value);
823
+ this.writeZclUInt24(value);
739
824
  break;
740
825
  }
741
826
  case DataType.DATA32:
@@ -743,63 +828,63 @@ export class BuffaloZcl extends Buffalo {
743
828
  case DataType.UINT32:
744
829
  case DataType.UTC:
745
830
  case DataType.BAC_OID: {
746
- this.writeUInt32(value);
831
+ this.writeZclUInt32(value);
747
832
  break;
748
833
  }
749
834
  case DataType.DATA40:
750
835
  case DataType.BITMAP40:
751
836
  case DataType.UINT40: {
752
- this.writeUInt40(value);
837
+ this.writeZclUInt40(value);
753
838
  break;
754
839
  }
755
840
  case DataType.DATA48:
756
841
  case DataType.BITMAP48:
757
842
  case DataType.UINT48: {
758
- this.writeUInt48(value);
843
+ this.writeZclUInt48(value);
759
844
  break;
760
845
  }
761
846
  case DataType.DATA56:
762
847
  case DataType.BITMAP56:
763
848
  case DataType.UINT56: {
764
- this.writeUInt56(value);
849
+ this.writeZclUInt56(value);
765
850
  break;
766
851
  }
767
852
  case DataType.DATA64:
768
853
  case DataType.BITMAP64:
769
854
  case DataType.UINT64: {
770
- this.writeUInt64(value);
855
+ this.writeZclUInt64(value);
771
856
  break;
772
857
  }
773
858
  case DataType.INT8: {
774
- this.writeInt8(value);
859
+ this.writeZclInt8(value);
775
860
  break;
776
861
  }
777
862
  case DataType.INT16: {
778
- this.writeInt16(value);
863
+ this.writeZclInt16(value);
779
864
  break;
780
865
  }
781
866
  case DataType.INT24: {
782
- this.writeInt24(value);
867
+ this.writeZclInt24(value);
783
868
  break;
784
869
  }
785
870
  case DataType.INT32: {
786
- this.writeInt32(value);
871
+ this.writeZclInt32(value);
787
872
  break;
788
873
  }
789
874
  case DataType.INT40: {
790
- this.writeInt40(value);
875
+ this.writeZclInt40(value);
791
876
  break;
792
877
  }
793
878
  case DataType.INT48: {
794
- this.writeInt48(value);
879
+ this.writeZclInt48(value);
795
880
  break;
796
881
  }
797
882
  case DataType.INT56: {
798
- this.writeInt56(value);
883
+ this.writeZclInt56(value);
799
884
  break;
800
885
  }
801
886
  case DataType.INT64: {
802
- this.writeInt64(value);
887
+ this.writeZclInt64(value);
803
888
  break;
804
889
  }
805
890
  // case DataType.SEMI_PREC: {
@@ -944,12 +1029,12 @@ export class BuffaloZcl extends Buffalo {
944
1029
  case DataType.UNKNOWN: {
945
1030
  return; // nothing to write
946
1031
  }
947
- case DataType.DATA8:
948
1032
  case DataType.BOOLEAN:
1033
+ case DataType.DATA8:
949
1034
  case DataType.BITMAP8:
950
1035
  case DataType.UINT8:
951
1036
  case DataType.ENUM8: {
952
- return this.readUInt8();
1037
+ return this.readZclUInt8();
953
1038
  }
954
1039
  case DataType.DATA16:
955
1040
  case DataType.BITMAP16:
@@ -957,63 +1042,63 @@ export class BuffaloZcl extends Buffalo {
957
1042
  case DataType.ENUM16:
958
1043
  case DataType.CLUSTER_ID:
959
1044
  case DataType.ATTR_ID: {
960
- return this.readUInt16();
1045
+ return this.readZclUInt16();
961
1046
  }
962
1047
  case DataType.DATA24:
963
1048
  case DataType.BITMAP24:
964
1049
  case DataType.UINT24: {
965
- return this.readUInt24();
1050
+ return this.readZclUInt24();
966
1051
  }
967
1052
  case DataType.DATA32:
968
1053
  case DataType.BITMAP32:
969
1054
  case DataType.UINT32:
970
1055
  case DataType.UTC:
971
1056
  case DataType.BAC_OID: {
972
- return this.readUInt32();
1057
+ return this.readZclUInt32();
973
1058
  }
974
1059
  case DataType.DATA40:
975
1060
  case DataType.BITMAP40:
976
1061
  case DataType.UINT40: {
977
- return this.readUInt40();
1062
+ return this.readZclUInt40();
978
1063
  }
979
1064
  case DataType.DATA48:
980
1065
  case DataType.BITMAP48:
981
1066
  case DataType.UINT48: {
982
- return this.readUInt48();
1067
+ return this.readZclUInt48();
983
1068
  }
984
1069
  case DataType.DATA56:
985
1070
  case DataType.BITMAP56:
986
1071
  case DataType.UINT56: {
987
- return this.readUInt56();
1072
+ return this.readZclUInt56();
988
1073
  }
989
1074
  case DataType.DATA64:
990
1075
  case DataType.BITMAP64:
991
1076
  case DataType.UINT64: {
992
- return this.readUInt64();
1077
+ return this.readZclUInt64();
993
1078
  }
994
1079
  case DataType.INT8: {
995
- return this.readInt8();
1080
+ return this.readZclInt8();
996
1081
  }
997
1082
  case DataType.INT16: {
998
- return this.readInt16();
1083
+ return this.readZclInt16();
999
1084
  }
1000
1085
  case DataType.INT24: {
1001
- return this.readInt24();
1086
+ return this.readZclInt24();
1002
1087
  }
1003
1088
  case DataType.INT32: {
1004
- return this.readInt32();
1089
+ return this.readZclInt32();
1005
1090
  }
1006
1091
  case DataType.INT40: {
1007
- return this.readInt40();
1092
+ return this.readZclInt40();
1008
1093
  }
1009
1094
  case DataType.INT48: {
1010
- return this.readInt48();
1095
+ return this.readZclInt48();
1011
1096
  }
1012
1097
  case DataType.INT56: {
1013
- return this.readInt56();
1098
+ return this.readZclInt56();
1014
1099
  }
1015
1100
  case DataType.INT64: {
1016
- return this.readInt64();
1101
+ return this.readZclInt64();
1017
1102
  }
1018
1103
  // case DataType.SEMI_PREC: {
1019
1104
  // // https://tc39.es/proposal-float16array/