@willieee802/zigbee-herdsman 0.49.4 → 0.50.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 (209) hide show
  1. package/.github/dependabot.yml +0 -3
  2. package/.github/workflows/ci.yml +1 -2
  3. package/.github/workflows/release-please.yml +1 -1
  4. package/.github/workflows/typedoc.yaml +3 -3
  5. package/.release-please-manifest.json +1 -1
  6. package/CHANGELOG.md +143 -0
  7. package/biome.json +1 -1
  8. package/dist/adapter/adapter.d.ts +14 -1
  9. package/dist/adapter/adapter.d.ts.map +1 -1
  10. package/dist/adapter/adapter.js +17 -0
  11. package/dist/adapter/adapter.js.map +1 -1
  12. package/dist/adapter/adapterDiscovery.d.ts.map +1 -1
  13. package/dist/adapter/adapterDiscovery.js.map +1 -1
  14. package/dist/adapter/deconz/adapter/deconzAdapter.d.ts +1 -3
  15. package/dist/adapter/deconz/adapter/deconzAdapter.d.ts.map +1 -1
  16. package/dist/adapter/deconz/adapter/deconzAdapter.js +14 -29
  17. package/dist/adapter/deconz/adapter/deconzAdapter.js.map +1 -1
  18. package/dist/adapter/deconz/driver/constants.d.ts +1 -1
  19. package/dist/adapter/deconz/driver/constants.d.ts.map +1 -1
  20. package/dist/adapter/ember/adapter/emberAdapter.d.ts +1 -1
  21. package/dist/adapter/ember/adapter/emberAdapter.d.ts.map +1 -1
  22. package/dist/adapter/ember/adapter/emberAdapter.js +19 -10
  23. package/dist/adapter/ember/adapter/emberAdapter.js.map +1 -1
  24. package/dist/adapter/ember/adapter/oneWaitress.d.ts +2 -0
  25. package/dist/adapter/ember/adapter/oneWaitress.d.ts.map +1 -1
  26. package/dist/adapter/ember/adapter/oneWaitress.js +13 -5
  27. package/dist/adapter/ember/adapter/oneWaitress.js.map +1 -1
  28. package/dist/adapter/ezsp/adapter/ezspAdapter.d.ts +1 -3
  29. package/dist/adapter/ezsp/adapter/ezspAdapter.d.ts.map +1 -1
  30. package/dist/adapter/ezsp/adapter/ezspAdapter.js +17 -30
  31. package/dist/adapter/ezsp/adapter/ezspAdapter.js.map +1 -1
  32. package/dist/adapter/ezsp/driver/index.d.ts +1 -1
  33. package/dist/adapter/ezsp/driver/index.d.ts.map +1 -1
  34. package/dist/adapter/ezsp/driver/index.js +1 -1
  35. package/dist/adapter/ezsp/driver/index.js.map +1 -1
  36. package/dist/adapter/ezsp/driver/types/index.d.ts +1 -1
  37. package/dist/adapter/ezsp/driver/types/index.d.ts.map +1 -1
  38. package/dist/adapter/ezsp/driver/types/index.js +3 -3
  39. package/dist/adapter/ezsp/driver/types/index.js.map +1 -1
  40. package/dist/adapter/serialPort.d.ts.map +1 -1
  41. package/dist/adapter/serialPort.js +7 -0
  42. package/dist/adapter/serialPort.js.map +1 -1
  43. package/dist/adapter/z-stack/adapter/adapter-backup.js +1 -1
  44. package/dist/adapter/z-stack/adapter/adapter-backup.js.map +1 -1
  45. package/dist/adapter/z-stack/adapter/adapter-nv-memory.js +1 -1
  46. package/dist/adapter/z-stack/adapter/adapter-nv-memory.js.map +1 -1
  47. package/dist/adapter/z-stack/adapter/manager.d.ts.map +1 -1
  48. package/dist/adapter/z-stack/adapter/manager.js +12 -2
  49. package/dist/adapter/z-stack/adapter/manager.js.map +1 -1
  50. package/dist/adapter/z-stack/adapter/tstype.d.ts.map +1 -1
  51. package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts +1 -3
  52. package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts.map +1 -1
  53. package/dist/adapter/z-stack/adapter/zStackAdapter.js +20 -34
  54. package/dist/adapter/z-stack/adapter/zStackAdapter.js.map +1 -1
  55. package/dist/adapter/z-stack/constants/index.d.ts +1 -1
  56. package/dist/adapter/z-stack/constants/index.d.ts.map +1 -1
  57. package/dist/adapter/z-stack/constants/index.js +1 -1
  58. package/dist/adapter/z-stack/constants/index.js.map +1 -1
  59. package/dist/adapter/z-stack/unpi/constants.d.ts +1 -1
  60. package/dist/adapter/z-stack/unpi/constants.d.ts.map +1 -1
  61. package/dist/adapter/z-stack/unpi/constants.js +1 -1
  62. package/dist/adapter/z-stack/unpi/constants.js.map +1 -1
  63. package/dist/adapter/zboss/adapter/zbossAdapter.d.ts +7 -8
  64. package/dist/adapter/zboss/adapter/zbossAdapter.d.ts.map +1 -1
  65. package/dist/adapter/zboss/adapter/zbossAdapter.js +12 -30
  66. package/dist/adapter/zboss/adapter/zbossAdapter.js.map +1 -1
  67. package/dist/adapter/zboss/driver.d.ts.map +1 -1
  68. package/dist/adapter/zboss/driver.js +8 -1
  69. package/dist/adapter/zboss/driver.js.map +1 -1
  70. package/dist/adapter/zboss/uart.d.ts.map +1 -1
  71. package/dist/adapter/zboss/uart.js +14 -2
  72. package/dist/adapter/zboss/uart.js.map +1 -1
  73. package/dist/adapter/zigate/adapter/zigateAdapter.d.ts +1 -3
  74. package/dist/adapter/zigate/adapter/zigateAdapter.d.ts.map +1 -1
  75. package/dist/adapter/zigate/adapter/zigateAdapter.js +8 -29
  76. package/dist/adapter/zigate/adapter/zigateAdapter.js.map +1 -1
  77. package/dist/adapter/zoh/adapter/zohAdapter.d.ts +1 -3
  78. package/dist/adapter/zoh/adapter/zohAdapter.d.ts.map +1 -1
  79. package/dist/adapter/zoh/adapter/zohAdapter.js +18 -33
  80. package/dist/adapter/zoh/adapter/zohAdapter.js.map +1 -1
  81. package/dist/controller/controller.d.ts.map +1 -1
  82. package/dist/controller/controller.js +10 -2
  83. package/dist/controller/controller.js.map +1 -1
  84. package/dist/controller/greenPower.d.ts.map +1 -1
  85. package/dist/controller/greenPower.js +15 -9
  86. package/dist/controller/greenPower.js.map +1 -1
  87. package/dist/controller/helpers/ota.d.ts +4 -4
  88. package/dist/controller/helpers/ota.d.ts.map +1 -1
  89. package/dist/controller/helpers/ota.js +28 -9
  90. package/dist/controller/helpers/ota.js.map +1 -1
  91. package/dist/controller/helpers/zclFrameConverter.d.ts.map +1 -1
  92. package/dist/controller/helpers/zclFrameConverter.js +17 -16
  93. package/dist/controller/helpers/zclFrameConverter.js.map +1 -1
  94. package/dist/controller/model/device.d.ts +14 -3
  95. package/dist/controller/model/device.d.ts.map +1 -1
  96. package/dist/controller/model/device.js +155 -68
  97. package/dist/controller/model/device.js.map +1 -1
  98. package/dist/controller/model/endpoint.d.ts +7 -3
  99. package/dist/controller/model/endpoint.d.ts.map +1 -1
  100. package/dist/controller/model/endpoint.js +34 -21
  101. package/dist/controller/model/endpoint.js.map +1 -1
  102. package/dist/controller/model/group.js +4 -4
  103. package/dist/controller/model/group.js.map +1 -1
  104. package/dist/controller/touchlink.js +3 -3
  105. package/dist/controller/touchlink.js.map +1 -1
  106. package/dist/utils/timeService.js +2 -2
  107. package/dist/utils/timeService.js.map +1 -1
  108. package/dist/zspec/zcl/buffaloZcl.d.ts +3 -3
  109. package/dist/zspec/zcl/buffaloZcl.d.ts.map +1 -1
  110. package/dist/zspec/zcl/buffaloZcl.js +198 -96
  111. package/dist/zspec/zcl/buffaloZcl.js.map +1 -1
  112. package/dist/zspec/zcl/definition/cluster.d.ts +2 -2
  113. package/dist/zspec/zcl/definition/cluster.d.ts.map +1 -1
  114. package/dist/zspec/zcl/definition/cluster.js +2699 -2808
  115. package/dist/zspec/zcl/definition/cluster.js.map +1 -1
  116. package/dist/zspec/zcl/definition/clusters-types.d.ts +63 -1109
  117. package/dist/zspec/zcl/definition/clusters-types.d.ts.map +1 -1
  118. package/dist/zspec/zcl/definition/enums.d.ts +0 -1
  119. package/dist/zspec/zcl/definition/enums.d.ts.map +1 -1
  120. package/dist/zspec/zcl/definition/enums.js +0 -1
  121. package/dist/zspec/zcl/definition/enums.js.map +1 -1
  122. package/dist/zspec/zcl/definition/foundation.d.ts +306 -7
  123. package/dist/zspec/zcl/definition/foundation.d.ts.map +1 -1
  124. package/dist/zspec/zcl/definition/foundation.js +552 -207
  125. package/dist/zspec/zcl/definition/foundation.js.map +1 -1
  126. package/dist/zspec/zcl/definition/status.d.ts +21 -10
  127. package/dist/zspec/zcl/definition/status.d.ts.map +1 -1
  128. package/dist/zspec/zcl/definition/status.js +11 -0
  129. package/dist/zspec/zcl/definition/status.js.map +1 -1
  130. package/dist/zspec/zcl/definition/tstype.d.ts +57 -48
  131. package/dist/zspec/zcl/definition/tstype.d.ts.map +1 -1
  132. package/dist/zspec/zcl/utils.d.ts +7 -4
  133. package/dist/zspec/zcl/utils.d.ts.map +1 -1
  134. package/dist/zspec/zcl/utils.js +133 -240
  135. package/dist/zspec/zcl/utils.js.map +1 -1
  136. package/dist/zspec/zcl/zclFrame.d.ts +4 -4
  137. package/dist/zspec/zcl/zclFrame.d.ts.map +1 -1
  138. package/dist/zspec/zcl/zclFrame.js +19 -103
  139. package/dist/zspec/zcl/zclFrame.js.map +1 -1
  140. package/dist/zspec/zcl/zclStatusError.d.ts +1 -1
  141. package/dist/zspec/zcl/zclStatusError.d.ts.map +1 -1
  142. package/dist/zspec/zcl/zclStatusError.js +2 -2
  143. package/dist/zspec/zcl/zclStatusError.js.map +1 -1
  144. package/package.json +1 -1
  145. package/scripts/clusters-typegen.ts +44 -139
  146. package/src/adapter/adapter.ts +38 -3
  147. package/src/adapter/adapterDiscovery.ts +2 -1
  148. package/src/adapter/deconz/adapter/deconzAdapter.ts +24 -51
  149. package/src/adapter/deconz/driver/constants.ts +1 -1
  150. package/src/adapter/ember/adapter/emberAdapter.ts +23 -10
  151. package/src/adapter/ember/adapter/oneWaitress.ts +16 -6
  152. package/src/adapter/ezsp/adapter/ezspAdapter.ts +27 -48
  153. package/src/adapter/ezsp/driver/index.ts +1 -1
  154. package/src/adapter/ezsp/driver/types/index.ts +99 -99
  155. package/src/adapter/serialPort.ts +9 -0
  156. package/src/adapter/z-stack/adapter/adapter-backup.ts +1 -1
  157. package/src/adapter/z-stack/adapter/adapter-nv-memory.ts +1 -1
  158. package/src/adapter/z-stack/adapter/manager.ts +16 -2
  159. package/src/adapter/z-stack/adapter/tstype.ts +1 -0
  160. package/src/adapter/z-stack/adapter/zStackAdapter.ts +34 -81
  161. package/src/adapter/z-stack/constants/index.ts +1 -1
  162. package/src/adapter/z-stack/unpi/constants.ts +1 -1
  163. package/src/adapter/zboss/adapter/zbossAdapter.ts +23 -54
  164. package/src/adapter/zboss/driver.ts +8 -1
  165. package/src/adapter/zboss/uart.ts +14 -1
  166. package/src/adapter/zigate/adapter/zigateAdapter.ts +17 -48
  167. package/src/adapter/zoh/adapter/zohAdapter.ts +27 -50
  168. package/src/controller/controller.ts +12 -2
  169. package/src/controller/greenPower.ts +16 -9
  170. package/src/controller/helpers/ota.ts +37 -11
  171. package/src/controller/helpers/zclFrameConverter.ts +20 -17
  172. package/src/controller/model/device.ts +192 -79
  173. package/src/controller/model/endpoint.ts +36 -24
  174. package/src/controller/model/group.ts +4 -4
  175. package/src/controller/touchlink.ts +3 -3
  176. package/src/utils/timeService.ts +2 -2
  177. package/src/zspec/zcl/buffaloZcl.ts +226 -100
  178. package/src/zspec/zcl/definition/cluster.ts +2713 -2822
  179. package/src/zspec/zcl/definition/clusters-types.ts +80 -1135
  180. package/src/zspec/zcl/definition/enums.ts +0 -1
  181. package/src/zspec/zcl/definition/foundation.ts +703 -216
  182. package/src/zspec/zcl/definition/status.ts +22 -11
  183. package/src/zspec/zcl/definition/tstype.ts +59 -58
  184. package/src/zspec/zcl/utils.ts +137 -264
  185. package/src/zspec/zcl/zclFrame.ts +25 -130
  186. package/src/zspec/zcl/zclStatusError.ts +2 -2
  187. package/test/adapter/ember/emberAdapter.test.ts +191 -4
  188. package/test/adapter/ezsp/uart.test.ts +10 -10
  189. package/test/adapter/z-stack/adapter.test.ts +88 -32
  190. package/test/adapter/zoh/zohAdapter.test.ts +4 -4
  191. package/test/controller.test.ts +822 -248
  192. package/test/device-ota.test.ts +141 -16
  193. package/test/device.test.ts +731 -0
  194. package/test/requests.bench.ts +2 -0
  195. package/test/zcl.test.ts +70 -95
  196. package/test/zspec/zcl/buffalo.test.ts +251 -11
  197. package/test/zspec/zcl/foundation.test.ts +990 -0
  198. package/test/zspec/zcl/frame.test.ts +84 -69
  199. package/test/zspec/zcl/utils.test.ts +105 -81
  200. package/tsconfig.json +0 -1
  201. package/scripts/check-clusters-changes.ts +0 -328
  202. package/scripts/clusters-changes.log +0 -584
  203. package/scripts/utils.ts +0 -88
  204. package/scripts/zap-update-clusters-report.json +0 -303
  205. package/scripts/zap-update-clusters.ts +0 -1520
  206. package/scripts/zap-update-types.ts +0 -707
  207. package/scripts/zap-xml-clusters-overrides-data.ts +0 -52
  208. package/scripts/zap-xml-clusters-overrides.ts +0 -400
  209. package/scripts/zap-xml-types.ts +0 -146
@@ -1,72 +1,10 @@
1
1
  import {Clusters} from "./definition/cluster";
2
2
  import {ZCL_TYPE_INVALID_BY_TYPE} from "./definition/datatypes";
3
- import {DataType, DataTypeClass} from "./definition/enums";
3
+ import {DataType} from "./definition/enums";
4
4
  import {Foundation, type FoundationCommandName, type FoundationDefinition} from "./definition/foundation";
5
- import type {Attribute, Cluster, ClusterDefinition, ClusterName, Command, CustomClusters, Parameter} from "./definition/tstype";
6
-
7
- const DATA_TYPE_CLASS_DISCRETE = [
8
- DataType.DATA8,
9
- DataType.DATA16,
10
- DataType.DATA24,
11
- DataType.DATA32,
12
- DataType.DATA40,
13
- DataType.DATA48,
14
- DataType.DATA56,
15
- DataType.DATA64,
16
- DataType.BOOLEAN,
17
- DataType.BITMAP8,
18
- DataType.BITMAP16,
19
- DataType.BITMAP24,
20
- DataType.BITMAP32,
21
- DataType.BITMAP40,
22
- DataType.BITMAP48,
23
- DataType.BITMAP56,
24
- DataType.BITMAP64,
25
- DataType.ENUM8,
26
- DataType.ENUM16,
27
- DataType.OCTET_STR,
28
- DataType.CHAR_STR,
29
- DataType.LONG_OCTET_STR,
30
- DataType.LONG_CHAR_STR,
31
- DataType.ARRAY,
32
- DataType.STRUCT,
33
- DataType.SET,
34
- DataType.BAG,
35
- DataType.CLUSTER_ID,
36
- DataType.ATTR_ID,
37
- DataType.BAC_OID,
38
- DataType.IEEE_ADDR,
39
- DataType.SEC_KEY,
40
- ];
41
- const DATA_TYPE_CLASS_ANALOG = [
42
- DataType.UINT8,
43
- DataType.UINT16,
44
- DataType.UINT24,
45
- DataType.UINT32,
46
- DataType.UINT40,
47
- DataType.UINT48,
48
- DataType.UINT56,
49
- DataType.INT8,
50
- DataType.INT16,
51
- DataType.INT24,
52
- DataType.INT32,
53
- DataType.INT40,
54
- DataType.INT48,
55
- DataType.INT56,
56
- DataType.SEMI_PREC,
57
- DataType.SINGLE_PREC,
58
- DataType.DOUBLE_PREC,
59
- DataType.TOD,
60
- DataType.DATE,
61
- DataType.UTC,
62
- ];
63
-
64
- const FOUNDATION_DISCOVER_RSP_IDS = [
65
- Foundation.discoverRsp.ID,
66
- Foundation.discoverCommandsRsp.ID,
67
- Foundation.discoverCommandsGenRsp.ID,
68
- Foundation.discoverExtRsp.ID,
69
- ];
5
+ import {Status} from "./definition/status";
6
+ import type {Attribute, Cluster, ClusterName, Command, CustomClusters, Parameter} from "./definition/tstype";
7
+ import {ZclStatusError} from "./zclStatusError";
70
8
 
71
9
  /** Runtime fast lookup */
72
10
  const ZCL_CLUSTERS_ID_TO_NAMES = (() => {
@@ -81,227 +19,166 @@ const ZCL_CLUSTERS_ID_TO_NAMES = (() => {
81
19
  return map;
82
20
  })();
83
21
 
84
- export function getDataTypeClass(dataType: DataType): DataTypeClass {
85
- if (DATA_TYPE_CLASS_DISCRETE.includes(dataType)) {
86
- return DataTypeClass.DISCRETE;
87
- }
88
-
89
- if (DATA_TYPE_CLASS_ANALOG.includes(dataType)) {
90
- return DataTypeClass.ANALOG;
91
- }
92
-
93
- throw new Error(`Don't know value type for '${DataType[dataType]}'`);
22
+ export function isAnalogDataType(dataType: DataType): boolean {
23
+ return (
24
+ dataType === DataType.UINT8 ||
25
+ dataType === DataType.UINT16 ||
26
+ dataType === DataType.UINT24 ||
27
+ dataType === DataType.UINT32 ||
28
+ dataType === DataType.UINT40 ||
29
+ dataType === DataType.UINT48 ||
30
+ dataType === DataType.UINT56 ||
31
+ dataType === DataType.INT8 ||
32
+ dataType === DataType.INT16 ||
33
+ dataType === DataType.INT24 ||
34
+ dataType === DataType.INT32 ||
35
+ dataType === DataType.INT40 ||
36
+ dataType === DataType.INT48 ||
37
+ dataType === DataType.INT56 ||
38
+ dataType === DataType.SEMI_PREC ||
39
+ dataType === DataType.SINGLE_PREC ||
40
+ dataType === DataType.DOUBLE_PREC ||
41
+ dataType === DataType.TOD ||
42
+ dataType === DataType.DATE ||
43
+ dataType === DataType.UTC
44
+ );
94
45
  }
95
46
 
96
- function hasCustomClusters(customClusters: CustomClusters): boolean {
97
- // XXX: was there a good reason to not set the parameter `customClusters` optional? it would allow simple undefined check
98
- // below is twice faster than checking `Object.keys(customClusters).length`
99
- for (const _k in customClusters) return true;
100
- return false;
101
- }
102
-
103
- function findClusterNameByID(
104
- id: number,
105
- manufacturerCode: number | undefined,
106
- clusters: typeof Clusters | CustomClusters,
107
- zcl: boolean,
108
- ): [name: string | undefined, partialMatch: boolean] {
109
- let name: string | undefined;
110
- // if manufacturer code is given, consider partial match if didn't match against manufacturer code
111
- let partialMatch = Boolean(manufacturerCode);
112
-
113
- if (zcl) {
114
- const zclName = ZCL_CLUSTERS_ID_TO_NAMES.get(id);
115
-
116
- if (zclName) {
117
- const cluster = clusters[zclName];
118
-
119
- // priority on first match when matching only ID
120
- if (name === undefined) {
121
- name = zclName;
122
- }
47
+ export function getCluster(key: string | number, manufacturerCode: number | undefined = undefined, customClusters: CustomClusters = {}): Cluster {
48
+ let cluster: Cluster | undefined;
123
49
 
124
- if (manufacturerCode && cluster.manufacturerCode === manufacturerCode) {
125
- name = zclName;
126
- partialMatch = false;
127
- } else if (!cluster.manufacturerCode) {
128
- name = zclName;
129
- }
130
- }
131
- } else {
132
- for (const clusterName in clusters) {
133
- const cluster = clusters[clusterName as ClusterName];
50
+ if (typeof key === "number") {
51
+ // custom clusters have priority over Zcl clusters, except in case of better match (see below)
52
+ for (const clusterName in customClusters) {
53
+ const foundCluster = customClusters[clusterName as ClusterName];
134
54
 
135
- if (cluster.ID === id) {
55
+ if (foundCluster.ID === key) {
136
56
  // priority on first match when matching only ID
137
- if (name === undefined) {
138
- name = clusterName;
57
+ if (cluster === undefined) {
58
+ cluster = foundCluster;
139
59
  }
140
60
 
141
- if (manufacturerCode && cluster.manufacturerCode === manufacturerCode) {
142
- name = clusterName;
143
- partialMatch = false;
61
+ if (manufacturerCode && foundCluster.manufacturerCode === manufacturerCode) {
62
+ cluster = foundCluster;
144
63
  break;
145
64
  }
146
65
 
147
- if (!cluster.manufacturerCode) {
148
- name = clusterName;
66
+ if (!foundCluster.manufacturerCode) {
67
+ cluster = foundCluster;
149
68
  break;
150
69
  }
151
70
  }
152
71
  }
153
- }
154
72
 
155
- return [name, partialMatch];
156
- }
157
-
158
- function getClusterDefinition(
159
- key: string | number,
160
- manufacturerCode: number | undefined,
161
- customClusters: CustomClusters,
162
- ): {name: string; cluster: ClusterDefinition} {
163
- let name: string | undefined;
73
+ if (!cluster) {
74
+ const zclName = ZCL_CLUSTERS_ID_TO_NAMES.get(key);
164
75
 
165
- if (typeof key === "number") {
166
- let partialMatch: boolean;
76
+ if (zclName) {
77
+ const foundCluster = Clusters[zclName];
167
78
 
168
- // custom clusters have priority over Zcl clusters, except in case of better match (see below)
169
- [name, partialMatch] = findClusterNameByID(key, manufacturerCode, customClusters, false);
79
+ // TODO: can remove all below once all manuf-specific moved to ZHC
170
80
 
171
- if (!name) {
172
- [name, partialMatch] = findClusterNameByID(key, manufacturerCode, Clusters, true);
173
- } else if (partialMatch) {
174
- let zclName: string | undefined;
175
- [zclName, partialMatch] = findClusterNameByID(key, manufacturerCode, Clusters, true);
81
+ // priority on first match when matching only ID
82
+ if (cluster === undefined) {
83
+ cluster = foundCluster;
84
+ }
176
85
 
177
- // Zcl clusters contain a better match, use that one
178
- if (zclName !== undefined && !partialMatch) {
179
- name = zclName;
86
+ if (manufacturerCode && foundCluster.manufacturerCode === manufacturerCode) {
87
+ cluster = foundCluster;
88
+ } else if (foundCluster.manufacturerCode === undefined) {
89
+ cluster = foundCluster;
90
+ }
180
91
  }
181
92
  }
182
- } else {
183
- name = key;
184
- }
185
93
 
186
- let cluster =
187
- name !== undefined && hasCustomClusters(customClusters)
188
- ? {
189
- ...Clusters[name as ClusterName],
190
- ...customClusters[name], // should override Zcl clusters
191
- }
192
- : Clusters[name as ClusterName];
193
-
194
- if (!cluster || cluster.ID === undefined) {
195
- if (typeof key === "number") {
196
- name = key.toString();
197
- cluster = {attributes: {}, commands: {}, commandsResponse: {}, manufacturerCode: undefined, ID: key};
198
- } else {
199
- name = undefined;
94
+ // TODO: cluster.ID can't be undefined?
95
+ if (!cluster || cluster.ID === undefined) {
96
+ cluster = {name: `${key}`, ID: key, attributes: {}, commands: {}, commandsResponse: {}};
97
+ // XXX: align behavior with string key?
98
+ // throw new ZclStatusError(Status.UNSUPPORTED_CLUSTER, `${key}`);
200
99
  }
201
- }
202
-
203
- if (!name) {
204
- throw new Error(`Cluster with name '${key}' does not exist`);
205
- }
206
-
207
- return {name, cluster};
208
- }
209
-
210
- function cloneClusterEntriesWithName<T extends Record<string, unknown>>(entries: Record<string, T>): Record<string, {name: string} & T> {
211
- const clone: Record<string, {name: string} & T> = {};
100
+ } else {
101
+ cluster = key in customClusters ? customClusters[key] : Clusters[key as ClusterName];
212
102
 
213
- for (const key in entries) {
214
- clone[key] = {...entries[key], name: key};
103
+ // TODO: cluster.ID can't be undefined?
104
+ if (!cluster || cluster.ID === undefined) {
105
+ throw new ZclStatusError(Status.UNSUPPORTED_CLUSTER, key);
106
+ }
215
107
  }
216
108
 
217
- return clone;
109
+ return cluster;
218
110
  }
219
111
 
220
- function createCluster(name: string, cluster: ClusterDefinition, manufacturerCode?: number): Cluster {
221
- const attributes: Record<string, Attribute> = cloneClusterEntriesWithName(cluster.attributes);
222
- const commands: Record<string, Command> = cloneClusterEntriesWithName(cluster.commands);
223
- const commandsResponse: Record<string, Command> = cloneClusterEntriesWithName(cluster.commandsResponse);
112
+ export function getClusterAttribute(cluster: Cluster, key: number | string, manufacturerCode: number | undefined): Attribute | undefined {
113
+ const attributes = cluster.attributes;
224
114
 
225
- const getAttribute = (key: number | string): Attribute | undefined => {
226
- if (typeof key === "number") {
227
- let partialMatchAttr: Attribute | undefined;
115
+ if (typeof key === "number") {
116
+ let partialMatchAttr: Attribute | undefined;
228
117
 
229
- for (const attrKey in attributes) {
230
- const attr = attributes[attrKey];
118
+ for (const attrKey in attributes) {
119
+ const attr = attributes[attrKey];
231
120
 
232
- if (attr.ID === key) {
233
- if (manufacturerCode && attr.manufacturerCode === manufacturerCode) {
234
- return attr;
235
- }
121
+ if (attr.ID === key) {
122
+ if (manufacturerCode !== undefined && attr.manufacturerCode === manufacturerCode) {
123
+ return attr;
124
+ }
236
125
 
237
- if (attr.manufacturerCode === undefined) {
238
- partialMatchAttr = attr;
239
- }
126
+ if (attr.manufacturerCode === undefined) {
127
+ partialMatchAttr = attr;
240
128
  }
241
129
  }
242
-
243
- return partialMatchAttr;
244
130
  }
245
131
 
246
- return attributes[key];
247
- };
132
+ return partialMatchAttr;
133
+ }
248
134
 
249
- const getCommand = (key: number | string): Command => {
250
- if (typeof key === "number") {
251
- for (const cmdKey in commands) {
252
- const cmd = commands[cmdKey];
135
+ return attributes[key];
136
+ // XXX: align behavior with cmds?
137
+ // throw new ZclStatusError(Status.UNSUPPORTED_ATTRIBUTE, `${cluster.name}:${key}`);
138
+ }
253
139
 
254
- if (cmd.ID === key) {
255
- return cmd;
256
- }
257
- }
258
- } else {
259
- const cmd = commands[key];
140
+ export function getClusterCommand(cluster: Cluster, key: number | string): Command {
141
+ const commands = cluster.commands;
142
+
143
+ if (typeof key === "number") {
144
+ for (const cmdKey in commands) {
145
+ const cmd = commands[cmdKey];
260
146
 
261
- if (cmd) {
147
+ if (cmd.ID === key) {
262
148
  return cmd;
263
149
  }
264
150
  }
151
+ } else {
152
+ const cmd = commands[key];
153
+
154
+ if (cmd) {
155
+ return cmd;
156
+ }
157
+ }
265
158
 
266
- throw new Error(`Cluster '${name}' has no command '${key}'`);
267
- };
159
+ throw new ZclStatusError(Status.UNSUP_COMMAND, `${cluster.name}:${key}`);
160
+ }
268
161
 
269
- const getCommandResponse = (key: number | string): Command => {
270
- if (typeof key === "number") {
271
- for (const cmdKey in commandsResponse) {
272
- const cmd = commandsResponse[cmdKey];
162
+ export function getClusterCommandResponse(cluster: Cluster, key: number | string): Command {
163
+ const commandResponses = cluster.commandsResponse;
273
164
 
274
- if (cmd.ID === key) {
275
- return cmd;
276
- }
277
- }
278
- } else {
279
- const cmd = commandsResponse[key];
165
+ if (typeof key === "number") {
166
+ for (const cmdKey in commandResponses) {
167
+ const cmd = commandResponses[cmdKey];
280
168
 
281
- if (cmd) {
169
+ if (cmd.ID === key) {
282
170
  return cmd;
283
171
  }
284
172
  }
173
+ } else {
174
+ const cmd = commandResponses[key];
285
175
 
286
- throw new Error(`Cluster '${name}' has no command response '${key}'`);
287
- };
288
-
289
- return {
290
- ID: cluster.ID,
291
- attributes,
292
- manufacturerCode: cluster.manufacturerCode,
293
- name,
294
- commands,
295
- commandsResponse,
296
- getAttribute,
297
- getCommand,
298
- getCommandResponse,
299
- };
300
- }
176
+ if (cmd) {
177
+ return cmd;
178
+ }
179
+ }
301
180
 
302
- export function getCluster(key: string | number, manufacturerCode: number | undefined = undefined, customClusters: CustomClusters = {}): Cluster {
303
- const {name, cluster} = getClusterDefinition(key, manufacturerCode, customClusters);
304
- return createCluster(name, cluster, manufacturerCode);
181
+ throw new ZclStatusError(Status.UNSUP_COMMAND, `response ${cluster.name}:${key}`);
305
182
  }
306
183
 
307
184
  function getGlobalCommandNameById(id: number): FoundationCommandName {
@@ -311,28 +188,18 @@ function getGlobalCommandNameById(id: number): FoundationCommandName {
311
188
  }
312
189
  }
313
190
 
314
- throw new Error(`Global command with id '${id}' does not exist.`);
191
+ throw new ZclStatusError(Status.UNSUP_COMMAND, `foundation:${id}`);
315
192
  }
316
193
 
317
- export function getGlobalCommand(key: number | string): Command {
194
+ export function getGlobalCommand(key: number | string): FoundationDefinition {
318
195
  const name = typeof key === "number" ? getGlobalCommandNameById(key) : (key as FoundationCommandName);
319
196
  const command = Foundation[name];
320
197
 
321
198
  if (!command) {
322
- throw new Error(`Global command with key '${key}' does not exist`);
323
- }
324
-
325
- const result: Command = {
326
- ID: command.ID,
327
- name,
328
- parameters: command.parameters,
329
- };
330
-
331
- if (command.response !== undefined) {
332
- result.response = command.response;
199
+ throw new ZclStatusError(Status.UNSUP_COMMAND, `foundation:${key}`);
333
200
  }
334
201
 
335
- return result;
202
+ return command;
336
203
  }
337
204
 
338
205
  export function isClusterName(name: string): name is ClusterName {
@@ -348,11 +215,17 @@ export function getFoundationCommand(id: number): FoundationDefinition {
348
215
  }
349
216
  }
350
217
 
351
- throw new Error(`Foundation command '${id}' does not exist.`);
218
+ throw new ZclStatusError(Status.UNSUP_COMMAND, `foundation:${id}`);
352
219
  }
353
220
 
354
- export function isFoundationDiscoverRsp(id: number): boolean {
355
- return FOUNDATION_DISCOVER_RSP_IDS.includes(id);
221
+ export function getFoundationCommandByName(name: string): FoundationDefinition {
222
+ const command = Foundation[name as FoundationCommandName];
223
+
224
+ if (command === undefined) {
225
+ throw new ZclStatusError(Status.UNSUP_COMMAND, `foundation:${name}`);
226
+ }
227
+
228
+ return command;
356
229
  }
357
230
 
358
231
  /** Check if value is equal to either min, max, minRef or maxRef */
@@ -366,37 +239,37 @@ function isMinOrMax<T>(entry: Attribute | Parameter, value: T): boolean {
366
239
 
367
240
  function processRestrictions<T>(entry: Attribute | Parameter, value: T): void {
368
241
  if (entry.min !== undefined && (value as number) < entry.min) {
369
- throw new Error(`${entry.name} requires min of ${entry.min}`);
242
+ throw new ZclStatusError(Status.INVALID_VALUE, `${entry.name} requires min of ${entry.min}`);
370
243
  }
371
244
 
372
245
  if (entry.minExcl !== undefined && (value as number) <= entry.minExcl) {
373
- throw new Error(`${entry.name} requires min exclusive of ${entry.minExcl}`);
246
+ throw new ZclStatusError(Status.INVALID_VALUE, `${entry.name} requires min exclusive of ${entry.minExcl}`);
374
247
  }
375
248
 
376
249
  if (entry.max !== undefined && (value as number) > entry.max) {
377
- throw new Error(`${entry.name} requires max of ${entry.max}`);
250
+ throw new ZclStatusError(Status.INVALID_VALUE, `${entry.name} requires max of ${entry.max}`);
378
251
  }
379
252
 
380
253
  if (entry.maxExcl !== undefined && (value as number) >= entry.maxExcl) {
381
- throw new Error(`${entry.name} requires max exclusive of ${entry.maxExcl}`);
254
+ throw new ZclStatusError(Status.INVALID_VALUE, `${entry.name} requires max exclusive of ${entry.maxExcl}`);
382
255
  }
383
256
 
384
257
  if (entry.length !== undefined && (value as string | unknown[] | Buffer).length !== entry.length) {
385
- throw new Error(`${entry.name} requires length of ${entry.length}`);
258
+ throw new ZclStatusError(Status.INVALID_VALUE, `${entry.name} requires length of ${entry.length}`);
386
259
  }
387
260
 
388
261
  if (entry.minLen !== undefined && (value as string | unknown[] | Buffer).length < entry.minLen) {
389
- throw new Error(`${entry.name} requires min length of ${entry.minLen}`);
262
+ throw new ZclStatusError(Status.INVALID_VALUE, `${entry.name} requires min length of ${entry.minLen}`);
390
263
  }
391
264
 
392
265
  if (entry.maxLen !== undefined && (value as string | unknown[] | Buffer).length > entry.maxLen) {
393
- throw new Error(`${entry.name} requires max length of ${entry.maxLen}`);
266
+ throw new ZclStatusError(Status.INVALID_VALUE, `${entry.name} requires max length of ${entry.maxLen}`);
394
267
  }
395
268
  }
396
269
 
397
270
  export function processAttributeWrite<T>(attribute: Attribute, value: T): T {
398
271
  if (attribute.write !== true) {
399
- throw new Error(`Attribute ${attribute.name} (${attribute.ID}) is not writable`);
272
+ throw new ZclStatusError(Status.NOT_AUTHORIZED, `${attribute.name} (${attribute.ID}) is not writable`);
400
273
  }
401
274
 
402
275
  if (value == null) {
@@ -413,7 +286,7 @@ export function processAttributeWrite<T>(attribute: Attribute, value: T): T {
413
286
  const nonValue = ZCL_TYPE_INVALID_BY_TYPE[attribute.type];
414
287
 
415
288
  if (nonValue === undefined) {
416
- throw new Error(`Attribute ${attribute.name} (${attribute.ID}) does not have a default nor a non-value`);
289
+ throw new ZclStatusError(Status.INVALID_FIELD, `${attribute.name} (${attribute.ID}) does not have a default nor a non-value`);
417
290
  }
418
291
 
419
292
  return nonValue as T;
@@ -429,7 +302,7 @@ export function processAttributeWrite<T>(attribute: Attribute, value: T): T {
429
302
 
430
303
  export function processAttributePreRead(attribute: Attribute): void {
431
304
  if (attribute.read === false) {
432
- throw new Error(`Attribute ${attribute.name} (${attribute.ID}) is not readable`);
305
+ throw new ZclStatusError(Status.NOT_AUTHORIZED, `${attribute.name} (${attribute.ID}) is not readable`);
433
306
  }
434
307
  }
435
308
 
@@ -470,7 +343,7 @@ export function processParameterWrite<T>(parameter: Parameter, value: T): T {
470
343
  const nonValue = ZCL_TYPE_INVALID_BY_TYPE[parameter.type];
471
344
 
472
345
  if (nonValue === undefined) {
473
- throw new Error(`Parameter ${parameter.name} does not have a non-value`);
346
+ throw new ZclStatusError(Status.INVALID_FIELD, `${parameter.name} does not have a non-value`);
474
347
  }
475
348
 
476
349
  return nonValue as T;