@willieee802/zigbee-herdsman 0.48.3 → 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 (1045) hide show
  1. package/.github/ISSUE_TEMPLATE/config.yml +5 -0
  2. package/.github/copilot-instructions.md +689 -0
  3. package/.github/dependabot.yml +22 -0
  4. package/.github/prompts/copilot-instructions-blueprint-generator.prompt.md +294 -0
  5. package/.github/prompts/create-agentsmd.prompt.md +249 -0
  6. package/.github/prompts/create-specification.prompt.md +127 -0
  7. package/.github/prompts/review-and-refactor.prompt.md +15 -0
  8. package/.github/prompts/update-specification.prompt.md +127 -0
  9. package/.github/workflows/ci.yml +70 -0
  10. package/.github/workflows/release-please.yml +18 -0
  11. package/.github/workflows/stale.yml +20 -0
  12. package/.github/workflows/typedoc.yaml +47 -0
  13. package/.release-please-manifest.json +2 -2
  14. package/.vscode/extensions.json +3 -0
  15. package/.vscode/settings.json +11 -0
  16. package/AGENTS.md +441 -0
  17. package/CHANGELOG.md +1283 -0
  18. package/README.md +1 -1
  19. package/biome.json +103 -0
  20. package/dist/adapter/adapter.d.ts.map +1 -1
  21. package/dist/adapter/adapterDiscovery.d.ts.map +1 -0
  22. package/dist/adapter/const.d.ts.map +1 -0
  23. package/dist/adapter/deconz/adapter/deconzAdapter.d.ts.map +1 -1
  24. package/dist/adapter/ember/adapter/emberAdapter.d.ts.map +1 -1
  25. package/dist/adapter/ember/adapter/endpoints.d.ts.map +1 -1
  26. package/dist/adapter/ember/adapter/oneWaitress.d.ts.map +1 -1
  27. package/dist/adapter/ember/adapter/tokensManager.d.ts.map +1 -1
  28. package/dist/adapter/ember/ezsp/ezsp.d.ts.map +1 -1
  29. package/dist/adapter/ember/utils/initters.d.ts.map +1 -1
  30. package/dist/adapter/events.d.ts.map +1 -1
  31. package/dist/adapter/ezsp/adapter/backup.d.ts.map +1 -1
  32. package/dist/adapter/ezsp/adapter/ezspAdapter.d.ts.map +1 -1
  33. package/dist/adapter/ezsp/driver/driver.d.ts.map +1 -1
  34. package/dist/adapter/ezsp/driver/index.d.ts.map +1 -1
  35. package/dist/adapter/ezsp/driver/multicast.d.ts.map +1 -1
  36. package/dist/adapter/index.d.ts.map +1 -1
  37. package/dist/adapter/z-stack/adapter/endpoints.d.ts.map +1 -1
  38. package/dist/adapter/z-stack/adapter/manager.d.ts.map +1 -1
  39. package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts.map +1 -1
  40. package/dist/adapter/z-stack/models/startup-options.d.ts.map +1 -1
  41. package/dist/adapter/zboss/adapter/zbossAdapter.d.ts.map +1 -0
  42. package/dist/adapter/zboss/driver.d.ts.map +1 -0
  43. package/dist/adapter/zboss/frame.d.ts.map +1 -0
  44. package/dist/adapter/zboss/frame.js +200 -0
  45. package/dist/adapter/zboss/frame.js.map +1 -0
  46. package/dist/adapter/zboss/uart.d.ts.map +1 -0
  47. package/dist/adapter/zigate/adapter/zigateAdapter.d.ts.map +1 -1
  48. package/dist/adapter/zigate/driver/buffaloZiGate.d.ts.map +1 -1
  49. package/dist/adapter/zigate/driver/buffaloZiGate.js +70 -62
  50. package/dist/adapter/zigate/driver/buffaloZiGate.js.map +1 -1
  51. package/dist/adapter/zigate/driver/ziGateObject.d.ts.map +1 -1
  52. package/dist/adapter/zigate/driver/zigate.d.ts.map +1 -1
  53. package/dist/adapter/zoh/adapter/zohAdapter.d.ts.map +1 -0
  54. package/dist/controller/controller.d.ts.map +1 -1
  55. package/dist/controller/controller.js +524 -350
  56. package/dist/controller/controller.js.map +1 -1
  57. package/dist/controller/database.d.ts.map +1 -1
  58. package/dist/controller/events.d.ts.map +1 -1
  59. package/dist/controller/events.js +0 -110
  60. package/dist/controller/events.js.map +1 -1
  61. package/dist/controller/greenPower.d.ts.map +1 -1
  62. package/dist/controller/greenPower.js +330 -121
  63. package/dist/controller/greenPower.js.map +1 -1
  64. package/dist/controller/helpers/index.d.ts.map +1 -1
  65. package/dist/controller/helpers/ota.d.ts.map +1 -0
  66. package/dist/controller/helpers/ota.js +467 -0
  67. package/dist/controller/helpers/ota.js.map +1 -0
  68. package/dist/controller/helpers/request.d.ts.map +1 -1
  69. package/dist/controller/helpers/requestQueue.d.ts.map +1 -1
  70. package/dist/controller/helpers/zclFrameConverter.d.ts.map +1 -1
  71. package/dist/controller/helpers/zclFrameConverter.js +36 -22
  72. package/dist/controller/helpers/zclFrameConverter.js.map +1 -1
  73. package/dist/controller/index.d.ts.map +1 -1
  74. package/dist/controller/model/device.d.ts.map +1 -1
  75. package/dist/controller/model/device.js +1052 -402
  76. package/dist/controller/model/device.js.map +1 -1
  77. package/dist/controller/model/endpoint.d.ts.map +1 -1
  78. package/dist/controller/model/endpoint.js +447 -292
  79. package/dist/controller/model/endpoint.js.map +1 -1
  80. package/dist/controller/model/entity.d.ts.map +1 -1
  81. package/dist/controller/model/group.d.ts.map +1 -1
  82. package/dist/controller/model/group.js +202 -85
  83. package/dist/controller/model/group.js.map +1 -1
  84. package/dist/controller/model/index.d.ts.map +1 -1
  85. package/dist/controller/model/zigbeeEntity.d.ts.map +1 -0
  86. package/dist/controller/touchlink.d.ts.map +1 -1
  87. package/dist/controller/tstype.d.ts.map +1 -1
  88. package/dist/controller/tstype.js +0 -6
  89. package/dist/controller/tstype.js.map +1 -1
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/utils/timeService.d.ts.map +1 -0
  92. package/dist/utils/timeService.js +127 -0
  93. package/dist/utils/timeService.js.map +1 -0
  94. package/dist/zspec/zcl/buffaloZcl.d.ts.map +1 -1
  95. package/dist/zspec/zcl/buffaloZcl.js +327 -287
  96. package/dist/zspec/zcl/buffaloZcl.js.map +1 -1
  97. package/dist/zspec/zcl/definition/cluster.d.ts.map +1 -1
  98. package/dist/zspec/zcl/definition/cluster.js +6050 -4043
  99. package/dist/zspec/zcl/definition/cluster.js.map +1 -1
  100. package/dist/zspec/zcl/definition/clusters-types.d.ts +8135 -0
  101. package/dist/zspec/zcl/definition/clusters-types.d.ts.map +1 -0
  102. package/dist/zspec/zcl/definition/clusters-types.js +3 -0
  103. package/dist/zspec/zcl/definition/clusters-types.js.map +1 -0
  104. package/dist/zspec/zcl/definition/foundation.d.ts.map +1 -1
  105. package/dist/zspec/zcl/definition/foundation.js +170 -99
  106. package/dist/zspec/zcl/definition/foundation.js.map +1 -1
  107. package/dist/zspec/zcl/definition/tstype.d.ts +190 -31
  108. package/dist/zspec/zcl/definition/tstype.d.ts.map +1 -1
  109. package/dist/zspec/zcl/definition/tstype.js +0 -1
  110. package/dist/zspec/zcl/definition/tstype.js.map +1 -1
  111. package/dist/zspec/zcl/index.d.ts.map +1 -1
  112. package/dist/zspec/zcl/index.js +23 -13
  113. package/dist/zspec/zcl/index.js.map +1 -1
  114. package/dist/zspec/zcl/utils.d.ts.map +1 -1
  115. package/dist/zspec/zcl/utils.js +288 -103
  116. package/dist/zspec/zcl/utils.js.map +1 -1
  117. package/dist/zspec/zcl/zclFrame.d.ts.map +1 -1
  118. package/dist/zspec/zcl/zclFrame.js +121 -97
  119. package/dist/zspec/zcl/zclFrame.js.map +1 -1
  120. package/dist/zspec/zcl/zclHeader.d.ts.map +1 -1
  121. package/dist/zspec/zcl/zclHeader.js +13 -13
  122. package/dist/zspec/zcl/zclHeader.js.map +1 -1
  123. package/examples/join-and-log.js +18 -18
  124. package/package.json +23 -3
  125. package/release-please-config.json +8 -8
  126. package/scripts/check-clusters-changes.ts +328 -0
  127. package/scripts/clusters-changes.log +584 -0
  128. package/scripts/clusters-typegen.ts +608 -0
  129. package/scripts/utils.ts +88 -0
  130. package/scripts/zap-update-clusters-report.json +303 -0
  131. package/scripts/zap-update-clusters.ts +1520 -0
  132. package/scripts/zap-update-types.ts +707 -0
  133. package/scripts/zap-xml-clusters-overrides-data.ts +52 -0
  134. package/scripts/zap-xml-clusters-overrides.ts +400 -0
  135. package/scripts/zap-xml-types.ts +146 -0
  136. package/src/adapter/adapter.ts +210 -0
  137. package/src/adapter/adapterDiscovery.ts +736 -0
  138. package/src/adapter/const.ts +12 -0
  139. package/src/adapter/deconz/adapter/deconzAdapter.ts +888 -0
  140. package/src/adapter/deconz/driver/constants.ts +246 -0
  141. package/src/adapter/deconz/driver/driver.ts +1528 -0
  142. package/src/adapter/deconz/driver/frame.ts +11 -0
  143. package/src/adapter/deconz/driver/frameParser.ts +766 -0
  144. package/src/adapter/deconz/driver/parser.ts +45 -0
  145. package/src/adapter/deconz/driver/writer.ts +22 -0
  146. package/src/adapter/deconz/types.d.ts +13 -0
  147. package/src/adapter/ember/adapter/emberAdapter.ts +2262 -0
  148. package/src/adapter/ember/adapter/endpoints.ts +86 -0
  149. package/src/adapter/ember/adapter/oneWaitress.ts +324 -0
  150. package/src/adapter/ember/adapter/tokensManager.ts +780 -0
  151. package/src/adapter/ember/consts.ts +178 -0
  152. package/src/adapter/ember/enums.ts +1746 -0
  153. package/src/adapter/ember/ezsp/buffalo.ts +1392 -0
  154. package/src/adapter/ember/ezsp/consts.ts +148 -0
  155. package/src/adapter/ember/ezsp/enums.ts +1114 -0
  156. package/src/adapter/ember/ezsp/ezsp.ts +9073 -0
  157. package/src/adapter/ember/ezspError.ts +10 -0
  158. package/src/adapter/ember/types.ts +866 -0
  159. package/src/adapter/ember/uart/ash.ts +1933 -0
  160. package/src/adapter/ember/uart/consts.ts +109 -0
  161. package/src/adapter/ember/uart/enums.ts +192 -0
  162. package/src/adapter/ember/uart/parser.ts +42 -0
  163. package/src/adapter/ember/uart/queues.ts +247 -0
  164. package/src/adapter/ember/uart/writer.ts +50 -0
  165. package/src/adapter/ember/utils/initters.ts +58 -0
  166. package/src/adapter/ember/utils/math.ts +71 -0
  167. package/src/adapter/events.ts +21 -0
  168. package/src/adapter/ezsp/adapter/backup.ts +100 -0
  169. package/src/adapter/ezsp/adapter/ezspAdapter.ts +632 -0
  170. package/src/adapter/ezsp/driver/commands.ts +2497 -0
  171. package/src/adapter/ezsp/driver/consts.ts +11 -0
  172. package/src/adapter/ezsp/driver/driver.ts +1002 -0
  173. package/src/adapter/ezsp/driver/ezsp.ts +802 -0
  174. package/src/adapter/ezsp/driver/frame.ts +101 -0
  175. package/src/adapter/ezsp/driver/index.ts +4 -0
  176. package/src/adapter/ezsp/driver/multicast.ts +78 -0
  177. package/src/adapter/ezsp/driver/parser.ts +81 -0
  178. package/src/adapter/ezsp/driver/types/basic.ts +201 -0
  179. package/src/adapter/ezsp/driver/types/index.ts +239 -0
  180. package/src/adapter/ezsp/driver/types/named.ts +2330 -0
  181. package/src/adapter/ezsp/driver/types/struct.ts +844 -0
  182. package/src/adapter/ezsp/driver/uart.ts +460 -0
  183. package/src/adapter/ezsp/driver/utils/crc16ccitt.ts +44 -0
  184. package/src/adapter/ezsp/driver/utils/index.ts +32 -0
  185. package/src/adapter/ezsp/driver/writer.ts +64 -0
  186. package/src/adapter/index.ts +3 -0
  187. package/src/adapter/serialPort.ts +58 -0
  188. package/src/adapter/tstype.ts +57 -0
  189. package/src/adapter/utils.ts +41 -0
  190. package/src/adapter/z-stack/adapter/adapter-backup.ts +509 -0
  191. package/src/adapter/z-stack/adapter/adapter-nv-memory.ts +457 -0
  192. package/src/adapter/z-stack/adapter/endpoints.ts +60 -0
  193. package/src/adapter/z-stack/adapter/manager.ts +543 -0
  194. package/src/adapter/z-stack/adapter/tstype.ts +6 -0
  195. package/src/adapter/z-stack/adapter/zStackAdapter.ts +1350 -0
  196. package/src/adapter/z-stack/constants/af.ts +27 -0
  197. package/src/adapter/z-stack/constants/common.ts +285 -0
  198. package/src/adapter/z-stack/constants/dbg.ts +23 -0
  199. package/src/adapter/z-stack/constants/index.ts +11 -0
  200. package/src/adapter/z-stack/constants/mac.ts +128 -0
  201. package/src/adapter/z-stack/constants/sapi.ts +25 -0
  202. package/src/adapter/z-stack/constants/sys.ts +72 -0
  203. package/src/adapter/z-stack/constants/util.ts +82 -0
  204. package/src/adapter/z-stack/constants/utils.ts +14 -0
  205. package/src/adapter/z-stack/constants/zdo.ts +103 -0
  206. package/src/adapter/z-stack/models/startup-options.ts +13 -0
  207. package/src/adapter/z-stack/structs/entries/address-manager-entry.ts +44 -0
  208. package/src/adapter/z-stack/structs/entries/address-manager-table.ts +19 -0
  209. package/src/adapter/z-stack/structs/entries/aps-link-key-data-entry.ts +12 -0
  210. package/src/adapter/z-stack/structs/entries/aps-link-key-data-table.ts +21 -0
  211. package/src/adapter/z-stack/structs/entries/aps-tc-link-key-entry.ts +19 -0
  212. package/src/adapter/z-stack/structs/entries/aps-tc-link-key-table.ts +21 -0
  213. package/src/adapter/z-stack/structs/entries/channel-list.ts +8 -0
  214. package/src/adapter/z-stack/structs/entries/has-configured.ts +16 -0
  215. package/src/adapter/z-stack/structs/entries/index.ts +16 -0
  216. package/src/adapter/z-stack/structs/entries/nib.ts +66 -0
  217. package/src/adapter/z-stack/structs/entries/nwk-key-descriptor.ts +15 -0
  218. package/src/adapter/z-stack/structs/entries/nwk-key.ts +13 -0
  219. package/src/adapter/z-stack/structs/entries/nwk-pan-id.ts +8 -0
  220. package/src/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-entry.ts +20 -0
  221. package/src/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-table.ts +19 -0
  222. package/src/adapter/z-stack/structs/entries/security-manager-entry.ts +33 -0
  223. package/src/adapter/z-stack/structs/entries/security-manager-table.ts +22 -0
  224. package/src/adapter/z-stack/structs/index.ts +4 -0
  225. package/src/adapter/z-stack/structs/serializable-memory-object.ts +14 -0
  226. package/src/adapter/z-stack/structs/struct.ts +367 -0
  227. package/src/adapter/z-stack/structs/table.ts +198 -0
  228. package/src/adapter/z-stack/unpi/constants.ts +33 -0
  229. package/src/adapter/z-stack/unpi/frame.ts +62 -0
  230. package/src/adapter/z-stack/unpi/index.ts +4 -0
  231. package/src/adapter/z-stack/unpi/parser.ts +67 -0
  232. package/src/adapter/z-stack/unpi/writer.ts +37 -0
  233. package/src/adapter/z-stack/utils/channel-list.ts +40 -0
  234. package/src/adapter/z-stack/utils/index.ts +2 -0
  235. package/src/adapter/z-stack/utils/network-options.ts +26 -0
  236. package/src/adapter/z-stack/znp/buffaloZnp.ts +175 -0
  237. package/src/adapter/z-stack/znp/definition.ts +2713 -0
  238. package/src/adapter/z-stack/znp/index.ts +2 -0
  239. package/src/adapter/z-stack/znp/parameterType.ts +22 -0
  240. package/src/adapter/z-stack/znp/tstype.ts +44 -0
  241. package/src/adapter/z-stack/znp/utils.ts +10 -0
  242. package/src/adapter/z-stack/znp/znp.ts +345 -0
  243. package/src/adapter/z-stack/znp/zpiObject.ts +148 -0
  244. package/src/adapter/zboss/adapter/zbossAdapter.ts +535 -0
  245. package/src/adapter/zboss/commands.ts +1184 -0
  246. package/src/adapter/zboss/consts.ts +9 -0
  247. package/src/adapter/zboss/driver.ts +422 -0
  248. package/src/adapter/zboss/enums.ts +360 -0
  249. package/src/adapter/zboss/frame.ts +227 -0
  250. package/src/adapter/zboss/reader.ts +65 -0
  251. package/src/adapter/zboss/types.ts +0 -0
  252. package/src/adapter/zboss/uart.ts +428 -0
  253. package/src/adapter/zboss/utils.ts +58 -0
  254. package/src/adapter/zboss/writer.ts +49 -0
  255. package/src/adapter/zigate/adapter/patchZdoBuffaloBE.ts +25 -0
  256. package/src/adapter/zigate/adapter/zigateAdapter.ts +633 -0
  257. package/src/adapter/zigate/driver/LICENSE +17 -0
  258. package/src/adapter/zigate/driver/buffaloZiGate.ts +210 -0
  259. package/src/adapter/zigate/driver/commandType.ts +418 -0
  260. package/src/adapter/zigate/driver/constants.ts +150 -0
  261. package/src/adapter/zigate/driver/frame.ts +197 -0
  262. package/src/adapter/zigate/driver/messageType.ts +287 -0
  263. package/src/adapter/zigate/driver/parameterType.ts +32 -0
  264. package/src/adapter/zigate/driver/ziGateObject.ts +146 -0
  265. package/src/adapter/zigate/driver/zigate.ts +422 -0
  266. package/src/adapter/zoh/adapter/utils.ts +27 -0
  267. package/src/adapter/zoh/adapter/zohAdapter.ts +931 -0
  268. package/src/buffalo/buffalo.ts +336 -0
  269. package/src/buffalo/index.ts +1 -0
  270. package/src/controller/controller.ts +1159 -0
  271. package/src/controller/database.ts +148 -0
  272. package/src/controller/events.ts +52 -0
  273. package/src/controller/greenPower.ts +613 -0
  274. package/src/controller/helpers/index.ts +1 -0
  275. package/src/controller/helpers/installCodes.ts +107 -0
  276. package/src/controller/helpers/ota.ts +578 -0
  277. package/src/controller/helpers/request.ts +96 -0
  278. package/src/controller/helpers/requestQueue.ts +126 -0
  279. package/src/controller/helpers/zclFrameConverter.ts +64 -0
  280. package/src/controller/helpers/zclTransactionSequenceNumber.ts +15 -0
  281. package/src/controller/index.ts +6 -0
  282. package/src/controller/model/device.ts +1791 -0
  283. package/src/controller/model/endpoint.ts +1235 -0
  284. package/src/controller/model/entity.ts +43 -0
  285. package/src/controller/model/group.ts +446 -0
  286. package/src/controller/model/index.ts +5 -0
  287. package/src/controller/model/konnextConfig.ts +6 -0
  288. package/src/controller/model/zigbeeEntity.ts +30 -0
  289. package/src/controller/touchlink.ts +195 -0
  290. package/src/controller/tstype.ts +374 -0
  291. package/src/index.ts +14 -0
  292. package/src/models/backup-storage-legacy.ts +48 -0
  293. package/src/models/backup-storage-unified.ts +47 -0
  294. package/src/models/backup.ts +37 -0
  295. package/src/models/index.ts +5 -0
  296. package/src/models/network-options.ts +11 -0
  297. package/src/utils/aes.ts +218 -0
  298. package/src/utils/async-mutex.ts +31 -0
  299. package/src/utils/backup.ts +152 -0
  300. package/src/utils/index.ts +5 -0
  301. package/src/utils/logger.ts +20 -0
  302. package/src/utils/patchBigIntSerialization.ts +8 -0
  303. package/src/utils/queue.ts +79 -0
  304. package/src/utils/timeService.ts +139 -0
  305. package/src/utils/utils.ts +19 -0
  306. package/src/utils/wait.ts +5 -0
  307. package/src/utils/waitress.ts +96 -0
  308. package/src/zspec/consts.ts +89 -0
  309. package/src/zspec/enums.ts +22 -0
  310. package/src/zspec/index.ts +3 -0
  311. package/src/zspec/tstypes.ts +18 -0
  312. package/src/zspec/utils.ts +247 -0
  313. package/src/zspec/zcl/buffaloZcl.ts +1073 -0
  314. package/src/zspec/zcl/definition/cluster.ts +7554 -0
  315. package/src/zspec/zcl/definition/clusters-types.ts +8228 -0
  316. package/src/zspec/zcl/definition/consts.ts +24 -0
  317. package/src/zspec/zcl/definition/datatypes.ts +2454 -0
  318. package/src/zspec/zcl/definition/enums.ts +224 -0
  319. package/src/zspec/zcl/definition/foundation.ts +342 -0
  320. package/src/zspec/zcl/definition/manufacturerCode.ts +730 -0
  321. package/src/zspec/zcl/definition/status.ts +69 -0
  322. package/src/zspec/zcl/definition/tstype.ts +432 -0
  323. package/src/zspec/zcl/index.ts +11 -0
  324. package/src/zspec/zcl/utils.ts +504 -0
  325. package/src/zspec/zcl/zclFrame.ts +383 -0
  326. package/src/zspec/zcl/zclHeader.ts +102 -0
  327. package/src/zspec/zcl/zclStatusError.ts +10 -0
  328. package/src/zspec/zdo/buffaloZdo.ts +2336 -0
  329. package/src/zspec/zdo/definition/clusters.ts +722 -0
  330. package/src/zspec/zdo/definition/consts.ts +16 -0
  331. package/src/zspec/zdo/definition/enums.ts +99 -0
  332. package/src/zspec/zdo/definition/status.ts +105 -0
  333. package/src/zspec/zdo/definition/tstypes.ts +1062 -0
  334. package/src/zspec/zdo/index.ts +7 -0
  335. package/src/zspec/zdo/utils.ts +76 -0
  336. package/src/zspec/zdo/zdoStatusError.ts +10 -0
  337. package/test/adapter/adapter.test.ts +1276 -0
  338. package/test/adapter/ember/ash.test.ts +337 -0
  339. package/test/adapter/ember/consts.ts +131 -0
  340. package/test/adapter/ember/emberAdapter.test.ts +3447 -0
  341. package/test/adapter/ember/ezsp.test.ts +389 -0
  342. package/test/adapter/ember/ezspBuffalo.test.ts +93 -0
  343. package/test/adapter/ember/ezspError.test.ts +12 -0
  344. package/test/adapter/ember/math.test.ts +190 -0
  345. package/test/adapter/ezsp/frame.test.ts +30 -0
  346. package/test/adapter/ezsp/uart.test.ts +181 -0
  347. package/test/adapter/z-stack/adapter.test.ts +4260 -0
  348. package/test/adapter/z-stack/constants.test.ts +33 -0
  349. package/test/adapter/z-stack/structs.test.ts +115 -0
  350. package/test/adapter/z-stack/unpi.test.ts +213 -0
  351. package/test/adapter/z-stack/znp.test.ts +1288 -0
  352. package/test/adapter/zboss/fixZdoResponse.test.ts +179 -0
  353. package/test/adapter/zigate/patchZdoBuffaloBE.test.ts +81 -0
  354. package/test/adapter/zigate/zdo.test.ts +187 -0
  355. package/test/adapter/zoh/utils.test.ts +36 -0
  356. package/test/adapter/zoh/zohAdapter.test.ts +1451 -0
  357. package/test/benchOptions.ts +14 -0
  358. package/test/buffalo.test.ts +431 -0
  359. package/test/controller.bench.ts +215 -0
  360. package/test/controller.test.ts +10038 -0
  361. package/test/data/integrity-code-1166-012B-24031511-upgradeMe-RB 249 T.zigbee +0 -0
  362. package/test/data/manuf-tags-tradfri-cv-cct-unified_release_prod_v587757105_33e34452-9267-4665-bc5a-844c8f61f063.ota +0 -0
  363. package/test/data/padded-tretakt_smart_plug_soc-0x1100-2.4.25-prod.ota.ota.signed +0 -0
  364. package/test/data/telink-aes-A60_RGBW_T-0x00B6-0x03483712-MF_DIS.OTA +0 -0
  365. package/test/data/zbminir2_v1.0.8.ota +0 -0
  366. package/test/device-ota.test.ts +3332 -0
  367. package/test/greenpower.test.ts +1409 -0
  368. package/test/mockAdapters.ts +95 -0
  369. package/test/mockDevices.ts +623 -0
  370. package/test/requests.bench.ts +321 -0
  371. package/test/testUtils.ts +20 -0
  372. package/test/timeService.test.ts +214 -0
  373. package/test/tsconfig.json +9 -0
  374. package/test/utils/math.ts +19 -0
  375. package/test/utils.test.ts +352 -0
  376. package/test/vitest.config.mts +28 -0
  377. package/test/vitest.ts +9 -0
  378. package/test/zcl.test.ts +2887 -0
  379. package/test/zspec/utils.test.ts +68 -0
  380. package/test/zspec/zcl/buffalo.test.ts +1316 -0
  381. package/test/zspec/zcl/frame.test.ts +1056 -0
  382. package/test/zspec/zcl/utils.test.ts +650 -0
  383. package/test/zspec/zdo/buffalo.test.ts +1850 -0
  384. package/test/zspec/zdo/utils.test.ts +241 -0
  385. package/tsconfig.json +8 -10
  386. package/.babelrc.js +0 -6
  387. package/.eslintignore +0 -3
  388. package/dist/adapter/adapter.d.ts +0 -64
  389. package/dist/adapter/adapter.js +0 -157
  390. package/dist/adapter/adapter.js.map +0 -1
  391. package/dist/adapter/deconz/adapter/deconzAdapter.d.ts +0 -71
  392. package/dist/adapter/deconz/adapter/deconzAdapter.js +0 -1072
  393. package/dist/adapter/deconz/adapter/deconzAdapter.js.map +0 -1
  394. package/dist/adapter/deconz/adapter/index.d.ts +0 -3
  395. package/dist/adapter/deconz/adapter/index.d.ts.map +0 -1
  396. package/dist/adapter/deconz/adapter/index.js +0 -11
  397. package/dist/adapter/deconz/adapter/index.js.map +0 -1
  398. package/dist/adapter/deconz/driver/constants.d.ts +0 -105
  399. package/dist/adapter/deconz/driver/constants.d.ts.map +0 -1
  400. package/dist/adapter/deconz/driver/constants.js +0 -56
  401. package/dist/adapter/deconz/driver/constants.js.map +0 -1
  402. package/dist/adapter/deconz/driver/driver.d.ts +0 -82
  403. package/dist/adapter/deconz/driver/driver.d.ts.map +0 -1
  404. package/dist/adapter/deconz/driver/driver.js +0 -751
  405. package/dist/adapter/deconz/driver/driver.js.map +0 -1
  406. package/dist/adapter/deconz/driver/frame.d.ts +0 -7
  407. package/dist/adapter/deconz/driver/frame.d.ts.map +0 -1
  408. package/dist/adapter/deconz/driver/frame.js +0 -14
  409. package/dist/adapter/deconz/driver/frame.js.map +0 -1
  410. package/dist/adapter/deconz/driver/frameParser.d.ts +0 -3
  411. package/dist/adapter/deconz/driver/frameParser.d.ts.map +0 -1
  412. package/dist/adapter/deconz/driver/frameParser.js +0 -444
  413. package/dist/adapter/deconz/driver/frameParser.js.map +0 -1
  414. package/dist/adapter/deconz/driver/parser.d.ts +0 -13
  415. package/dist/adapter/deconz/driver/parser.d.ts.map +0 -1
  416. package/dist/adapter/deconz/driver/parser.js +0 -64
  417. package/dist/adapter/deconz/driver/parser.js.map +0 -1
  418. package/dist/adapter/deconz/driver/writer.d.ts +0 -9
  419. package/dist/adapter/deconz/driver/writer.d.ts.map +0 -1
  420. package/dist/adapter/deconz/driver/writer.js +0 -45
  421. package/dist/adapter/deconz/driver/writer.js.map +0 -1
  422. package/dist/adapter/ember/adapter/emberAdapter.d.ts +0 -806
  423. package/dist/adapter/ember/adapter/emberAdapter.js +0 -2942
  424. package/dist/adapter/ember/adapter/emberAdapter.js.map +0 -1
  425. package/dist/adapter/ember/adapter/endpoints.d.ts +0 -27
  426. package/dist/adapter/ember/adapter/endpoints.js +0 -68
  427. package/dist/adapter/ember/adapter/endpoints.js.map +0 -1
  428. package/dist/adapter/ember/adapter/index.d.ts +0 -3
  429. package/dist/adapter/ember/adapter/index.d.ts.map +0 -1
  430. package/dist/adapter/ember/adapter/index.js +0 -6
  431. package/dist/adapter/ember/adapter/index.js.map +0 -1
  432. package/dist/adapter/ember/adapter/oneWaitress.d.ts +0 -108
  433. package/dist/adapter/ember/adapter/oneWaitress.js +0 -241
  434. package/dist/adapter/ember/adapter/oneWaitress.js.map +0 -1
  435. package/dist/adapter/ember/adapter/requestQueue.d.ts +0 -57
  436. package/dist/adapter/ember/adapter/requestQueue.d.ts.map +0 -1
  437. package/dist/adapter/ember/adapter/requestQueue.js +0 -139
  438. package/dist/adapter/ember/adapter/requestQueue.js.map +0 -1
  439. package/dist/adapter/ember/adapter/tokensManager.d.ts +0 -69
  440. package/dist/adapter/ember/adapter/tokensManager.js +0 -688
  441. package/dist/adapter/ember/adapter/tokensManager.js.map +0 -1
  442. package/dist/adapter/ember/consts.d.ts +0 -191
  443. package/dist/adapter/ember/consts.d.ts.map +0 -1
  444. package/dist/adapter/ember/consts.js +0 -246
  445. package/dist/adapter/ember/consts.js.map +0 -1
  446. package/dist/adapter/ember/enums.d.ts +0 -2172
  447. package/dist/adapter/ember/enums.d.ts.map +0 -1
  448. package/dist/adapter/ember/enums.js +0 -2375
  449. package/dist/adapter/ember/enums.js.map +0 -1
  450. package/dist/adapter/ember/ezsp/buffalo.d.ts +0 -156
  451. package/dist/adapter/ember/ezsp/buffalo.d.ts.map +0 -1
  452. package/dist/adapter/ember/ezsp/buffalo.js +0 -1033
  453. package/dist/adapter/ember/ezsp/buffalo.js.map +0 -1
  454. package/dist/adapter/ember/ezsp/consts.d.ts +0 -116
  455. package/dist/adapter/ember/ezsp/consts.d.ts.map +0 -1
  456. package/dist/adapter/ember/ezsp/consts.js +0 -128
  457. package/dist/adapter/ember/ezsp/consts.js.map +0 -1
  458. package/dist/adapter/ember/ezsp/enums.d.ts +0 -879
  459. package/dist/adapter/ember/ezsp/enums.d.ts.map +0 -1
  460. package/dist/adapter/ember/ezsp/enums.js +0 -948
  461. package/dist/adapter/ember/ezsp/enums.js.map +0 -1
  462. package/dist/adapter/ember/ezsp/ezsp.d.ts +0 -2662
  463. package/dist/adapter/ember/ezsp/ezsp.js +0 -6454
  464. package/dist/adapter/ember/ezsp/ezsp.js.map +0 -1
  465. package/dist/adapter/ember/types.d.ts +0 -733
  466. package/dist/adapter/ember/types.d.ts.map +0 -1
  467. package/dist/adapter/ember/types.js +0 -3
  468. package/dist/adapter/ember/types.js.map +0 -1
  469. package/dist/adapter/ember/uart/ash.d.ts +0 -464
  470. package/dist/adapter/ember/uart/ash.d.ts.map +0 -1
  471. package/dist/adapter/ember/uart/ash.js +0 -1633
  472. package/dist/adapter/ember/uart/ash.js.map +0 -1
  473. package/dist/adapter/ember/uart/consts.d.ts +0 -91
  474. package/dist/adapter/ember/uart/consts.d.ts.map +0 -1
  475. package/dist/adapter/ember/uart/consts.js +0 -100
  476. package/dist/adapter/ember/uart/consts.js.map +0 -1
  477. package/dist/adapter/ember/uart/enums.d.ts +0 -191
  478. package/dist/adapter/ember/uart/enums.d.ts.map +0 -1
  479. package/dist/adapter/ember/uart/enums.js +0 -197
  480. package/dist/adapter/ember/uart/enums.js.map +0 -1
  481. package/dist/adapter/ember/uart/parser.d.ts +0 -10
  482. package/dist/adapter/ember/uart/parser.d.ts.map +0 -1
  483. package/dist/adapter/ember/uart/parser.js +0 -37
  484. package/dist/adapter/ember/uart/parser.js.map +0 -1
  485. package/dist/adapter/ember/uart/queues.d.ts +0 -85
  486. package/dist/adapter/ember/uart/queues.d.ts.map +0 -1
  487. package/dist/adapter/ember/uart/queues.js +0 -214
  488. package/dist/adapter/ember/uart/queues.js.map +0 -1
  489. package/dist/adapter/ember/uart/writer.d.ts +0 -15
  490. package/dist/adapter/ember/uart/writer.d.ts.map +0 -1
  491. package/dist/adapter/ember/uart/writer.js +0 -46
  492. package/dist/adapter/ember/uart/writer.js.map +0 -1
  493. package/dist/adapter/ember/utils/initters.d.ts +0 -20
  494. package/dist/adapter/ember/utils/initters.js +0 -58
  495. package/dist/adapter/ember/utils/initters.js.map +0 -1
  496. package/dist/adapter/ember/utils/math.d.ts +0 -51
  497. package/dist/adapter/ember/utils/math.d.ts.map +0 -1
  498. package/dist/adapter/ember/utils/math.js +0 -102
  499. package/dist/adapter/ember/utils/math.js.map +0 -1
  500. package/dist/adapter/ember/zdo.d.ts +0 -925
  501. package/dist/adapter/ember/zdo.d.ts.map +0 -1
  502. package/dist/adapter/ember/zdo.js +0 -723
  503. package/dist/adapter/ember/zdo.js.map +0 -1
  504. package/dist/adapter/events.d.ts +0 -42
  505. package/dist/adapter/events.js +0 -13
  506. package/dist/adapter/events.js.map +0 -1
  507. package/dist/adapter/ezsp/adapter/backup.d.ts +0 -13
  508. package/dist/adapter/ezsp/adapter/backup.js +0 -101
  509. package/dist/adapter/ezsp/adapter/backup.js.map +0 -1
  510. package/dist/adapter/ezsp/adapter/ezspAdapter.d.ts +0 -65
  511. package/dist/adapter/ezsp/adapter/ezspAdapter.js +0 -634
  512. package/dist/adapter/ezsp/adapter/ezspAdapter.js.map +0 -1
  513. package/dist/adapter/ezsp/adapter/index.d.ts +0 -3
  514. package/dist/adapter/ezsp/adapter/index.d.ts.map +0 -1
  515. package/dist/adapter/ezsp/adapter/index.js +0 -11
  516. package/dist/adapter/ezsp/adapter/index.js.map +0 -1
  517. package/dist/adapter/ezsp/driver/commands.d.ts +0 -37
  518. package/dist/adapter/ezsp/driver/commands.d.ts.map +0 -1
  519. package/dist/adapter/ezsp/driver/commands.js +0 -2387
  520. package/dist/adapter/ezsp/driver/commands.js.map +0 -1
  521. package/dist/adapter/ezsp/driver/consts.d.ts +0 -11
  522. package/dist/adapter/ezsp/driver/consts.d.ts.map +0 -1
  523. package/dist/adapter/ezsp/driver/consts.js +0 -14
  524. package/dist/adapter/ezsp/driver/consts.js.map +0 -1
  525. package/dist/adapter/ezsp/driver/driver.d.ts +0 -109
  526. package/dist/adapter/ezsp/driver/driver.js +0 -796
  527. package/dist/adapter/ezsp/driver/driver.js.map +0 -1
  528. package/dist/adapter/ezsp/driver/ezsp.d.ts +0 -106
  529. package/dist/adapter/ezsp/driver/ezsp.d.ts.map +0 -1
  530. package/dist/adapter/ezsp/driver/ezsp.js +0 -664
  531. package/dist/adapter/ezsp/driver/ezsp.js.map +0 -1
  532. package/dist/adapter/ezsp/driver/frame.d.ts +0 -40
  533. package/dist/adapter/ezsp/driver/frame.d.ts.map +0 -1
  534. package/dist/adapter/ezsp/driver/frame.js +0 -101
  535. package/dist/adapter/ezsp/driver/frame.js.map +0 -1
  536. package/dist/adapter/ezsp/driver/index.d.ts +0 -4
  537. package/dist/adapter/ezsp/driver/index.js +0 -9
  538. package/dist/adapter/ezsp/driver/index.js.map +0 -1
  539. package/dist/adapter/ezsp/driver/multicast.d.ts +0 -13
  540. package/dist/adapter/ezsp/driver/multicast.js +0 -74
  541. package/dist/adapter/ezsp/driver/multicast.js.map +0 -1
  542. package/dist/adapter/ezsp/driver/parser.d.ts +0 -12
  543. package/dist/adapter/ezsp/driver/parser.d.ts.map +0 -1
  544. package/dist/adapter/ezsp/driver/parser.js +0 -105
  545. package/dist/adapter/ezsp/driver/parser.js.map +0 -1
  546. package/dist/adapter/ezsp/driver/types/basic.d.ts +0 -63
  547. package/dist/adapter/ezsp/driver/types/basic.d.ts.map +0 -1
  548. package/dist/adapter/ezsp/driver/types/basic.js +0 -209
  549. package/dist/adapter/ezsp/driver/types/basic.js.map +0 -1
  550. package/dist/adapter/ezsp/driver/types/index.d.ts +0 -10
  551. package/dist/adapter/ezsp/driver/types/index.d.ts.map +0 -1
  552. package/dist/adapter/ezsp/driver/types/index.js +0 -139
  553. package/dist/adapter/ezsp/driver/types/index.js.map +0 -1
  554. package/dist/adapter/ezsp/driver/types/named.d.ts +0 -1288
  555. package/dist/adapter/ezsp/driver/types/named.d.ts.map +0 -1
  556. package/dist/adapter/ezsp/driver/types/named.js +0 -2330
  557. package/dist/adapter/ezsp/driver/types/named.js.map +0 -1
  558. package/dist/adapter/ezsp/driver/types/struct.d.ts +0 -271
  559. package/dist/adapter/ezsp/driver/types/struct.d.ts.map +0 -1
  560. package/dist/adapter/ezsp/driver/types/struct.js +0 -804
  561. package/dist/adapter/ezsp/driver/types/struct.js.map +0 -1
  562. package/dist/adapter/ezsp/driver/uart.d.ts +0 -49
  563. package/dist/adapter/ezsp/driver/uart.d.ts.map +0 -1
  564. package/dist/adapter/ezsp/driver/uart.js +0 -383
  565. package/dist/adapter/ezsp/driver/uart.js.map +0 -1
  566. package/dist/adapter/ezsp/driver/utils/crc16ccitt.d.ts +0 -3
  567. package/dist/adapter/ezsp/driver/utils/crc16ccitt.d.ts.map +0 -1
  568. package/dist/adapter/ezsp/driver/utils/crc16ccitt.js +0 -56
  569. package/dist/adapter/ezsp/driver/utils/crc16ccitt.js.map +0 -1
  570. package/dist/adapter/ezsp/driver/utils/index.d.ts +0 -20
  571. package/dist/adapter/ezsp/driver/utils/index.d.ts.map +0 -1
  572. package/dist/adapter/ezsp/driver/utils/index.js +0 -73
  573. package/dist/adapter/ezsp/driver/utils/index.js.map +0 -1
  574. package/dist/adapter/ezsp/driver/writer.d.ts +0 -14
  575. package/dist/adapter/ezsp/driver/writer.d.ts.map +0 -1
  576. package/dist/adapter/ezsp/driver/writer.js +0 -83
  577. package/dist/adapter/ezsp/driver/writer.js.map +0 -1
  578. package/dist/adapter/index.d.ts +0 -5
  579. package/dist/adapter/index.js +0 -36
  580. package/dist/adapter/index.js.map +0 -1
  581. package/dist/adapter/serialPort.d.ts +0 -14
  582. package/dist/adapter/serialPort.d.ts.map +0 -1
  583. package/dist/adapter/serialPort.js +0 -47
  584. package/dist/adapter/serialPort.js.map +0 -1
  585. package/dist/adapter/serialPortUtils.d.ts +0 -13
  586. package/dist/adapter/serialPortUtils.d.ts.map +0 -1
  587. package/dist/adapter/serialPortUtils.js +0 -19
  588. package/dist/adapter/serialPortUtils.js.map +0 -1
  589. package/dist/adapter/socketPortUtils.d.ts +0 -11
  590. package/dist/adapter/socketPortUtils.d.ts.map +0 -1
  591. package/dist/adapter/socketPortUtils.js +0 -17
  592. package/dist/adapter/socketPortUtils.js.map +0 -1
  593. package/dist/adapter/tstype.d.ts +0 -86
  594. package/dist/adapter/tstype.d.ts.map +0 -1
  595. package/dist/adapter/tstype.js +0 -3
  596. package/dist/adapter/tstype.js.map +0 -1
  597. package/dist/adapter/z-stack/adapter/adapter-backup.d.ts +0 -62
  598. package/dist/adapter/z-stack/adapter/adapter-backup.d.ts.map +0 -1
  599. package/dist/adapter/z-stack/adapter/adapter-backup.js +0 -459
  600. package/dist/adapter/z-stack/adapter/adapter-backup.js.map +0 -1
  601. package/dist/adapter/z-stack/adapter/adapter-nv-memory.d.ts +0 -151
  602. package/dist/adapter/z-stack/adapter/adapter-nv-memory.d.ts.map +0 -1
  603. package/dist/adapter/z-stack/adapter/adapter-nv-memory.js +0 -259
  604. package/dist/adapter/z-stack/adapter/adapter-nv-memory.js.map +0 -1
  605. package/dist/adapter/z-stack/adapter/endpoints.d.ts +0 -12
  606. package/dist/adapter/z-stack/adapter/endpoints.js +0 -74
  607. package/dist/adapter/z-stack/adapter/endpoints.js.map +0 -1
  608. package/dist/adapter/z-stack/adapter/index.d.ts +0 -3
  609. package/dist/adapter/z-stack/adapter/index.d.ts.map +0 -1
  610. package/dist/adapter/z-stack/adapter/index.js +0 -9
  611. package/dist/adapter/z-stack/adapter/index.js.map +0 -1
  612. package/dist/adapter/z-stack/adapter/manager.d.ts +0 -84
  613. package/dist/adapter/z-stack/adapter/manager.js +0 -474
  614. package/dist/adapter/z-stack/adapter/manager.js.map +0 -1
  615. package/dist/adapter/z-stack/adapter/tstype.d.ts +0 -7
  616. package/dist/adapter/z-stack/adapter/tstype.d.ts.map +0 -1
  617. package/dist/adapter/z-stack/adapter/tstype.js +0 -10
  618. package/dist/adapter/z-stack/adapter/tstype.js.map +0 -1
  619. package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts +0 -86
  620. package/dist/adapter/z-stack/adapter/zStackAdapter.js +0 -912
  621. package/dist/adapter/z-stack/adapter/zStackAdapter.js.map +0 -1
  622. package/dist/adapter/z-stack/constants/af.d.ts +0 -24
  623. package/dist/adapter/z-stack/constants/af.d.ts.map +0 -1
  624. package/dist/adapter/z-stack/constants/af.js +0 -28
  625. package/dist/adapter/z-stack/constants/af.js.map +0 -1
  626. package/dist/adapter/z-stack/constants/common.d.ts +0 -279
  627. package/dist/adapter/z-stack/constants/common.d.ts.map +0 -1
  628. package/dist/adapter/z-stack/constants/common.js +0 -293
  629. package/dist/adapter/z-stack/constants/common.js.map +0 -1
  630. package/dist/adapter/z-stack/constants/dbg.d.ts +0 -23
  631. package/dist/adapter/z-stack/constants/dbg.d.ts.map +0 -1
  632. package/dist/adapter/z-stack/constants/dbg.js +0 -25
  633. package/dist/adapter/z-stack/constants/dbg.js.map +0 -1
  634. package/dist/adapter/z-stack/constants/index.d.ts +0 -11
  635. package/dist/adapter/z-stack/constants/index.d.ts.map +0 -1
  636. package/dist/adapter/z-stack/constants/index.js +0 -48
  637. package/dist/adapter/z-stack/constants/index.js.map +0 -1
  638. package/dist/adapter/z-stack/constants/mac.d.ts +0 -128
  639. package/dist/adapter/z-stack/constants/mac.d.ts.map +0 -1
  640. package/dist/adapter/z-stack/constants/mac.js +0 -130
  641. package/dist/adapter/z-stack/constants/mac.js.map +0 -1
  642. package/dist/adapter/z-stack/constants/sapi.d.ts +0 -25
  643. package/dist/adapter/z-stack/constants/sapi.d.ts.map +0 -1
  644. package/dist/adapter/z-stack/constants/sapi.js +0 -27
  645. package/dist/adapter/z-stack/constants/sapi.js.map +0 -1
  646. package/dist/adapter/z-stack/constants/sys.d.ts +0 -72
  647. package/dist/adapter/z-stack/constants/sys.d.ts.map +0 -1
  648. package/dist/adapter/z-stack/constants/sys.js +0 -74
  649. package/dist/adapter/z-stack/constants/sys.js.map +0 -1
  650. package/dist/adapter/z-stack/constants/util.d.ts +0 -82
  651. package/dist/adapter/z-stack/constants/util.d.ts.map +0 -1
  652. package/dist/adapter/z-stack/constants/util.js +0 -84
  653. package/dist/adapter/z-stack/constants/util.js.map +0 -1
  654. package/dist/adapter/z-stack/constants/utils.d.ts +0 -5
  655. package/dist/adapter/z-stack/constants/utils.d.ts.map +0 -1
  656. package/dist/adapter/z-stack/constants/utils.js +0 -15
  657. package/dist/adapter/z-stack/constants/utils.js.map +0 -1
  658. package/dist/adapter/z-stack/constants/zdo.d.ts +0 -103
  659. package/dist/adapter/z-stack/constants/zdo.d.ts.map +0 -1
  660. package/dist/adapter/z-stack/constants/zdo.js +0 -105
  661. package/dist/adapter/z-stack/constants/zdo.js.map +0 -1
  662. package/dist/adapter/z-stack/models/index.d.ts +0 -2
  663. package/dist/adapter/z-stack/models/index.d.ts.map +0 -1
  664. package/dist/adapter/z-stack/models/index.js +0 -18
  665. package/dist/adapter/z-stack/models/index.js.map +0 -1
  666. package/dist/adapter/z-stack/models/startup-options.d.ts +0 -13
  667. package/dist/adapter/z-stack/models/startup-options.js +0 -3
  668. package/dist/adapter/z-stack/models/startup-options.js.map +0 -1
  669. package/dist/adapter/z-stack/structs/entries/address-manager-entry.d.ts +0 -24
  670. package/dist/adapter/z-stack/structs/entries/address-manager-entry.d.ts.map +0 -1
  671. package/dist/adapter/z-stack/structs/entries/address-manager-entry.js +0 -46
  672. package/dist/adapter/z-stack/structs/entries/address-manager-entry.js.map +0 -1
  673. package/dist/adapter/z-stack/structs/entries/address-manager-table.d.ts +0 -11
  674. package/dist/adapter/z-stack/structs/entries/address-manager-table.d.ts.map +0 -1
  675. package/dist/adapter/z-stack/structs/entries/address-manager-table.js +0 -23
  676. package/dist/adapter/z-stack/structs/entries/address-manager-table.js.map +0 -1
  677. package/dist/adapter/z-stack/structs/entries/aps-link-key-data-entry.d.ts +0 -11
  678. package/dist/adapter/z-stack/structs/entries/aps-link-key-data-entry.d.ts.map +0 -1
  679. package/dist/adapter/z-stack/structs/entries/aps-link-key-data-entry.js +0 -22
  680. package/dist/adapter/z-stack/structs/entries/aps-link-key-data-entry.js.map +0 -1
  681. package/dist/adapter/z-stack/structs/entries/aps-link-key-data-table.d.ts +0 -11
  682. package/dist/adapter/z-stack/structs/entries/aps-link-key-data-table.d.ts.map +0 -1
  683. package/dist/adapter/z-stack/structs/entries/aps-link-key-data-table.js +0 -24
  684. package/dist/adapter/z-stack/structs/entries/aps-link-key-data-table.js.map +0 -1
  685. package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-entry.d.ts +0 -11
  686. package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-entry.d.ts.map +0 -1
  687. package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-entry.js +0 -25
  688. package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-entry.js.map +0 -1
  689. package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-table.d.ts +0 -11
  690. package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-table.d.ts.map +0 -1
  691. package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-table.js +0 -24
  692. package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-table.js.map +0 -1
  693. package/dist/adapter/z-stack/structs/entries/channel-list.d.ts +0 -9
  694. package/dist/adapter/z-stack/structs/entries/channel-list.d.ts.map +0 -1
  695. package/dist/adapter/z-stack/structs/entries/channel-list.js +0 -16
  696. package/dist/adapter/z-stack/structs/entries/channel-list.js.map +0 -1
  697. package/dist/adapter/z-stack/structs/entries/has-configured.d.ts +0 -9
  698. package/dist/adapter/z-stack/structs/entries/has-configured.d.ts.map +0 -1
  699. package/dist/adapter/z-stack/structs/entries/has-configured.js +0 -17
  700. package/dist/adapter/z-stack/structs/entries/has-configured.js.map +0 -1
  701. package/dist/adapter/z-stack/structs/entries/index.d.ts +0 -17
  702. package/dist/adapter/z-stack/structs/entries/index.d.ts.map +0 -1
  703. package/dist/adapter/z-stack/structs/entries/index.js +0 -33
  704. package/dist/adapter/z-stack/structs/entries/index.js.map +0 -1
  705. package/dist/adapter/z-stack/structs/entries/nib.d.ts +0 -11
  706. package/dist/adapter/z-stack/structs/entries/nib.d.ts.map +0 -1
  707. package/dist/adapter/z-stack/structs/entries/nib.js +0 -69
  708. package/dist/adapter/z-stack/structs/entries/nib.js.map +0 -1
  709. package/dist/adapter/z-stack/structs/entries/nwk-key-descriptor.d.ts +0 -11
  710. package/dist/adapter/z-stack/structs/entries/nwk-key-descriptor.d.ts.map +0 -1
  711. package/dist/adapter/z-stack/structs/entries/nwk-key-descriptor.js +0 -19
  712. package/dist/adapter/z-stack/structs/entries/nwk-key-descriptor.js.map +0 -1
  713. package/dist/adapter/z-stack/structs/entries/nwk-key.d.ts +0 -9
  714. package/dist/adapter/z-stack/structs/entries/nwk-key.d.ts.map +0 -1
  715. package/dist/adapter/z-stack/structs/entries/nwk-key.js +0 -16
  716. package/dist/adapter/z-stack/structs/entries/nwk-key.js.map +0 -1
  717. package/dist/adapter/z-stack/structs/entries/nwk-pan-id.d.ts +0 -9
  718. package/dist/adapter/z-stack/structs/entries/nwk-pan-id.d.ts.map +0 -1
  719. package/dist/adapter/z-stack/structs/entries/nwk-pan-id.js +0 -16
  720. package/dist/adapter/z-stack/structs/entries/nwk-pan-id.js.map +0 -1
  721. package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-entry.d.ts +0 -14
  722. package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-entry.d.ts.map +0 -1
  723. package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-entry.js +0 -24
  724. package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-entry.js.map +0 -1
  725. package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-table.d.ts +0 -11
  726. package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-table.d.ts.map +0 -1
  727. package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-table.js +0 -23
  728. package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-table.js.map +0 -1
  729. package/dist/adapter/z-stack/structs/entries/security-manager-entry.d.ts +0 -21
  730. package/dist/adapter/z-stack/structs/entries/security-manager-entry.d.ts.map +0 -1
  731. package/dist/adapter/z-stack/structs/entries/security-manager-entry.js +0 -37
  732. package/dist/adapter/z-stack/structs/entries/security-manager-entry.js.map +0 -1
  733. package/dist/adapter/z-stack/structs/entries/security-manager-table.d.ts +0 -11
  734. package/dist/adapter/z-stack/structs/entries/security-manager-table.d.ts.map +0 -1
  735. package/dist/adapter/z-stack/structs/entries/security-manager-table.js +0 -25
  736. package/dist/adapter/z-stack/structs/entries/security-manager-table.js.map +0 -1
  737. package/dist/adapter/z-stack/structs/index.d.ts +0 -5
  738. package/dist/adapter/z-stack/structs/index.d.ts.map +0 -1
  739. package/dist/adapter/z-stack/structs/index.js +0 -21
  740. package/dist/adapter/z-stack/structs/index.js.map +0 -1
  741. package/dist/adapter/z-stack/structs/serializable-memory-object.d.ts +0 -14
  742. package/dist/adapter/z-stack/structs/serializable-memory-object.d.ts.map +0 -1
  743. package/dist/adapter/z-stack/structs/serializable-memory-object.js +0 -3
  744. package/dist/adapter/z-stack/structs/serializable-memory-object.js.map +0 -1
  745. package/dist/adapter/z-stack/structs/struct.d.ts +0 -100
  746. package/dist/adapter/z-stack/structs/struct.d.ts.map +0 -1
  747. package/dist/adapter/z-stack/structs/struct.js +0 -297
  748. package/dist/adapter/z-stack/structs/struct.js.map +0 -1
  749. package/dist/adapter/z-stack/structs/table.d.ts +0 -95
  750. package/dist/adapter/z-stack/structs/table.d.ts.map +0 -1
  751. package/dist/adapter/z-stack/structs/table.js +0 -164
  752. package/dist/adapter/z-stack/structs/table.js.map +0 -1
  753. package/dist/adapter/z-stack/unpi/constants.d.ts +0 -29
  754. package/dist/adapter/z-stack/unpi/constants.d.ts.map +0 -1
  755. package/dist/adapter/z-stack/unpi/constants.js +0 -40
  756. package/dist/adapter/z-stack/unpi/constants.js.map +0 -1
  757. package/dist/adapter/z-stack/unpi/frame.d.ts +0 -17
  758. package/dist/adapter/z-stack/unpi/frame.d.ts.map +0 -1
  759. package/dist/adapter/z-stack/unpi/frame.js +0 -55
  760. package/dist/adapter/z-stack/unpi/frame.js.map +0 -1
  761. package/dist/adapter/z-stack/unpi/index.d.ts +0 -6
  762. package/dist/adapter/z-stack/unpi/index.d.ts.map +0 -1
  763. package/dist/adapter/z-stack/unpi/index.js +0 -38
  764. package/dist/adapter/z-stack/unpi/index.js.map +0 -1
  765. package/dist/adapter/z-stack/unpi/parser.d.ts +0 -13
  766. package/dist/adapter/z-stack/unpi/parser.d.ts.map +0 -1
  767. package/dist/adapter/z-stack/unpi/parser.js +0 -86
  768. package/dist/adapter/z-stack/unpi/parser.js.map +0 -1
  769. package/dist/adapter/z-stack/unpi/writer.d.ts +0 -12
  770. package/dist/adapter/z-stack/unpi/writer.d.ts.map +0 -1
  771. package/dist/adapter/z-stack/unpi/writer.js +0 -55
  772. package/dist/adapter/z-stack/unpi/writer.js.map +0 -1
  773. package/dist/adapter/z-stack/utils/channel-list.d.ts +0 -21
  774. package/dist/adapter/z-stack/utils/channel-list.d.ts.map +0 -1
  775. package/dist/adapter/z-stack/utils/channel-list.js +0 -41
  776. package/dist/adapter/z-stack/utils/channel-list.js.map +0 -1
  777. package/dist/adapter/z-stack/utils/index.d.ts +0 -3
  778. package/dist/adapter/z-stack/utils/index.d.ts.map +0 -1
  779. package/dist/adapter/z-stack/utils/index.js +0 -19
  780. package/dist/adapter/z-stack/utils/index.js.map +0 -1
  781. package/dist/adapter/z-stack/utils/network-options.d.ts +0 -9
  782. package/dist/adapter/z-stack/utils/network-options.d.ts.map +0 -1
  783. package/dist/adapter/z-stack/utils/network-options.js +0 -23
  784. package/dist/adapter/z-stack/utils/network-options.js.map +0 -1
  785. package/dist/adapter/z-stack/znp/buffaloZnp.d.ts +0 -14
  786. package/dist/adapter/z-stack/znp/buffaloZnp.d.ts.map +0 -1
  787. package/dist/adapter/z-stack/znp/buffaloZnp.js +0 -243
  788. package/dist/adapter/z-stack/znp/buffaloZnp.js.map +0 -1
  789. package/dist/adapter/z-stack/znp/definition.d.ts +0 -6
  790. package/dist/adapter/z-stack/znp/definition.d.ts.map +0 -1
  791. package/dist/adapter/z-stack/znp/definition.js +0 -3052
  792. package/dist/adapter/z-stack/znp/definition.js.map +0 -1
  793. package/dist/adapter/z-stack/znp/index.d.ts +0 -4
  794. package/dist/adapter/z-stack/znp/index.d.ts.map +0 -1
  795. package/dist/adapter/z-stack/znp/index.js +0 -11
  796. package/dist/adapter/z-stack/znp/index.js.map +0 -1
  797. package/dist/adapter/z-stack/znp/parameterType.d.ts +0 -23
  798. package/dist/adapter/z-stack/znp/parameterType.d.ts.map +0 -1
  799. package/dist/adapter/z-stack/znp/parameterType.js +0 -26
  800. package/dist/adapter/z-stack/znp/parameterType.js.map +0 -1
  801. package/dist/adapter/z-stack/znp/tstype.d.ts +0 -23
  802. package/dist/adapter/z-stack/znp/tstype.d.ts.map +0 -1
  803. package/dist/adapter/z-stack/znp/tstype.js +0 -3
  804. package/dist/adapter/z-stack/znp/tstype.js.map +0 -1
  805. package/dist/adapter/z-stack/znp/znp.d.ts +0 -47
  806. package/dist/adapter/z-stack/znp/znp.d.ts.map +0 -1
  807. package/dist/adapter/z-stack/znp/znp.js +0 -322
  808. package/dist/adapter/z-stack/znp/znp.js.map +0 -1
  809. package/dist/adapter/z-stack/znp/zpiObject.d.ts +0 -20
  810. package/dist/adapter/z-stack/znp/zpiObject.d.ts.map +0 -1
  811. package/dist/adapter/z-stack/znp/zpiObject.js +0 -103
  812. package/dist/adapter/z-stack/znp/zpiObject.js.map +0 -1
  813. package/dist/adapter/zigate/adapter/index.d.ts +0 -3
  814. package/dist/adapter/zigate/adapter/index.d.ts.map +0 -1
  815. package/dist/adapter/zigate/adapter/index.js +0 -11
  816. package/dist/adapter/zigate/adapter/index.js.map +0 -1
  817. package/dist/adapter/zigate/adapter/zigateAdapter.d.ts +0 -72
  818. package/dist/adapter/zigate/adapter/zigateAdapter.js +0 -681
  819. package/dist/adapter/zigate/adapter/zigateAdapter.js.map +0 -1
  820. package/dist/adapter/zigate/driver/buffaloZiGate.d.ts +0 -18
  821. package/dist/adapter/zigate/driver/commandType.d.ts +0 -43
  822. package/dist/adapter/zigate/driver/commandType.d.ts.map +0 -1
  823. package/dist/adapter/zigate/driver/commandType.js +0 -390
  824. package/dist/adapter/zigate/driver/commandType.js.map +0 -1
  825. package/dist/adapter/zigate/driver/constants.d.ts +0 -277
  826. package/dist/adapter/zigate/driver/constants.d.ts.map +0 -1
  827. package/dist/adapter/zigate/driver/constants.js +0 -372
  828. package/dist/adapter/zigate/driver/constants.js.map +0 -1
  829. package/dist/adapter/zigate/driver/frame.d.ts +0 -27
  830. package/dist/adapter/zigate/driver/frame.d.ts.map +0 -1
  831. package/dist/adapter/zigate/driver/frame.js +0 -173
  832. package/dist/adapter/zigate/driver/frame.js.map +0 -1
  833. package/dist/adapter/zigate/driver/messageType.d.ts +0 -13
  834. package/dist/adapter/zigate/driver/messageType.d.ts.map +0 -1
  835. package/dist/adapter/zigate/driver/messageType.js +0 -284
  836. package/dist/adapter/zigate/driver/messageType.js.map +0 -1
  837. package/dist/adapter/zigate/driver/parameterType.d.ts +0 -28
  838. package/dist/adapter/zigate/driver/parameterType.d.ts.map +0 -1
  839. package/dist/adapter/zigate/driver/parameterType.js +0 -33
  840. package/dist/adapter/zigate/driver/parameterType.js.map +0 -1
  841. package/dist/adapter/zigate/driver/ziGateObject.d.ts +0 -24
  842. package/dist/adapter/zigate/driver/ziGateObject.js +0 -111
  843. package/dist/adapter/zigate/driver/ziGateObject.js.map +0 -1
  844. package/dist/adapter/zigate/driver/zigate.d.ts +0 -50
  845. package/dist/adapter/zigate/driver/zigate.js +0 -289
  846. package/dist/adapter/zigate/driver/zigate.js.map +0 -1
  847. package/dist/buffalo/buffalo.d.ts +0 -55
  848. package/dist/buffalo/buffalo.d.ts.map +0 -1
  849. package/dist/buffalo/buffalo.js +0 -230
  850. package/dist/buffalo/buffalo.js.map +0 -1
  851. package/dist/buffalo/index.d.ts +0 -3
  852. package/dist/buffalo/index.d.ts.map +0 -1
  853. package/dist/buffalo/index.js +0 -9
  854. package/dist/buffalo/index.js.map +0 -1
  855. package/dist/controller/controller.d.ts +0 -119
  856. package/dist/controller/database.d.ts +0 -20
  857. package/dist/controller/database.js +0 -94
  858. package/dist/controller/database.js.map +0 -1
  859. package/dist/controller/events.d.ts +0 -59
  860. package/dist/controller/greenPower.d.ts +0 -14
  861. package/dist/controller/helpers/index.d.ts +0 -3
  862. package/dist/controller/helpers/index.js +0 -29
  863. package/dist/controller/helpers/index.js.map +0 -1
  864. package/dist/controller/helpers/request.d.ts +0 -22
  865. package/dist/controller/helpers/request.js +0 -78
  866. package/dist/controller/helpers/request.js.map +0 -1
  867. package/dist/controller/helpers/requestQueue.d.ts +0 -13
  868. package/dist/controller/helpers/requestQueue.js +0 -106
  869. package/dist/controller/helpers/requestQueue.js.map +0 -1
  870. package/dist/controller/helpers/zclFrameConverter.d.ts +0 -9
  871. package/dist/controller/helpers/zclTransactionSequenceNumber.d.ts +0 -6
  872. package/dist/controller/helpers/zclTransactionSequenceNumber.d.ts.map +0 -1
  873. package/dist/controller/helpers/zclTransactionSequenceNumber.js +0 -14
  874. package/dist/controller/helpers/zclTransactionSequenceNumber.js.map +0 -1
  875. package/dist/controller/index.d.ts +0 -6
  876. package/dist/controller/index.js +0 -9
  877. package/dist/controller/index.js.map +0 -1
  878. package/dist/controller/model/device.d.ts +0 -140
  879. package/dist/controller/model/endpoint.d.ts +0 -134
  880. package/dist/controller/model/entity.d.ts +0 -15
  881. package/dist/controller/model/entity.js +0 -27
  882. package/dist/controller/model/entity.js.map +0 -1
  883. package/dist/controller/model/group.d.ts +0 -39
  884. package/dist/controller/model/index.d.ts +0 -6
  885. package/dist/controller/model/index.js +0 -15
  886. package/dist/controller/model/index.js.map +0 -1
  887. package/dist/controller/model/konnextConfig.d.ts +0 -7
  888. package/dist/controller/model/konnextConfig.d.ts.map +0 -1
  889. package/dist/controller/model/konnextConfig.js +0 -3
  890. package/dist/controller/model/konnextConfig.js.map +0 -1
  891. package/dist/controller/touchlink.d.ts +0 -20
  892. package/dist/controller/touchlink.js +0 -157
  893. package/dist/controller/touchlink.js.map +0 -1
  894. package/dist/controller/tstype.d.ts +0 -21
  895. package/dist/index.d.ts +0 -6
  896. package/dist/index.js +0 -37
  897. package/dist/index.js.map +0 -1
  898. package/dist/models/backup-storage-legacy.d.ts +0 -27
  899. package/dist/models/backup-storage-legacy.d.ts.map +0 -1
  900. package/dist/models/backup-storage-legacy.js +0 -3
  901. package/dist/models/backup-storage-legacy.js.map +0 -1
  902. package/dist/models/backup-storage-unified.d.ts +0 -50
  903. package/dist/models/backup-storage-unified.d.ts.map +0 -1
  904. package/dist/models/backup-storage-unified.js +0 -3
  905. package/dist/models/backup-storage-unified.js.map +0 -1
  906. package/dist/models/backup.d.ts +0 -38
  907. package/dist/models/backup.d.ts.map +0 -1
  908. package/dist/models/backup.js +0 -3
  909. package/dist/models/backup.js.map +0 -1
  910. package/dist/models/index.d.ts +0 -5
  911. package/dist/models/index.d.ts.map +0 -1
  912. package/dist/models/index.js +0 -21
  913. package/dist/models/index.js.map +0 -1
  914. package/dist/models/network-options.d.ts +0 -13
  915. package/dist/models/network-options.d.ts.map +0 -1
  916. package/dist/models/network-options.js +0 -3
  917. package/dist/models/network-options.js.map +0 -1
  918. package/dist/utils/aes.d.ts +0 -40
  919. package/dist/utils/aes.d.ts.map +0 -1
  920. package/dist/utils/aes.js +0 -198
  921. package/dist/utils/aes.js.map +0 -1
  922. package/dist/utils/assertString.d.ts +0 -3
  923. package/dist/utils/assertString.d.ts.map +0 -1
  924. package/dist/utils/assertString.js +0 -9
  925. package/dist/utils/assertString.js.map +0 -1
  926. package/dist/utils/backup.d.ts +0 -21
  927. package/dist/utils/backup.d.ts.map +0 -1
  928. package/dist/utils/backup.js +0 -190
  929. package/dist/utils/backup.js.map +0 -1
  930. package/dist/utils/equalsPartial.d.ts +0 -3
  931. package/dist/utils/equalsPartial.d.ts.map +0 -1
  932. package/dist/utils/equalsPartial.js +0 -12
  933. package/dist/utils/equalsPartial.js.map +0 -1
  934. package/dist/utils/index.d.ts +0 -10
  935. package/dist/utils/index.d.ts.map +0 -1
  936. package/dist/utils/index.js +0 -46
  937. package/dist/utils/index.js.map +0 -1
  938. package/dist/utils/isNumberArray.d.ts +0 -3
  939. package/dist/utils/isNumberArray.d.ts.map +0 -1
  940. package/dist/utils/isNumberArray.js +0 -7
  941. package/dist/utils/isNumberArray.js.map +0 -1
  942. package/dist/utils/logger.d.ts +0 -9
  943. package/dist/utils/logger.d.ts.map +0 -1
  944. package/dist/utils/logger.js +0 -14
  945. package/dist/utils/logger.js.map +0 -1
  946. package/dist/utils/queue.d.ts +0 -12
  947. package/dist/utils/queue.d.ts.map +0 -1
  948. package/dist/utils/queue.js +0 -62
  949. package/dist/utils/queue.js.map +0 -1
  950. package/dist/utils/realpathSync.d.ts +0 -3
  951. package/dist/utils/realpathSync.d.ts.map +0 -1
  952. package/dist/utils/realpathSync.js +0 -13
  953. package/dist/utils/realpathSync.js.map +0 -1
  954. package/dist/utils/wait.d.ts +0 -3
  955. package/dist/utils/wait.d.ts.map +0 -1
  956. package/dist/utils/wait.js +0 -9
  957. package/dist/utils/wait.js.map +0 -1
  958. package/dist/utils/waitress.d.ts +0 -22
  959. package/dist/utils/waitress.d.ts.map +0 -1
  960. package/dist/utils/waitress.js +0 -69
  961. package/dist/utils/waitress.js.map +0 -1
  962. package/dist/zspec/consts.d.ts +0 -60
  963. package/dist/zspec/consts.d.ts.map +0 -1
  964. package/dist/zspec/consts.js +0 -64
  965. package/dist/zspec/consts.js.map +0 -1
  966. package/dist/zspec/enums.d.ts +0 -19
  967. package/dist/zspec/enums.d.ts.map +0 -1
  968. package/dist/zspec/enums.js +0 -28
  969. package/dist/zspec/enums.js.map +0 -1
  970. package/dist/zspec/index.d.ts +0 -4
  971. package/dist/zspec/index.d.ts.map +0 -1
  972. package/dist/zspec/index.js +0 -43
  973. package/dist/zspec/index.js.map +0 -1
  974. package/dist/zspec/tstypes.d.ts +0 -19
  975. package/dist/zspec/tstypes.d.ts.map +0 -1
  976. package/dist/zspec/tstypes.js +0 -3
  977. package/dist/zspec/tstypes.js.map +0 -1
  978. package/dist/zspec/utils.d.ts +0 -14
  979. package/dist/zspec/utils.d.ts.map +0 -1
  980. package/dist/zspec/utils.js +0 -29
  981. package/dist/zspec/utils.js.map +0 -1
  982. package/dist/zspec/zcl/buffaloZcl.d.ts +0 -55
  983. package/dist/zspec/zcl/definition/cluster.d.ts +0 -3
  984. package/dist/zspec/zcl/definition/consts.d.ts +0 -9
  985. package/dist/zspec/zcl/definition/consts.d.ts.map +0 -1
  986. package/dist/zspec/zcl/definition/consts.js +0 -27
  987. package/dist/zspec/zcl/definition/consts.js.map +0 -1
  988. package/dist/zspec/zcl/definition/enums.d.ts +0 -177
  989. package/dist/zspec/zcl/definition/enums.d.ts.map +0 -1
  990. package/dist/zspec/zcl/definition/enums.js +0 -187
  991. package/dist/zspec/zcl/definition/enums.js.map +0 -1
  992. package/dist/zspec/zcl/definition/foundation.d.ts +0 -11
  993. package/dist/zspec/zcl/definition/manufacturerCode.d.ts +0 -727
  994. package/dist/zspec/zcl/definition/manufacturerCode.d.ts.map +0 -1
  995. package/dist/zspec/zcl/definition/manufacturerCode.js +0 -733
  996. package/dist/zspec/zcl/definition/manufacturerCode.js.map +0 -1
  997. package/dist/zspec/zcl/definition/status.d.ts +0 -69
  998. package/dist/zspec/zcl/definition/status.d.ts.map +0 -1
  999. package/dist/zspec/zcl/definition/status.js +0 -74
  1000. package/dist/zspec/zcl/definition/status.js.map +0 -1
  1001. package/dist/zspec/zcl/index.d.ts +0 -11
  1002. package/dist/zspec/zcl/utils.d.ts +0 -7
  1003. package/dist/zspec/zcl/zclFrame.d.ts +0 -36
  1004. package/dist/zspec/zcl/zclHeader.d.ts +0 -17
  1005. package/dist/zspec/zcl/zclStatusError.d.ts +0 -6
  1006. package/dist/zspec/zcl/zclStatusError.d.ts.map +0 -1
  1007. package/dist/zspec/zcl/zclStatusError.js +0 -13
  1008. package/dist/zspec/zcl/zclStatusError.js.map +0 -1
  1009. package/dist/zspec/zdo/buffaloZdo.d.ts +0 -438
  1010. package/dist/zspec/zdo/buffaloZdo.d.ts.map +0 -1
  1011. package/dist/zspec/zdo/buffaloZdo.js +0 -1892
  1012. package/dist/zspec/zdo/buffaloZdo.js.map +0 -1
  1013. package/dist/zspec/zdo/definition/clusters.d.ts +0 -624
  1014. package/dist/zspec/zdo/definition/clusters.d.ts.map +0 -1
  1015. package/dist/zspec/zdo/definition/clusters.js +0 -687
  1016. package/dist/zspec/zdo/definition/clusters.js.map +0 -1
  1017. package/dist/zspec/zdo/definition/consts.d.ts +0 -13
  1018. package/dist/zspec/zdo/definition/consts.d.ts.map +0 -1
  1019. package/dist/zspec/zdo/definition/consts.js +0 -16
  1020. package/dist/zspec/zdo/definition/consts.js.map +0 -1
  1021. package/dist/zspec/zdo/definition/enums.d.ts +0 -75
  1022. package/dist/zspec/zdo/definition/enums.d.ts.map +0 -1
  1023. package/dist/zspec/zdo/definition/enums.js +0 -97
  1024. package/dist/zspec/zdo/definition/enums.js.map +0 -1
  1025. package/dist/zspec/zdo/definition/status.d.ts +0 -99
  1026. package/dist/zspec/zdo/definition/status.d.ts.map +0 -1
  1027. package/dist/zspec/zdo/definition/status.js +0 -109
  1028. package/dist/zspec/zdo/definition/status.js.map +0 -1
  1029. package/dist/zspec/zdo/definition/tstypes.d.ts +0 -787
  1030. package/dist/zspec/zdo/definition/tstypes.d.ts.map +0 -1
  1031. package/dist/zspec/zdo/definition/tstypes.js +0 -3
  1032. package/dist/zspec/zdo/definition/tstypes.js.map +0 -1
  1033. package/dist/zspec/zdo/index.d.ts +0 -7
  1034. package/dist/zspec/zdo/index.d.ts.map +0 -1
  1035. package/dist/zspec/zdo/index.js +0 -39
  1036. package/dist/zspec/zdo/index.js.map +0 -1
  1037. package/dist/zspec/zdo/utils.d.ts +0 -25
  1038. package/dist/zspec/zdo/utils.d.ts.map +0 -1
  1039. package/dist/zspec/zdo/utils.js +0 -75
  1040. package/dist/zspec/zdo/utils.js.map +0 -1
  1041. package/dist/zspec/zdo/zdoStatusError.d.ts +0 -6
  1042. package/dist/zspec/zdo/zdoStatusError.d.ts.map +0 -1
  1043. package/dist/zspec/zdo/zdoStatusError.js +0 -13
  1044. package/dist/zspec/zdo/zdoStatusError.js.map +0 -1
  1045. package/typedoc-tsconfig.json +0 -44
@@ -0,0 +1,3447 @@
1
+ import {existsSync, mkdirSync, unlinkSync, writeFileSync} from "node:fs";
2
+ import path from "node:path";
3
+ import {EventEmitter} from "node:stream";
4
+ import {afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
5
+ import type {TsType} from "../../../src/adapter";
6
+ import {
7
+ DEFAULT_APS_OPTIONS,
8
+ DEFAULT_STACK_CONFIG,
9
+ EmberAdapter,
10
+ type LinkKeyBackupData,
11
+ type NetworkCache,
12
+ } from "../../../src/adapter/ember/adapter/emberAdapter";
13
+ import {FIXED_ENDPOINTS} from "../../../src/adapter/ember/adapter/endpoints";
14
+ import {OneWaitressEvents} from "../../../src/adapter/ember/adapter/oneWaitress";
15
+ import {EMBER_LOW_RAM_CONCENTRATOR, SECURITY_LEVEL_Z3} from "../../../src/adapter/ember/consts";
16
+ import {
17
+ EmberApsOption,
18
+ EmberDeviceUpdate,
19
+ EmberIncomingMessageType,
20
+ EmberJoinDecision,
21
+ EmberJoinMethod,
22
+ EmberKeyStructBitmask,
23
+ EmberNetworkStatus,
24
+ EmberNodeType,
25
+ EmberOutgoingMessageType,
26
+ EmberVersionType,
27
+ EzspStatus,
28
+ IEEE802154CcaMode,
29
+ SecManDerivedKeyType,
30
+ SecManFlag,
31
+ SecManKeyType,
32
+ SLStatus,
33
+ } from "../../../src/adapter/ember/enums";
34
+ import {EZSP_MIN_PROTOCOL_VERSION, EZSP_PROTOCOL_VERSION, EZSP_STACK_TYPE_MESH} from "../../../src/adapter/ember/ezsp/consts";
35
+ import {EzspConfigId, EzspDecisionBitmask, EzspEndpointFlag, EzspPolicyId, EzspValueId} from "../../../src/adapter/ember/ezsp/enums";
36
+ import type {EmberEzspEventMap} from "../../../src/adapter/ember/ezsp/ezsp";
37
+ import {EzspError} from "../../../src/adapter/ember/ezspError";
38
+ import type {
39
+ EmberApsFrame,
40
+ EmberMulticastTableEntry,
41
+ EmberNetworkInitStruct,
42
+ EmberNetworkParameters,
43
+ EmberVersion,
44
+ SecManAPSKeyMetadata,
45
+ SecManContext,
46
+ SecManKey,
47
+ SecManNetworkKeyInfo,
48
+ } from "../../../src/adapter/ember/types";
49
+ import {lowHighBytes} from "../../../src/adapter/ember/utils/math";
50
+ import type {DeviceJoinedPayload, DeviceLeavePayload, ZclPayload} from "../../../src/adapter/events";
51
+ import type {AdapterOptions, NetworkOptions, SerialPortOptions} from "../../../src/adapter/tstype";
52
+ import type {Backup} from "../../../src/models/backup";
53
+ import type {UnifiedBackupStorage} from "../../../src/models/backup-storage-unified";
54
+ import {logger} from "../../../src/utils/logger";
55
+ import * as ZSpec from "../../../src/zspec";
56
+ import type {Eui64, NodeId, PanId} from "../../../src/zspec/tstypes";
57
+ import * as Zcl from "../../../src/zspec/zcl";
58
+ import * as Zdo from "../../../src/zspec/zdo";
59
+ import type * as ZdoTypes from "../../../src/zspec/zdo/definition/tstypes";
60
+
61
+ // https://github.com/jestjs/jest/issues/6028#issuecomment-567669082
62
+ function defuseRejection<T>(promise: Promise<T>) {
63
+ promise.catch(() => {});
64
+
65
+ return promise;
66
+ }
67
+
68
+ function deepClone<T>(obj: T): T {
69
+ return JSON.parse(JSON.stringify(obj));
70
+ }
71
+
72
+ function reverseApsFrame(apsFrame: EmberApsFrame): EmberApsFrame {
73
+ return Object.assign({}, apsFrame, {sourceEndpoint: apsFrame.destinationEndpoint, destinationEndpoint: apsFrame.sourceEndpoint});
74
+ }
75
+
76
+ async function flushPromises(): Promise<void> {
77
+ const {setImmediate} = await vi.importActual<typeof import("node:timers")>("node:timers");
78
+ return new Promise(setImmediate);
79
+ }
80
+
81
+ const TEMP_PATH = path.resolve("temp");
82
+ const STACK_CONFIG_PATH = path.join(TEMP_PATH, "stack_config.json");
83
+ const DEFAULT_NETWORK_OPTIONS: Readonly<NetworkOptions> = {
84
+ panID: 24404,
85
+ extendedPanID: [118, 185, 136, 236, 199, 244, 246, 85],
86
+ channelList: [20],
87
+ networkKey: [72, 97, 39, 230, 92, 72, 101, 148, 64, 225, 250, 214, 195, 31, 105, 71],
88
+ networkKeyDistribute: false,
89
+ };
90
+ const DEFAULT_SERIAL_PORT_OPTIONS: Readonly<SerialPortOptions> = {
91
+ baudRate: 115200,
92
+ rtscts: false,
93
+ path: "MOCK",
94
+ adapter: "ember",
95
+ };
96
+ const DEFAULT_ADAPTER_OPTIONS: Readonly<AdapterOptions> = {
97
+ concurrent: 16,
98
+ disableLED: false,
99
+ };
100
+ const DEFAULT_BACKUP: Readonly<UnifiedBackupStorage> = {
101
+ metadata: {
102
+ format: "zigpy/open-coordinator-backup",
103
+ version: 1,
104
+ source: "zigbee-herdsman@0.55.0",
105
+ internal: {
106
+ date: "2024-07-19T15:57:15.163Z",
107
+ ezspVersion: 13,
108
+ },
109
+ },
110
+ stack_specific: {
111
+ ezsp: {
112
+ hashed_tclk: "da85e5bac80c8a958b14d44f14c2ba16",
113
+ },
114
+ },
115
+ coordinator_ieee: "1122334455667788",
116
+ pan_id: "5f54",
117
+ extended_pan_id: "76b988ecc7f4f655",
118
+ nwk_update_id: 0,
119
+ security_level: 5,
120
+ channel: 20,
121
+ channel_mask: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26],
122
+ network_key: {
123
+ key: "486127e65c48659440e1fad6c31f6947",
124
+ sequence_number: 0,
125
+ frame_counter: 16434,
126
+ },
127
+ devices: [],
128
+ };
129
+ const DEFAULT_COORDINATOR_IEEE: Eui64 = ZSpec.Utils.eui64LEBufferToHex(Buffer.from(DEFAULT_BACKUP.coordinator_ieee, "hex"));
130
+ const DEFAULT_ADAPTER_NETWORK_PARAMETERS: EmberNetworkParameters = {
131
+ extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
132
+ panId: DEFAULT_NETWORK_OPTIONS.panID,
133
+ radioTxPower: 5,
134
+ radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
135
+ joinMethod: 0,
136
+ nwkManagerId: 0,
137
+ nwkUpdateId: 0,
138
+ channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
139
+ };
140
+
141
+ let mockManufCode = Zcl.ManufacturerCode.SILICON_LABORATORIES;
142
+ let mockAPSSequence = -1; // start at 0
143
+ let mockMessageTag = -1; // start at 0
144
+ let mockEzspEmitter = new EventEmitter<EmberEzspEventMap>();
145
+ const mockEzspRemoveAllListeners = vi.fn().mockImplementation((e) => {
146
+ mockEzspEmitter.removeAllListeners(e);
147
+ });
148
+ const mockEzspOn = vi.fn().mockImplementation((e, l) => {
149
+ mockEzspEmitter.on(e, l);
150
+ });
151
+ const mockEzspOnce = vi.fn().mockImplementation((e, l) => {
152
+ mockEzspEmitter.once(e, l);
153
+ });
154
+ const mockEzspStart = vi.fn().mockResolvedValue(EzspStatus.SUCCESS);
155
+ const mockEzspStop = vi.fn();
156
+
157
+ const mockEzspSend = vi.fn().mockResolvedValue([SLStatus.OK, ++mockMessageTag]);
158
+ const mockEzspSetMulticastTableEntry = vi.fn().mockResolvedValue(SLStatus.OK);
159
+ const mockEzspSetManufacturerCode = vi.fn().mockImplementation((code) => {
160
+ mockManufCode = code;
161
+ });
162
+ const mockEzspReadAndClearCounters = vi.fn().mockResolvedValue([1, 2, 3, 4]); // not matching EmberCounterType, but doesn't matter here
163
+ const mockEzspGetNetworkParameters = vi
164
+ .fn()
165
+ .mockResolvedValue([SLStatus.OK, EmberNodeType.COORDINATOR, deepClone(DEFAULT_ADAPTER_NETWORK_PARAMETERS)]);
166
+ const mockEzspNetworkState = vi.fn().mockResolvedValue(EmberNetworkStatus.JOINED_NETWORK);
167
+ const mockEzspGetEui64 = vi.fn().mockResolvedValue(DEFAULT_COORDINATOR_IEEE);
168
+ const mockEzspSetConcentrator = vi.fn().mockResolvedValue(SLStatus.OK);
169
+ const mockEzspSetSourceRouteDiscoveryMode = vi.fn().mockResolvedValue(1240 /* ms */);
170
+ const mockEzspSetRadioIeee802154CcaMode = vi.fn().mockResolvedValue(SLStatus.OK);
171
+ // not OK by default since used to detected unreged EP
172
+ const mockEzspGetEndpointFlags = vi.fn().mockResolvedValue([SLStatus.NOT_FOUND, EzspEndpointFlag.DISABLED]);
173
+ const mockEzspAddEndpoint = vi.fn().mockResolvedValue(SLStatus.OK);
174
+ const mockEzspNetworkInit = vi.fn().mockImplementation((_networkInitStruct: EmberNetworkInitStruct) => {
175
+ setTimeout(async () => {
176
+ mockEzspEmitter.emit("stackStatus", SLStatus.NETWORK_UP);
177
+ await flushPromises();
178
+ }, 300);
179
+
180
+ return SLStatus.OK;
181
+ });
182
+ const mockEzspExportKey = vi.fn().mockImplementation((context: SecManContext) => {
183
+ switch (context.coreKeyType) {
184
+ case SecManKeyType.NETWORK: {
185
+ return [SLStatus.OK, {contents: Buffer.from(DEFAULT_BACKUP.network_key.key, "hex")} as SecManKey];
186
+ }
187
+ case SecManKeyType.TC_LINK: {
188
+ return [SLStatus.OK, {contents: Buffer.from(DEFAULT_BACKUP.stack_specific!.ezsp!.hashed_tclk!, "hex")} as SecManKey];
189
+ }
190
+ }
191
+ });
192
+ const mockEzspLeaveNetwork = vi.fn().mockImplementation(() => {
193
+ setTimeout(async () => {
194
+ mockEzspEmitter.emit("stackStatus", SLStatus.NETWORK_DOWN);
195
+ await flushPromises();
196
+ }, 300);
197
+
198
+ return SLStatus.OK;
199
+ });
200
+ const mockEzspSetInitialSecurityState = vi.fn().mockResolvedValue(SLStatus.OK);
201
+ const mockEzspSetExtendedSecurityBitmask = vi.fn().mockResolvedValue(SLStatus.OK);
202
+ const mockEzspClearKeyTable = vi.fn().mockResolvedValue(SLStatus.OK);
203
+ const mockEzspFormNetwork = vi.fn().mockImplementation((_parameters: EmberNetworkParameters) => {
204
+ setTimeout(async () => {
205
+ mockEzspEmitter.emit("stackStatus", SLStatus.NETWORK_UP);
206
+ await flushPromises();
207
+ }, 300);
208
+
209
+ return SLStatus.OK;
210
+ });
211
+ const mockEzspStartWritingStackTokens = vi.fn().mockResolvedValue(SLStatus.OK);
212
+ const mockEzspGetConfigurationValue = vi.fn().mockImplementation((config: EzspConfigId) => {
213
+ switch (config) {
214
+ case EzspConfigId.KEY_TABLE_SIZE: {
215
+ return [SLStatus.OK, 0];
216
+ }
217
+ }
218
+ });
219
+ const mockEzspExportLinkKeyByIndex = vi.fn();
220
+ const mockEzspEraseKeyTableEntry = vi.fn().mockResolvedValue(SLStatus.OK);
221
+ const mockEzspImportLinkKey = vi.fn().mockResolvedValue(SLStatus.OK);
222
+ const mockEzspBroadcastNextNetworkKey = vi.fn().mockResolvedValue(SLStatus.OK);
223
+ const mockEzspBroadcastNetworkKeySwitch = vi.fn().mockResolvedValue(SLStatus.OK);
224
+ const mockEzspStartScan = vi.fn().mockResolvedValue(SLStatus.OK);
225
+ const mockEzspVersion = vi.fn().mockImplementation((version: number) => [version, EZSP_STACK_TYPE_MESH, 0]);
226
+ const mockEzspSetProtocolVersion = vi.fn();
227
+ const mockEzspGetVersionStruct = vi.fn().mockResolvedValue([
228
+ SLStatus.OK,
229
+ {
230
+ build: 135,
231
+ major: 8,
232
+ minor: 0,
233
+ patch: 0,
234
+ special: 0,
235
+ type: EmberVersionType.GA,
236
+ } as EmberVersion,
237
+ ]);
238
+ const mockEzspSetConfigurationValue = vi.fn().mockResolvedValue(SLStatus.OK);
239
+ const mockEzspSetValue = vi.fn().mockResolvedValue(SLStatus.OK);
240
+ const mockEzspSetPolicy = vi.fn().mockResolvedValue(SLStatus.OK);
241
+ const mockEzspPermitJoining = vi.fn().mockImplementation((duration: number) => {
242
+ setTimeout(async () => {
243
+ mockEzspEmitter.emit("stackStatus", duration > 0 ? SLStatus.ZIGBEE_NETWORK_OPENED : SLStatus.ZIGBEE_NETWORK_CLOSED);
244
+ await flushPromises();
245
+ }, 300);
246
+
247
+ return SLStatus.OK;
248
+ });
249
+ const mockEzspSendBroadcast = vi.fn().mockResolvedValue([SLStatus.OK, ++mockAPSSequence]);
250
+ const mockEzspSendUnicast = vi.fn().mockResolvedValue([SLStatus.OK, ++mockAPSSequence]);
251
+ const mockEzspGetNetworkKeyInfo = vi.fn().mockResolvedValue([
252
+ SLStatus.OK,
253
+ {
254
+ networkKeySet: true,
255
+ alternateNetworkKeySet: false,
256
+ networkKeySequenceNumber: DEFAULT_BACKUP.network_key.sequence_number,
257
+ altNetworkKeySequenceNumber: 0,
258
+ networkKeyFrameCounter: DEFAULT_BACKUP.network_key.frame_counter,
259
+ } as SecManNetworkKeyInfo,
260
+ ]);
261
+ const mockEzspGetApsKeyInfo = vi.fn().mockResolvedValue([
262
+ SLStatus.OK,
263
+ {
264
+ bitmask: EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
265
+ outgoingFrameCounter: 456,
266
+ incomingFrameCounter: 0,
267
+ ttlInSeconds: 0,
268
+ } as SecManAPSKeyMetadata,
269
+ ]);
270
+ const mockEzspSetRadioPower = vi.fn().mockResolvedValue(SLStatus.OK);
271
+ const mockEzspImportTransientKey = vi.fn().mockResolvedValue(SLStatus.OK);
272
+ const mockEzspClearTransientLinkKeys = vi.fn().mockResolvedValue(SLStatus.OK);
273
+ const mockEzspSetLogicalAndRadioChannel = vi.fn().mockResolvedValue(SLStatus.OK);
274
+ const mockEzspSendRawMessage = vi.fn().mockResolvedValue(SLStatus.OK);
275
+ const mockEzspSetNWKFrameCounter = vi.fn().mockResolvedValue(SLStatus.OK);
276
+ const mockEzspSetAPSFrameCounter = vi.fn().mockResolvedValue(SLStatus.OK);
277
+
278
+ vi.mock("../../../src/adapter/ember/uart/ash");
279
+
280
+ vi.mock("../../../src/adapter/ember/ezsp/ezsp", async (importOriginal) => ({
281
+ ...(await importOriginal()),
282
+ Ezsp: vi.fn(() => ({
283
+ removeAllListeners: mockEzspRemoveAllListeners,
284
+ on: mockEzspOn,
285
+ once: mockEzspOnce,
286
+
287
+ // only functions called from adapter
288
+ ash: {readAndClearCounters: vi.fn().mockReturnValue([9, 8, 7])},
289
+
290
+ start: mockEzspStart,
291
+ stop: mockEzspStop,
292
+ send: mockEzspSend,
293
+ ezspSetMulticastTableEntry: mockEzspSetMulticastTableEntry,
294
+ ezspSetManufacturerCode: mockEzspSetManufacturerCode,
295
+ ezspReadAndClearCounters: mockEzspReadAndClearCounters,
296
+ ezspGetNetworkParameters: mockEzspGetNetworkParameters,
297
+ ezspNetworkState: mockEzspNetworkState,
298
+ ezspGetEui64: mockEzspGetEui64,
299
+ ezspSetConcentrator: mockEzspSetConcentrator,
300
+ ezspSetSourceRouteDiscoveryMode: mockEzspSetSourceRouteDiscoveryMode,
301
+ ezspSetRadioIeee802154CcaMode: mockEzspSetRadioIeee802154CcaMode,
302
+ ezspGetEndpointFlags: mockEzspGetEndpointFlags,
303
+ ezspAddEndpoint: mockEzspAddEndpoint,
304
+ ezspNetworkInit: mockEzspNetworkInit,
305
+ ezspExportKey: mockEzspExportKey,
306
+ ezspLeaveNetwork: mockEzspLeaveNetwork,
307
+ ezspSetInitialSecurityState: mockEzspSetInitialSecurityState,
308
+ ezspSetExtendedSecurityBitmask: mockEzspSetExtendedSecurityBitmask,
309
+ ezspClearKeyTable: mockEzspClearKeyTable,
310
+ ezspFormNetwork: mockEzspFormNetwork,
311
+ ezspStartWritingStackTokens: mockEzspStartWritingStackTokens,
312
+ ezspGetConfigurationValue: mockEzspGetConfigurationValue,
313
+ ezspExportLinkKeyByIndex: mockEzspExportLinkKeyByIndex,
314
+ ezspEraseKeyTableEntry: mockEzspEraseKeyTableEntry,
315
+ ezspImportLinkKey: mockEzspImportLinkKey,
316
+ ezspBroadcastNextNetworkKey: mockEzspBroadcastNextNetworkKey,
317
+ ezspBroadcastNetworkKeySwitch: mockEzspBroadcastNetworkKeySwitch,
318
+ ezspStartScan: mockEzspStartScan,
319
+ ezspVersion: mockEzspVersion,
320
+ setProtocolVersion: mockEzspSetProtocolVersion,
321
+ ezspGetVersionStruct: mockEzspGetVersionStruct,
322
+ ezspSetConfigurationValue: mockEzspSetConfigurationValue,
323
+ ezspSetValue: mockEzspSetValue,
324
+ ezspSetPolicy: mockEzspSetPolicy,
325
+ ezspPermitJoining: mockEzspPermitJoining,
326
+ ezspSendBroadcast: mockEzspSendBroadcast,
327
+ ezspSendUnicast: mockEzspSendUnicast,
328
+ ezspGetNetworkKeyInfo: mockEzspGetNetworkKeyInfo,
329
+ ezspGetApsKeyInfo: mockEzspGetApsKeyInfo,
330
+ ezspSetRadioPower: mockEzspSetRadioPower,
331
+ ezspImportTransientKey: mockEzspImportTransientKey,
332
+ ezspClearTransientLinkKeys: mockEzspClearTransientLinkKeys,
333
+ ezspSetLogicalAndRadioChannel: mockEzspSetLogicalAndRadioChannel,
334
+ ezspSendRawMessage: mockEzspSendRawMessage,
335
+ ezspSetNWKFrameCounter: mockEzspSetNWKFrameCounter,
336
+ ezspSetAPSFrameCounter: mockEzspSetAPSFrameCounter,
337
+ })),
338
+ }));
339
+
340
+ const ezspMocks = [
341
+ mockEzspRemoveAllListeners,
342
+ mockEzspOn,
343
+ mockEzspOnce,
344
+ mockEzspStart,
345
+ mockEzspStop,
346
+ mockEzspSend,
347
+ mockEzspSetMulticastTableEntry,
348
+ mockEzspSetManufacturerCode,
349
+ mockEzspReadAndClearCounters,
350
+ mockEzspGetNetworkParameters,
351
+ mockEzspNetworkState,
352
+ mockEzspGetEui64,
353
+ mockEzspSetConcentrator,
354
+ mockEzspSetSourceRouteDiscoveryMode,
355
+ mockEzspSetRadioIeee802154CcaMode,
356
+ mockEzspGetEndpointFlags,
357
+ mockEzspAddEndpoint,
358
+ mockEzspNetworkInit,
359
+ mockEzspExportKey,
360
+ mockEzspLeaveNetwork,
361
+ mockEzspSetInitialSecurityState,
362
+ mockEzspSetExtendedSecurityBitmask,
363
+ mockEzspClearKeyTable,
364
+ mockEzspFormNetwork,
365
+ mockEzspStartWritingStackTokens,
366
+ mockEzspGetConfigurationValue,
367
+ mockEzspExportLinkKeyByIndex,
368
+ mockEzspEraseKeyTableEntry,
369
+ mockEzspImportLinkKey,
370
+ mockEzspBroadcastNextNetworkKey,
371
+ mockEzspBroadcastNetworkKeySwitch,
372
+ mockEzspStartScan,
373
+ mockEzspVersion,
374
+ mockEzspSetProtocolVersion,
375
+ mockEzspGetVersionStruct,
376
+ mockEzspSetConfigurationValue,
377
+ mockEzspSetValue,
378
+ mockEzspSetPolicy,
379
+ mockEzspPermitJoining,
380
+ mockEzspSendBroadcast,
381
+ mockEzspSendUnicast,
382
+ mockEzspGetNetworkKeyInfo,
383
+ mockEzspGetApsKeyInfo,
384
+ mockEzspSetRadioPower,
385
+ mockEzspImportTransientKey,
386
+ mockEzspClearTransientLinkKeys,
387
+ mockEzspSetLogicalAndRadioChannel,
388
+ mockEzspSendRawMessage,
389
+ mockEzspSetNWKFrameCounter,
390
+ mockEzspSetAPSFrameCounter,
391
+ ];
392
+
393
+ describe("Ember Adapter Layer", () => {
394
+ let adapter: EmberAdapter;
395
+ let backupPath: string;
396
+ const loggerSpies = {
397
+ debug: vi.spyOn(logger, "debug"),
398
+ info: vi.spyOn(logger, "info"),
399
+ warning: vi.spyOn(logger, "warning"),
400
+ error: vi.spyOn(logger, "error"),
401
+ };
402
+
403
+ const deleteCoordinatorBackup = () => {
404
+ if (existsSync(backupPath)) {
405
+ unlinkSync(backupPath);
406
+ }
407
+ };
408
+
409
+ const deleteStackConfig = () => {
410
+ if (existsSync(STACK_CONFIG_PATH)) {
411
+ unlinkSync(STACK_CONFIG_PATH);
412
+ }
413
+ };
414
+
415
+ const takeResetCodePath = () => {
416
+ deleteCoordinatorBackup();
417
+ mockEzspGetNetworkParameters.mockResolvedValueOnce([
418
+ SLStatus.OK,
419
+ EmberNodeType.COORDINATOR,
420
+ {
421
+ extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
422
+ panId: 1234,
423
+ radioTxPower: 5,
424
+ radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
425
+ joinMethod: 0,
426
+ nwkManagerId: 0,
427
+ nwkUpdateId: 0,
428
+ channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
429
+ } as EmberNetworkParameters,
430
+ ]);
431
+ };
432
+
433
+ const takeRestoredCodePath = () => {
434
+ mockEzspGetNetworkParameters.mockResolvedValueOnce([
435
+ SLStatus.OK,
436
+ EmberNodeType.COORDINATOR,
437
+ {
438
+ extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
439
+ panId: 1234,
440
+ radioTxPower: 5,
441
+ radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
442
+ joinMethod: 0,
443
+ nwkManagerId: 0,
444
+ nwkUpdateId: 0,
445
+ channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
446
+ } as EmberNetworkParameters,
447
+ ]);
448
+ };
449
+
450
+ const clearMocks = () => {
451
+ for (const mock of ezspMocks) {
452
+ mock.mockClear();
453
+ }
454
+
455
+ loggerSpies.debug.mockClear();
456
+ loggerSpies.info.mockClear();
457
+ loggerSpies.warning.mockClear();
458
+ loggerSpies.error.mockClear();
459
+ };
460
+
461
+ beforeAll(() => {
462
+ if (!existsSync(TEMP_PATH)) {
463
+ mkdirSync(TEMP_PATH);
464
+ } else {
465
+ // just in case, remove previous remnants
466
+ deleteCoordinatorBackup();
467
+ deleteStackConfig();
468
+ }
469
+ });
470
+
471
+ afterAll(() => {
472
+ deleteCoordinatorBackup();
473
+ deleteStackConfig();
474
+ });
475
+
476
+ beforeEach(() => {
477
+ vi.useFakeTimers();
478
+
479
+ backupPath = path.join(TEMP_PATH, "ember_coordinator_backup.json");
480
+
481
+ writeFileSync(backupPath, JSON.stringify(DEFAULT_BACKUP, undefined, 2));
482
+
483
+ mockManufCode = Zcl.ManufacturerCode.SILICON_LABORATORIES;
484
+ mockAPSSequence = -1;
485
+ mockMessageTag = -1;
486
+ // make sure emitter is reset too
487
+ mockEzspEmitter = new EventEmitter();
488
+
489
+ clearMocks();
490
+ });
491
+
492
+ afterEach(() => {
493
+ vi.useRealTimers();
494
+ });
495
+
496
+ it("Creates default instance", () => {
497
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
498
+
499
+ expect(adapter).toBeInstanceOf(EmberAdapter);
500
+ expect(adapter.stackConfig).toStrictEqual(DEFAULT_STACK_CONFIG);
501
+ });
502
+
503
+ it("Loads custom stack config", () => {
504
+ const config = {
505
+ CONCENTRATOR_RAM_TYPE: "low",
506
+ CONCENTRATOR_MIN_TIME: 1,
507
+ CONCENTRATOR_MAX_TIME: 31,
508
+ CONCENTRATOR_ROUTE_ERROR_THRESHOLD: 5,
509
+ CONCENTRATOR_DELIVERY_FAILURE_THRESHOLD: 2,
510
+ CONCENTRATOR_MAX_HOPS: 5,
511
+ MAX_END_DEVICE_CHILDREN: 16,
512
+ TRANSIENT_DEVICE_TIMEOUT: 1000,
513
+ END_DEVICE_POLL_TIMEOUT: 12,
514
+ TRANSIENT_KEY_TIMEOUT_S: 500,
515
+ CCA_MODE: "SIGNAL_AND_RSSI",
516
+ };
517
+
518
+ writeFileSync(STACK_CONFIG_PATH, JSON.stringify(config, undefined, 2));
519
+
520
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
521
+
522
+ expect(adapter.stackConfig).toStrictEqual(config);
523
+
524
+ // cleanup
525
+ unlinkSync(STACK_CONFIG_PATH);
526
+ });
527
+
528
+ it("Loads only valid custom stack config", () => {
529
+ const config = {
530
+ CONCENTRATOR_RAM_TYPE: "bad",
531
+ CONCENTRATOR_MIN_TIME: -1,
532
+ CONCENTRATOR_MAX_TIME: 15,
533
+ CONCENTRATOR_ROUTE_ERROR_THRESHOLD: 500,
534
+ CONCENTRATOR_DELIVERY_FAILURE_THRESHOLD: 200,
535
+ CONCENTRATOR_MAX_HOPS: 35,
536
+ MAX_END_DEVICE_CHILDREN: 65,
537
+ TRANSIENT_DEVICE_TIMEOUT: 65536,
538
+ END_DEVICE_POLL_TIMEOUT: 15,
539
+ TRANSIENT_KEY_TIMEOUT_S: 65536,
540
+ CCA_MODE: "abcd",
541
+ };
542
+
543
+ writeFileSync(STACK_CONFIG_PATH, JSON.stringify(config, undefined, 2));
544
+
545
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
546
+
547
+ expect(adapter.stackConfig).toStrictEqual(DEFAULT_STACK_CONFIG);
548
+
549
+ // cleanup
550
+ unlinkSync(STACK_CONFIG_PATH);
551
+ });
552
+
553
+ it("Loads only valid custom stack config - null CCA_MODE", () => {
554
+ const config = {
555
+ CCA_MODE: null,
556
+ };
557
+
558
+ writeFileSync(STACK_CONFIG_PATH, JSON.stringify(config, undefined, 2));
559
+
560
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
561
+
562
+ expect(adapter.stackConfig).toStrictEqual(DEFAULT_STACK_CONFIG);
563
+
564
+ // cleanup
565
+ unlinkSync(STACK_CONFIG_PATH);
566
+ });
567
+
568
+ it("Uses default concurrency for queue if not supplied/valid", () => {
569
+ adapter = new EmberAdapter(
570
+ DEFAULT_NETWORK_OPTIONS,
571
+ DEFAULT_SERIAL_PORT_OPTIONS,
572
+ backupPath,
573
+ Object.assign({}, DEFAULT_ADAPTER_OPTIONS, {concurrent: undefined}),
574
+ );
575
+ });
576
+
577
+ it("Starts with resumed when everything matches", async () => {
578
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
579
+ const result = adapter.start();
580
+
581
+ await vi.advanceTimersByTimeAsync(5000);
582
+ await expect(result).resolves.toStrictEqual("resumed");
583
+ expect(mockEzspSetProtocolVersion).toHaveBeenCalledWith(EZSP_PROTOCOL_VERSION);
584
+ expect(
585
+ // @ts-expect-error private
586
+ adapter.networkCache,
587
+ ).toStrictEqual({
588
+ eui64: DEFAULT_COORDINATOR_IEEE,
589
+ parameters: {
590
+ extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
591
+ panId: DEFAULT_NETWORK_OPTIONS.panID,
592
+ radioTxPower: 5,
593
+ radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
594
+ joinMethod: 0,
595
+ nwkManagerId: 0,
596
+ nwkUpdateId: 0,
597
+ channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
598
+ } as EmberNetworkParameters,
599
+ } as NetworkCache);
600
+ });
601
+
602
+ it("Starts with custom stack config", async () => {
603
+ const config = {
604
+ CONCENTRATOR_RAM_TYPE: "low",
605
+ CONCENTRATOR_MIN_TIME: 1,
606
+ CONCENTRATOR_MAX_TIME: 31,
607
+ CONCENTRATOR_ROUTE_ERROR_THRESHOLD: 5,
608
+ CONCENTRATOR_DELIVERY_FAILURE_THRESHOLD: 2,
609
+ CONCENTRATOR_MAX_HOPS: 5,
610
+ MAX_END_DEVICE_CHILDREN: 16,
611
+ TRANSIENT_DEVICE_TIMEOUT: 1000,
612
+ END_DEVICE_POLL_TIMEOUT: 12,
613
+ TRANSIENT_KEY_TIMEOUT_S: 500,
614
+ CCA_MODE: "SIGNAL_AND_RSSI",
615
+ };
616
+
617
+ writeFileSync(STACK_CONFIG_PATH, JSON.stringify(config, undefined, 2));
618
+
619
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
620
+ const result = adapter.start();
621
+
622
+ await vi.advanceTimersByTimeAsync(5000);
623
+ await expect(result).resolves.toStrictEqual("resumed");
624
+ expect(mockEzspSetValue).toHaveBeenCalledWith(EzspValueId.TRANSIENT_DEVICE_TIMEOUT, 2, lowHighBytes(config.TRANSIENT_DEVICE_TIMEOUT));
625
+ expect(mockEzspSetConfigurationValue).toHaveBeenCalledWith(EzspConfigId.MAX_END_DEVICE_CHILDREN, config.MAX_END_DEVICE_CHILDREN);
626
+ expect(mockEzspSetConfigurationValue).toHaveBeenCalledWith(EzspConfigId.END_DEVICE_POLL_TIMEOUT, config.END_DEVICE_POLL_TIMEOUT);
627
+ expect(mockEzspSetConfigurationValue).toHaveBeenCalledWith(EzspConfigId.TRANSIENT_KEY_TIMEOUT_S, config.TRANSIENT_KEY_TIMEOUT_S);
628
+ expect(mockEzspSetConcentrator).toHaveBeenCalledWith(
629
+ true,
630
+ EMBER_LOW_RAM_CONCENTRATOR,
631
+ config.CONCENTRATOR_MIN_TIME,
632
+ config.CONCENTRATOR_MAX_TIME,
633
+ config.CONCENTRATOR_ROUTE_ERROR_THRESHOLD,
634
+ config.CONCENTRATOR_DELIVERY_FAILURE_THRESHOLD,
635
+ config.CONCENTRATOR_MAX_HOPS,
636
+ );
637
+ expect(mockEzspSetRadioIeee802154CcaMode).toHaveBeenCalledWith(IEEE802154CcaMode.SIGNAL_AND_RSSI);
638
+
639
+ // cleanup
640
+ unlinkSync(STACK_CONFIG_PATH);
641
+ });
642
+
643
+ it("Starts with custom stack config invalid CCA_MODE", async () => {
644
+ const config = {
645
+ CCA_MODE: "abcd",
646
+ };
647
+
648
+ writeFileSync(STACK_CONFIG_PATH, JSON.stringify(config, undefined, 2));
649
+
650
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
651
+ const result = adapter.start();
652
+
653
+ await vi.advanceTimersByTimeAsync(5000);
654
+ await expect(result).resolves.toStrictEqual("resumed");
655
+ expect(mockEzspSetRadioIeee802154CcaMode).toHaveBeenCalledTimes(0);
656
+
657
+ // cleanup
658
+ unlinkSync(STACK_CONFIG_PATH);
659
+ });
660
+
661
+ it("Starts with restored when no network in adapter", async () => {
662
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
663
+ const expectedNetParams: EmberNetworkParameters = {
664
+ extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
665
+ panId: DEFAULT_NETWORK_OPTIONS.panID,
666
+ radioTxPower: 5,
667
+ radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
668
+ joinMethod: 0,
669
+ nwkManagerId: 0,
670
+ nwkUpdateId: 0,
671
+ channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
672
+ };
673
+
674
+ mockEzspNetworkInit.mockResolvedValueOnce(SLStatus.NOT_JOINED);
675
+
676
+ const result = adapter.start();
677
+
678
+ await vi.advanceTimersByTimeAsync(5000);
679
+ expect(mockEzspSetNWKFrameCounter).toHaveBeenCalledWith(DEFAULT_BACKUP.network_key.frame_counter);
680
+ // expect(mockEzspSetAPSFrameCounter).toHaveBeenCalledWith(DEFAULT_BACKUP.???.???);
681
+ expect(mockEzspFormNetwork).toHaveBeenCalledWith(expectedNetParams);
682
+ await expect(result).resolves.toStrictEqual("restored");
683
+ });
684
+
685
+ it("Starts with restored when network param mismatch but backup available", async () => {
686
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
687
+ const expectedNetParams: EmberNetworkParameters = {
688
+ extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
689
+ panId: DEFAULT_NETWORK_OPTIONS.panID,
690
+ radioTxPower: 5,
691
+ radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
692
+ joinMethod: 0,
693
+ nwkManagerId: 0,
694
+ nwkUpdateId: 0,
695
+ channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
696
+ };
697
+
698
+ mockEzspGetNetworkParameters.mockResolvedValueOnce([
699
+ SLStatus.OK,
700
+ EmberNodeType.COORDINATOR,
701
+ {
702
+ extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
703
+ panId: 1234,
704
+ radioTxPower: 5,
705
+ radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
706
+ joinMethod: 0,
707
+ nwkManagerId: 0,
708
+ nwkUpdateId: 0,
709
+ channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
710
+ } as EmberNetworkParameters,
711
+ ]);
712
+
713
+ const result = adapter.start();
714
+
715
+ await vi.advanceTimersByTimeAsync(5000);
716
+ expect(mockEzspSetNWKFrameCounter).toHaveBeenCalledWith(DEFAULT_BACKUP.network_key.frame_counter);
717
+ // expect(mockEzspSetAPSFrameCounter).toHaveBeenCalledWith(DEFAULT_BACKUP.???.???);
718
+ expect(mockEzspFormNetwork).toHaveBeenCalledWith(expectedNetParams);
719
+ await expect(result).resolves.toStrictEqual("restored");
720
+ });
721
+
722
+ it("Starts with restored when network key mismatch but backup available", async () => {
723
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
724
+ const expectedNetParams: EmberNetworkParameters = {
725
+ extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
726
+ panId: DEFAULT_NETWORK_OPTIONS.panID,
727
+ radioTxPower: 5,
728
+ radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
729
+ joinMethod: 0,
730
+ nwkManagerId: 0,
731
+ nwkUpdateId: 0,
732
+ channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
733
+ };
734
+
735
+ mockEzspGetNetworkParameters.mockResolvedValueOnce([SLStatus.OK, EmberNodeType.COORDINATOR, expectedNetParams]);
736
+
737
+ const contents = Buffer.from(DEFAULT_BACKUP.network_key.key, "hex").fill(0xff);
738
+
739
+ mockEzspExportKey.mockResolvedValueOnce([SLStatus.OK, {contents} as SecManKey]);
740
+
741
+ const result = adapter.start();
742
+
743
+ await vi.advanceTimersByTimeAsync(5000);
744
+ await expect(result).resolves.toStrictEqual("restored");
745
+ expect(mockEzspSetNWKFrameCounter).toHaveBeenCalledWith(DEFAULT_BACKUP.network_key.frame_counter);
746
+ // expect(mockEzspSetAPSFrameCounter).toHaveBeenCalledWith(DEFAULT_BACKUP.???.???);
747
+ expect(mockEzspFormNetwork).toHaveBeenCalledWith(expectedNetParams);
748
+ });
749
+
750
+ it("Starts with reset when networks mismatch but no backup available", async () => {
751
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
752
+
753
+ deleteCoordinatorBackup();
754
+ mockEzspGetNetworkParameters.mockResolvedValueOnce([
755
+ SLStatus.OK,
756
+ EmberNodeType.COORDINATOR,
757
+ {
758
+ extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
759
+ panId: 1234,
760
+ radioTxPower: 5,
761
+ radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
762
+ joinMethod: 0,
763
+ nwkManagerId: 0,
764
+ nwkUpdateId: 0,
765
+ channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
766
+ } as EmberNetworkParameters,
767
+ ]);
768
+
769
+ const result = adapter.start();
770
+
771
+ await vi.advanceTimersByTimeAsync(5000);
772
+ await expect(result).resolves.toStrictEqual("reset");
773
+ });
774
+
775
+ it("Starts with reset when backup/config mismatch", async () => {
776
+ adapter = new EmberAdapter(
777
+ Object.assign({}, DEFAULT_NETWORK_OPTIONS, {panID: 1234}),
778
+ DEFAULT_SERIAL_PORT_OPTIONS,
779
+ backupPath,
780
+ DEFAULT_ADAPTER_OPTIONS,
781
+ );
782
+
783
+ const result = adapter.start();
784
+
785
+ await vi.advanceTimersByTimeAsync(5000);
786
+ await expect(result).resolves.toStrictEqual("reset");
787
+ expect(mockEzspSetNWKFrameCounter).toHaveBeenCalledTimes(0);
788
+ // expect(mockEzspSetAPSFrameCounter).toHaveBeenCalledTimes(0);
789
+ expect(mockEzspFormNetwork).toHaveBeenCalledWith({
790
+ panId: 1234,
791
+ extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
792
+ radioTxPower: 5, // default when setting `transmitPower` is null/zero
793
+ radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
794
+ joinMethod: EmberJoinMethod.MAC_ASSOCIATION,
795
+ nwkManagerId: ZSpec.COORDINATOR_ADDRESS,
796
+ nwkUpdateId: 0,
797
+ channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
798
+ } as EmberNetworkParameters);
799
+ });
800
+
801
+ it("Starts with reset and forms with given transmit power", async () => {
802
+ adapter = new EmberAdapter(
803
+ Object.assign({}, DEFAULT_NETWORK_OPTIONS, {panID: 1234}),
804
+ DEFAULT_SERIAL_PORT_OPTIONS,
805
+ backupPath,
806
+ Object.assign({}, DEFAULT_ADAPTER_OPTIONS, {transmitPower: 10}),
807
+ );
808
+
809
+ const result = adapter.start();
810
+
811
+ await vi.advanceTimersByTimeAsync(5000);
812
+ await expect(result).resolves.toStrictEqual("reset");
813
+ expect(mockEzspSetNWKFrameCounter).toHaveBeenCalledTimes(0);
814
+ // expect(mockEzspSetAPSFrameCounter).toHaveBeenCalledTimes(0);
815
+ expect(mockEzspFormNetwork).toHaveBeenCalledWith({
816
+ panId: 1234,
817
+ extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
818
+ radioTxPower: 10,
819
+ radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
820
+ joinMethod: EmberJoinMethod.MAC_ASSOCIATION,
821
+ nwkManagerId: ZSpec.COORDINATOR_ADDRESS,
822
+ nwkUpdateId: 0,
823
+ channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
824
+ } as EmberNetworkParameters);
825
+ });
826
+
827
+ it("Starts with mismatching transmit power", async () => {
828
+ adapter = new EmberAdapter(
829
+ DEFAULT_NETWORK_OPTIONS,
830
+ DEFAULT_SERIAL_PORT_OPTIONS,
831
+ backupPath,
832
+ Object.assign({}, DEFAULT_ADAPTER_OPTIONS, {transmitPower: 10}),
833
+ );
834
+
835
+ const result = adapter.start();
836
+
837
+ await vi.advanceTimersByTimeAsync(5000);
838
+ await expect(result).resolves.toStrictEqual("resumed");
839
+ expect(mockEzspSetRadioPower).toHaveBeenCalledTimes(1);
840
+ expect(mockEzspSetRadioPower).toHaveBeenCalledWith(10);
841
+ });
842
+
843
+ it("Starts with matching transmit power after form", async () => {
844
+ adapter = new EmberAdapter(
845
+ DEFAULT_NETWORK_OPTIONS,
846
+ DEFAULT_SERIAL_PORT_OPTIONS,
847
+ backupPath,
848
+ Object.assign({}, DEFAULT_ADAPTER_OPTIONS, {transmitPower: 10}),
849
+ );
850
+ mockEzspNetworkInit.mockResolvedValueOnce(SLStatus.NOT_JOINED);
851
+ mockEzspGetNetworkParameters.mockResolvedValueOnce([
852
+ SLStatus.OK,
853
+ EmberNodeType.COORDINATOR,
854
+ {
855
+ extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
856
+ panId: DEFAULT_NETWORK_OPTIONS.panID,
857
+ radioTxPower: 10,
858
+ radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
859
+ joinMethod: 0,
860
+ nwkManagerId: 0,
861
+ nwkUpdateId: 0,
862
+ channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
863
+ } as EmberNetworkParameters,
864
+ ]);
865
+
866
+ const result = adapter.start();
867
+
868
+ await vi.advanceTimersByTimeAsync(5000);
869
+ await expect(result).resolves.toStrictEqual("restored");
870
+ expect(mockEzspSetRadioPower).toHaveBeenCalledTimes(0);
871
+ });
872
+
873
+ it("Starts with mismatching transmit power, failure does not present start", async () => {
874
+ adapter = new EmberAdapter(
875
+ DEFAULT_NETWORK_OPTIONS,
876
+ DEFAULT_SERIAL_PORT_OPTIONS,
877
+ backupPath,
878
+ Object.assign({}, DEFAULT_ADAPTER_OPTIONS, {transmitPower: 12}),
879
+ );
880
+ mockEzspSetRadioPower.mockResolvedValueOnce(SLStatus.FAIL);
881
+
882
+ const result = adapter.start();
883
+
884
+ await vi.advanceTimersByTimeAsync(5000);
885
+ await expect(result).resolves.toStrictEqual("resumed");
886
+ expect(mockEzspSetRadioPower).toHaveBeenCalledTimes(1);
887
+ expect(mockEzspSetRadioPower).toHaveBeenCalledWith(12);
888
+ expect(loggerSpies.error).toHaveBeenCalledWith("Failed to set transmit power to 12 status=FAIL.", "zh:ember");
889
+ });
890
+
891
+ it("Fails to start when EZSP layer fails to start", async () => {
892
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
893
+
894
+ mockEzspStart.mockResolvedValueOnce(EzspStatus.HOST_FATAL_ERROR);
895
+
896
+ const result = adapter.start();
897
+
898
+ await expect(result).rejects.toThrow(`Failed to start EZSP layer with status=${EzspStatus[EzspStatus.HOST_FATAL_ERROR]}.`);
899
+ });
900
+
901
+ it.each([
902
+ [
903
+ "if NCP has improper stack type",
904
+ () => {
905
+ mockEzspVersion.mockResolvedValueOnce([14, 1, 123]);
906
+ },
907
+ "Stack type 1 is not expected!",
908
+ ],
909
+ [
910
+ "if NCP version unsupported",
911
+ () => {
912
+ mockEzspVersion.mockResolvedValueOnce([12, EZSP_STACK_TYPE_MESH, 123]);
913
+ },
914
+ `Adapter EZSP protocol version (12) is not supported by Host [${EZSP_MIN_PROTOCOL_VERSION}-${EZSP_PROTOCOL_VERSION}].`,
915
+ ],
916
+ [
917
+ "if NCP has old style version number",
918
+ () => {
919
+ mockEzspGetVersionStruct.mockResolvedValueOnce([SLStatus.INVALID_PARAMETER, 0]);
920
+ },
921
+ "NCP has old-style version number. Not supported.",
922
+ ],
923
+ [
924
+ "if network is not valid by end of init sequence",
925
+ () => {
926
+ mockEzspGetNetworkParameters
927
+ .mockResolvedValueOnce([SLStatus.OK, EmberNodeType.COORDINATOR, deepClone(DEFAULT_ADAPTER_NETWORK_PARAMETERS)])
928
+ .mockResolvedValueOnce([SLStatus.FAIL, 0, {}]);
929
+ },
930
+ "Failed to get network parameters with status=FAIL.",
931
+ ],
932
+ [
933
+ "if could not set concentrator",
934
+ () => {
935
+ mockEzspSetConcentrator.mockResolvedValueOnce(SLStatus.FAIL);
936
+ },
937
+ "[CONCENTRATOR] Failed to set concentrator with status=FAIL.",
938
+ ],
939
+ [
940
+ "if could not add endpoint",
941
+ () => {
942
+ mockEzspAddEndpoint.mockResolvedValueOnce(SLStatus.FAIL);
943
+ },
944
+ `Failed to register endpoint '1' with status=FAIL.`,
945
+ ],
946
+ [
947
+ "if could not set multicast table entry",
948
+ () => {
949
+ mockEzspSetMulticastTableEntry.mockResolvedValueOnce(SLStatus.FAIL);
950
+ },
951
+ `Failed to register group '0' in multicast table with status=FAIL.`,
952
+ ],
953
+ [
954
+ "if could not set TC key request policy",
955
+ () => {
956
+ mockEzspSetPolicy
957
+ .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.BINDING_MODIFICATION_POLICY
958
+ .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY
959
+ .mockResolvedValueOnce(SLStatus.FAIL); // EzspPolicyId.TC_KEY_REQUEST_POLICY
960
+ },
961
+ "[INIT TC] Failed to set EzspPolicyId TC_KEY_REQUEST_POLICY to ALLOW_TC_KEY_REQUESTS_AND_SEND_CURRENT_KEY with status=FAIL.",
962
+ ],
963
+ [
964
+ "if could not set app key request policy",
965
+ () => {
966
+ mockEzspSetPolicy
967
+ .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.BINDING_MODIFICATION_POLICY
968
+ .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY
969
+ .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.TC_KEY_REQUEST_POLICY
970
+ .mockResolvedValueOnce(SLStatus.FAIL); // EzspPolicyId.APP_KEY_REQUEST_POLICY
971
+ },
972
+ "[INIT TC] Failed to set EzspPolicyId APP_KEY_REQUEST_POLICY to DENY_APP_KEY_REQUESTS with status=FAIL.",
973
+ ],
974
+ [
975
+ "if could not set app key request policy",
976
+ () => {
977
+ mockEzspSetPolicy
978
+ .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.BINDING_MODIFICATION_POLICY
979
+ .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY
980
+ .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.TC_KEY_REQUEST_POLICY
981
+ .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.APP_KEY_REQUEST_POLICY
982
+ .mockResolvedValueOnce(SLStatus.FAIL); // EzspPolicyId.TRUST_CENTER_POLICY
983
+ },
984
+ "[INIT TC] Failed to set join policy to USE_PRECONFIGURED_KEY with status=FAIL.",
985
+ ],
986
+ [
987
+ "if could not init network",
988
+ () => {
989
+ mockEzspNetworkInit.mockResolvedValueOnce(SLStatus.FAIL);
990
+ },
991
+ "[INIT TC] Failed network init request with status=FAIL.",
992
+ ],
993
+ [
994
+ "if could not export network key",
995
+ () => {
996
+ mockEzspExportKey.mockResolvedValueOnce([SLStatus.FAIL, Buffer.alloc(16)]);
997
+ },
998
+ "[INIT TC] Failed to export Network Key with status=FAIL.",
999
+ ],
1000
+ [
1001
+ "if could not leave network",
1002
+ () => {
1003
+ // force leave code path
1004
+ mockEzspGetNetworkParameters.mockResolvedValueOnce([SLStatus.FAIL, 0, {}]);
1005
+ mockEzspLeaveNetwork.mockResolvedValueOnce(SLStatus.FAIL);
1006
+ },
1007
+ "[INIT TC] Failed leave network request with status=FAIL.",
1008
+ ],
1009
+ [
1010
+ "if form could not set NWK frame counter",
1011
+ () => {
1012
+ takeRestoredCodePath();
1013
+ mockEzspSetNWKFrameCounter.mockResolvedValueOnce(SLStatus.FAIL);
1014
+ },
1015
+ "[INIT FORM] Failed to set NWK frame counter with status=FAIL.",
1016
+ ],
1017
+ // [
1018
+ // 'if form could not set TC APS frame counter',
1019
+ // () => {
1020
+ // takeRestoredCodePath();
1021
+ // mockEzspSetAPSFrameCounter.mockResolvedValueOnce(SLStatus.FAIL);
1022
+ // },
1023
+ // `[INIT FORM] Failed to set TC APS frame counter with status=FAIL.`,
1024
+ // ],
1025
+ [
1026
+ "if form could not set initial security state",
1027
+ () => {
1028
+ takeResetCodePath();
1029
+ mockEzspSetInitialSecurityState.mockResolvedValueOnce(SLStatus.FAIL);
1030
+ },
1031
+ "[INIT FORM] Failed to set initial security state with status=FAIL.",
1032
+ ],
1033
+ [
1034
+ "if form could not set extended security bitmask",
1035
+ () => {
1036
+ takeResetCodePath();
1037
+ mockEzspSetExtendedSecurityBitmask.mockResolvedValueOnce(SLStatus.FAIL);
1038
+ },
1039
+ "[INIT FORM] Failed to set extended security bitmask to 272 with status=FAIL.",
1040
+ ],
1041
+ [
1042
+ "if could not form network",
1043
+ () => {
1044
+ takeResetCodePath();
1045
+ mockEzspFormNetwork.mockResolvedValueOnce(SLStatus.FAIL);
1046
+ },
1047
+ "[INIT FORM] Failed form network request with status=FAIL.",
1048
+ ],
1049
+ [
1050
+ "if backup corrupted",
1051
+ () => {
1052
+ writeFileSync(backupPath, "abcd");
1053
+ },
1054
+ "[BACKUP] Coordinator backup is corrupted.",
1055
+ ],
1056
+ [
1057
+ "if backup unsupported",
1058
+ () => {
1059
+ const customBackup = deepClone(DEFAULT_BACKUP);
1060
+ // @ts-expect-error mock override
1061
+ customBackup.metadata.version = 2;
1062
+
1063
+ writeFileSync(backupPath, JSON.stringify(customBackup, undefined, 2));
1064
+ },
1065
+ "[BACKUP] Unsupported open coordinator backup version (version=2).",
1066
+ ],
1067
+ [
1068
+ "if backup not EmberZNet stack specific",
1069
+ () => {
1070
+ const customBackup = deepClone(DEFAULT_BACKUP);
1071
+ customBackup.stack_specific!.ezsp = undefined;
1072
+
1073
+ writeFileSync(backupPath, JSON.stringify(customBackup, undefined, 2));
1074
+ },
1075
+ "[BACKUP] Current backup file is not for EmberZNet stack.",
1076
+ ],
1077
+ [
1078
+ "if backup not EmberZNet EZSP version",
1079
+ () => {
1080
+ const customBackup = deepClone(DEFAULT_BACKUP);
1081
+ customBackup.metadata.internal.ezspVersion = undefined;
1082
+
1083
+ writeFileSync(backupPath, JSON.stringify(customBackup, undefined, 2));
1084
+ },
1085
+ "[BACKUP] Current backup file is not for EmberZNet stack.",
1086
+ ],
1087
+ [
1088
+ "if backup unknown format",
1089
+ () => {
1090
+ const customBackup = deepClone(DEFAULT_BACKUP);
1091
+ // @ts-expect-error mock override
1092
+ customBackup.metadata.format = "unknown";
1093
+
1094
+ writeFileSync(backupPath, JSON.stringify(customBackup, undefined, 2));
1095
+ },
1096
+ "[BACKUP] Unknown backup format.",
1097
+ ],
1098
+ ])("Fails to start %s", async (_reason, setup, error) => {
1099
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1100
+
1101
+ setup();
1102
+
1103
+ const result = defuseRejection(adapter.start());
1104
+
1105
+ await vi.advanceTimersByTimeAsync(5000);
1106
+ await expect(result).rejects.toThrow(error);
1107
+ });
1108
+
1109
+ it("Warns if NCP has non-GA firmware", async () => {
1110
+ const type: EmberVersionType = EmberVersionType.ALPHA_1;
1111
+
1112
+ mockEzspGetVersionStruct.mockResolvedValueOnce([
1113
+ SLStatus.OK,
1114
+ {
1115
+ build: 135,
1116
+ major: 8,
1117
+ minor: 0,
1118
+ patch: 0,
1119
+ special: 0,
1120
+ type,
1121
+ } as EmberVersion,
1122
+ ]);
1123
+
1124
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1125
+
1126
+ const result = adapter.start();
1127
+
1128
+ await vi.advanceTimersByTimeAsync(5000);
1129
+ await expect(result).resolves.toStrictEqual("resumed");
1130
+
1131
+ expect(loggerSpies.warning).toHaveBeenCalledWith(`Adapter is running a non-GA version (${EmberVersionType[type]}).`, "zh:ember");
1132
+ });
1133
+
1134
+ it("Switches EZSP protocol when supported", async () => {
1135
+ mockEzspVersion.mockResolvedValueOnce([EZSP_MIN_PROTOCOL_VERSION, EZSP_STACK_TYPE_MESH, 123]);
1136
+
1137
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1138
+
1139
+ const result = adapter.start();
1140
+
1141
+ await vi.advanceTimersByTimeAsync(5000);
1142
+ await expect(result).resolves.toStrictEqual("resumed");
1143
+ expect(mockEzspVersion).toHaveBeenNthCalledWith(1, EZSP_PROTOCOL_VERSION);
1144
+ expect(mockEzspVersion).toHaveBeenNthCalledWith(2, EZSP_MIN_PROTOCOL_VERSION);
1145
+ expect(mockEzspSetProtocolVersion).toHaveBeenCalledWith(EZSP_MIN_PROTOCOL_VERSION);
1146
+ });
1147
+
1148
+ it("Logs failed set config value on start", async () => {
1149
+ mockEzspSetConfigurationValue.mockResolvedValueOnce(SLStatus.ALLOCATION_FAILED);
1150
+
1151
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1152
+
1153
+ const result = adapter.start();
1154
+
1155
+ await vi.advanceTimersByTimeAsync(5000);
1156
+ await expect(result).resolves.toStrictEqual("resumed");
1157
+
1158
+ expect(loggerSpies.info).toHaveBeenCalledWith(
1159
+ `[EzspConfigId] Failed to SET '${EzspConfigId[EzspConfigId.TRUST_CENTER_ADDRESS_CACHE_SIZE]}' TO '2' with status=${SLStatus[SLStatus.ALLOCATION_FAILED]}. Firmware value will be used instead.`,
1160
+ "zh:ember",
1161
+ );
1162
+ });
1163
+
1164
+ it("Starts and skips adding endpoint if already present", async () => {
1165
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1166
+
1167
+ mockEzspGetEndpointFlags
1168
+ .mockResolvedValueOnce([SLStatus.NOT_FOUND, EzspEndpointFlag.DISABLED])
1169
+ .mockResolvedValueOnce([SLStatus.OK, EzspEndpointFlag.ENABLED]); // mock GP already registered
1170
+
1171
+ const result = adapter.start();
1172
+
1173
+ await vi.advanceTimersByTimeAsync(5000);
1174
+ await expect(result).resolves.toStrictEqual("resumed");
1175
+ expect(mockEzspAddEndpoint).toHaveBeenCalledTimes(1);
1176
+ const ep = FIXED_ENDPOINTS[0];
1177
+ expect(mockEzspAddEndpoint).toHaveBeenCalledWith(
1178
+ ep.endpoint,
1179
+ ep.profileId,
1180
+ ep.deviceId,
1181
+ ep.deviceVersion,
1182
+ ep.inClusterList.slice(), // copy
1183
+ ep.outClusterList.slice(), // copy
1184
+ );
1185
+ });
1186
+
1187
+ it("Starts and detects when network key frame counter will soon wrap to 0", async () => {
1188
+ const customBackup = deepClone(DEFAULT_BACKUP);
1189
+ customBackup.network_key.frame_counter = 0xfeeeeeef;
1190
+
1191
+ writeFileSync(backupPath, JSON.stringify(customBackup, undefined, 2));
1192
+
1193
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1194
+ const result = adapter.start();
1195
+
1196
+ await vi.advanceTimersByTimeAsync(5000);
1197
+ await expect(result).resolves.toStrictEqual("resumed");
1198
+ expect(logger.warning).toHaveBeenCalledWith(
1199
+ "[INIT TC] Network key frame counter is reaching its limit. A new network key will have to be instaured soon.",
1200
+ "zh:ember",
1201
+ );
1202
+ });
1203
+
1204
+ it("Starts and soft-fails if unable to clear key table", async () => {
1205
+ takeResetCodePath();
1206
+ mockEzspClearKeyTable.mockResolvedValueOnce(SLStatus.FAIL);
1207
+
1208
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1209
+ const result = adapter.start();
1210
+
1211
+ await vi.advanceTimersByTimeAsync(5000);
1212
+ await expect(result).resolves.toStrictEqual("reset");
1213
+ expect(loggerSpies.error).toHaveBeenCalledWith("[INIT FORM] Failed to clear key table with status=FAIL.", "zh:ember");
1214
+ });
1215
+
1216
+ it("Starts but ignores backup if unsupported version", async () => {
1217
+ const customBackup = deepClone(DEFAULT_BACKUP);
1218
+ customBackup.metadata.internal.ezspVersion = 11;
1219
+
1220
+ writeFileSync(backupPath, JSON.stringify(customBackup, undefined, 2));
1221
+
1222
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1223
+ const result = adapter.start();
1224
+ const old = `${backupPath}.old`;
1225
+
1226
+ await vi.advanceTimersByTimeAsync(5000);
1227
+ await expect(result).resolves.toStrictEqual("resumed");
1228
+ expect(existsSync(old)).toBeTruthy();
1229
+ expect(loggerSpies.warning).toHaveBeenCalledWith(
1230
+ "[BACKUP] Current backup file is from an unsupported EZSP version. Renaming and ignoring.",
1231
+ "zh:ember",
1232
+ );
1233
+
1234
+ // cleanup
1235
+ unlinkSync(old);
1236
+ });
1237
+
1238
+ describe("When started", () => {
1239
+ beforeEach(async () => {
1240
+ adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1241
+
1242
+ const result = adapter.start();
1243
+
1244
+ await vi.advanceTimersByTimeAsync(5000);
1245
+ await result;
1246
+
1247
+ // clean slate "post-start"
1248
+ clearMocks();
1249
+ });
1250
+
1251
+ it("Stops Ezsp layer on stop", async () => {
1252
+ // @ts-expect-error private
1253
+ const ezspStopSpy = vi.spyOn(adapter.ezsp, "stop");
1254
+ // @ts-expect-error private
1255
+ const ezspRemoveAllListenersSpy = vi.spyOn(adapter.ezsp, "removeAllListeners");
1256
+
1257
+ await adapter.stop();
1258
+
1259
+ expect(ezspStopSpy).toHaveBeenCalledTimes(1);
1260
+ expect(ezspRemoveAllListenersSpy).toHaveBeenCalledTimes(1);
1261
+ });
1262
+
1263
+ it("Retrieves parameters from cache when cache valid", async () => {
1264
+ await expect(adapter.emberGetEui64()).resolves.toStrictEqual(DEFAULT_COORDINATOR_IEEE);
1265
+ expect(mockEzspGetEui64).toHaveBeenCalledTimes(0);
1266
+
1267
+ await expect(adapter.emberGetPanId()).resolves.toStrictEqual(DEFAULT_NETWORK_OPTIONS.panID);
1268
+ expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(0);
1269
+
1270
+ await expect(adapter.emberGetExtendedPanId()).resolves.toStrictEqual(DEFAULT_NETWORK_OPTIONS.extendedPanID!);
1271
+ expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(0);
1272
+
1273
+ await expect(adapter.emberGetRadioChannel()).resolves.toStrictEqual(DEFAULT_NETWORK_OPTIONS.channelList[0]);
1274
+ expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(0);
1275
+ });
1276
+
1277
+ it("Retrieves parameters from NCP when cache invalid", async () => {
1278
+ adapter.clearNetworkCache();
1279
+ await expect(adapter.emberGetEui64()).resolves.toStrictEqual(DEFAULT_COORDINATOR_IEEE);
1280
+ expect(mockEzspGetEui64).toHaveBeenCalledTimes(1);
1281
+
1282
+ adapter.clearNetworkCache();
1283
+ await expect(adapter.emberGetPanId()).resolves.toStrictEqual(DEFAULT_NETWORK_OPTIONS.panID);
1284
+ expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(1);
1285
+
1286
+ adapter.clearNetworkCache();
1287
+ await expect(adapter.emberGetExtendedPanId()).resolves.toStrictEqual(DEFAULT_NETWORK_OPTIONS.extendedPanID!);
1288
+ expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(2);
1289
+
1290
+ adapter.clearNetworkCache();
1291
+ await expect(adapter.emberGetRadioChannel()).resolves.toStrictEqual(DEFAULT_NETWORK_OPTIONS.channelList[0]);
1292
+ expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(3);
1293
+ });
1294
+
1295
+ it("Throws when failed to retrieve parameter from NCP", async () => {
1296
+ mockEzspGetNetworkParameters
1297
+ .mockResolvedValueOnce([SLStatus.FAIL, 0, {}])
1298
+ .mockResolvedValueOnce([SLStatus.FAIL, 0, {}])
1299
+ .mockResolvedValueOnce([SLStatus.FAIL, 0, {}]);
1300
+
1301
+ adapter.clearNetworkCache();
1302
+
1303
+ const p1 = defuseRejection(adapter.emberGetPanId());
1304
+
1305
+ await vi.advanceTimersByTimeAsync(5000);
1306
+ await expect(p1).rejects.toThrow("Failed to get PAN ID (via network parameters) with status=FAIL.");
1307
+
1308
+ adapter.clearNetworkCache();
1309
+
1310
+ const p2 = defuseRejection(adapter.emberGetExtendedPanId());
1311
+
1312
+ await vi.advanceTimersByTimeAsync(5000);
1313
+ await expect(p2).rejects.toThrow("Failed to get Extended PAN ID (via network parameters) with status=FAIL.");
1314
+
1315
+ adapter.clearNetworkCache();
1316
+
1317
+ const p3 = defuseRejection(adapter.emberGetRadioChannel());
1318
+
1319
+ await vi.advanceTimersByTimeAsync(5000);
1320
+ await expect(p3).rejects.toThrow("Failed to get radio channel (via network parameters) with status=FAIL.");
1321
+ });
1322
+
1323
+ it("Logs stack status change", async () => {
1324
+ mockEzspEmitter.emit("stackStatus", SLStatus.ZIGBEE_TRUST_CENTER_SWAP_EUI_HAS_CHANGED);
1325
+ await flushPromises();
1326
+
1327
+ expect(loggerSpies.debug).toHaveBeenCalledWith(
1328
+ `[STACK STATUS] ${SLStatus[SLStatus.ZIGBEE_TRUST_CENTER_SWAP_EUI_HAS_CHANGED]}.`,
1329
+ "zh:ember",
1330
+ );
1331
+ });
1332
+
1333
+ it("Handles message delivery failure", async () => {
1334
+ let apsFrame: EmberApsFrame = {
1335
+ profileId: ZSpec.HA_PROFILE_ID,
1336
+ clusterId: Zcl.Clusters.genBasic.ID,
1337
+ sourceEndpoint: 1,
1338
+ destinationEndpoint: 1,
1339
+ options: 0,
1340
+ groupId: 0,
1341
+ sequence: 0,
1342
+ };
1343
+
1344
+ mockEzspEmitter.emit("messageSent", SLStatus.ZIGBEE_DELIVERY_FAILED, EmberOutgoingMessageType.BROADCAST, 1234, apsFrame, 1);
1345
+ await flushPromises();
1346
+
1347
+ expect(loggerSpies.error).toHaveBeenCalledWith(`Delivery of BROADCAST failed for '1234'.`, "zh:ember");
1348
+
1349
+ const spyDeliveryFailedFor = vi.spyOn(
1350
+ // @ts-expect-error private
1351
+ adapter.oneWaitress,
1352
+ "deliveryFailedFor",
1353
+ );
1354
+
1355
+ apsFrame = {
1356
+ profileId: ZSpec.HA_PROFILE_ID,
1357
+ clusterId: Zcl.Clusters.genBasic.ID,
1358
+ sourceEndpoint: 1,
1359
+ destinationEndpoint: 1,
1360
+ options: 0,
1361
+ groupId: 0,
1362
+ sequence: 0,
1363
+ };
1364
+
1365
+ mockEzspEmitter.emit("messageSent", SLStatus.ZIGBEE_DELIVERY_FAILED, EmberOutgoingMessageType.DIRECT, 1234, apsFrame, 1);
1366
+ await flushPromises();
1367
+
1368
+ expect(spyDeliveryFailedFor).toHaveBeenCalledTimes(1);
1369
+ expect(spyDeliveryFailedFor).toHaveBeenCalledWith(1234, apsFrame);
1370
+ });
1371
+
1372
+ it("Registers message unknown group in multicast table", async () => {
1373
+ // @ts-expect-error private
1374
+ const tableIdx = adapter.multicastTable.length;
1375
+ const apsFrame = {
1376
+ profileId: ZSpec.HA_PROFILE_ID,
1377
+ clusterId: Zcl.Clusters.genBasic.ID,
1378
+ sourceEndpoint: 1,
1379
+ destinationEndpoint: 0xff,
1380
+ options: 0,
1381
+ groupId: 123,
1382
+ sequence: 0,
1383
+ };
1384
+
1385
+ mockEzspEmitter.emit("messageSent", SLStatus.OK, EmberOutgoingMessageType.MULTICAST, 1234, apsFrame, 1);
1386
+ await flushPromises();
1387
+
1388
+ expect(mockEzspSetMulticastTableEntry).toHaveBeenCalledTimes(1);
1389
+ expect(mockEzspSetMulticastTableEntry).toHaveBeenCalledWith(tableIdx, {
1390
+ multicastId: 123,
1391
+ endpoint: FIXED_ENDPOINTS[0].endpoint,
1392
+ networkIndex: FIXED_ENDPOINTS[0].networkIndex,
1393
+ } as EmberMulticastTableEntry);
1394
+ expect(
1395
+ // @ts-expect-error private
1396
+ adapter.multicastTable.length,
1397
+ ).toStrictEqual(tableIdx + 1);
1398
+ });
1399
+
1400
+ it("Fails to register message unknown group in multicast table", async () => {
1401
+ mockEzspSetMulticastTableEntry.mockResolvedValueOnce(SLStatus.FAIL);
1402
+
1403
+ // @ts-expect-error private
1404
+ const tableIdx = adapter.multicastTable.length;
1405
+ const apsFrame = {
1406
+ profileId: ZSpec.HA_PROFILE_ID,
1407
+ clusterId: Zcl.Clusters.genBasic.ID,
1408
+ sourceEndpoint: 1,
1409
+ destinationEndpoint: 0xff,
1410
+ options: 0,
1411
+ groupId: 123,
1412
+ sequence: 0,
1413
+ };
1414
+
1415
+ mockEzspEmitter.emit("messageSent", SLStatus.OK, EmberOutgoingMessageType.MULTICAST, 1234, apsFrame, 1);
1416
+ await flushPromises();
1417
+
1418
+ expect(mockEzspSetMulticastTableEntry).toHaveBeenCalledTimes(1);
1419
+ expect(mockEzspSetMulticastTableEntry).toHaveBeenCalledWith(tableIdx, {
1420
+ multicastId: 123,
1421
+ endpoint: FIXED_ENDPOINTS[0].endpoint,
1422
+ networkIndex: FIXED_ENDPOINTS[0].networkIndex,
1423
+ } as EmberMulticastTableEntry);
1424
+ expect(
1425
+ // @ts-expect-error private
1426
+ adapter.multicastTable.length,
1427
+ ).toStrictEqual(tableIdx); // not increased, entry was removed
1428
+ });
1429
+
1430
+ it("Emits network address event on ZDO NETWORK_ADDRESS_RESPONSE", async () => {
1431
+ const spyResolveZDO = vi.spyOn(
1432
+ // @ts-expect-error private
1433
+ adapter.oneWaitress,
1434
+ "resolveZDO",
1435
+ );
1436
+ const spyEmit = vi.spyOn(adapter, "emit");
1437
+ const sender = 1234;
1438
+ const apsFrame: EmberApsFrame = {
1439
+ profileId: Zdo.ZDO_PROFILE_ID,
1440
+ clusterId: Zdo.ClusterId.NETWORK_ADDRESS_RESPONSE,
1441
+ sourceEndpoint: Zdo.ZDO_ENDPOINT,
1442
+ destinationEndpoint: Zdo.ZDO_ENDPOINT,
1443
+ options: 0,
1444
+ groupId: 0,
1445
+ sequence: 0,
1446
+ };
1447
+
1448
+ mockEzspEmitter.emit(
1449
+ "zdoResponse",
1450
+ apsFrame,
1451
+ sender,
1452
+ Buffer.from([1, Zdo.Status.SUCCESS, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x11, 0x22, 0x33, 0xd2, 0x04]),
1453
+ );
1454
+ await flushPromises();
1455
+
1456
+ const zdoResponse = [
1457
+ Zdo.Status.SUCCESS,
1458
+ {
1459
+ eui64: "0x332211eeddccbbaa",
1460
+ nwkAddress: sender,
1461
+ startIndex: 0,
1462
+ assocDevList: [],
1463
+ } as ZdoTypes.NetworkAddressResponse,
1464
+ ];
1465
+
1466
+ expect(spyResolveZDO).toHaveBeenCalledTimes(1);
1467
+ expect(spyResolveZDO).toHaveBeenCalledWith("0x332211eeddccbbaa", apsFrame, zdoResponse);
1468
+ expect(spyEmit).toHaveBeenCalledWith("zdoResponse", Zdo.ClusterId.NETWORK_ADDRESS_RESPONSE, zdoResponse);
1469
+ });
1470
+
1471
+ it("Emits device announce event on ZDO END_DEVICE_ANNOUNCE", async () => {
1472
+ const spyResolveZDO = vi.spyOn(
1473
+ // @ts-expect-error private
1474
+ adapter.oneWaitress,
1475
+ "resolveZDO",
1476
+ );
1477
+ const spyEmit = vi.spyOn(adapter, "emit");
1478
+ const sender = 1234;
1479
+ const apsFrame: EmberApsFrame = {
1480
+ profileId: Zdo.ZDO_PROFILE_ID,
1481
+ clusterId: Zdo.ClusterId.END_DEVICE_ANNOUNCE,
1482
+ sourceEndpoint: Zdo.ZDO_ENDPOINT,
1483
+ destinationEndpoint: Zdo.ZDO_ENDPOINT,
1484
+ options: 0,
1485
+ groupId: 0,
1486
+ sequence: 0,
1487
+ };
1488
+
1489
+ mockEzspEmitter.emit("zdoResponse", apsFrame, sender, Buffer.from([1, 0xd2, 0x04, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x11, 0x22, 0x33, 6]));
1490
+
1491
+ await flushPromises();
1492
+
1493
+ const zdoResponse = [
1494
+ Zdo.Status.SUCCESS,
1495
+ {
1496
+ nwkAddress: sender,
1497
+ eui64: "0x332211eeddccbbaa",
1498
+ capabilities: {
1499
+ alternatePANCoordinator: 0,
1500
+ deviceType: 1,
1501
+ powerSource: 1,
1502
+ rxOnWhenIdle: 0,
1503
+ reserved1: 0,
1504
+ reserved2: 0,
1505
+ securityCapability: 0,
1506
+ allocateAddress: 0,
1507
+ },
1508
+ } as ZdoTypes.EndDeviceAnnounce,
1509
+ ];
1510
+ expect(spyResolveZDO).toHaveBeenCalledTimes(1);
1511
+ expect(spyResolveZDO).toHaveBeenCalledWith(sender, apsFrame, zdoResponse);
1512
+ expect(spyEmit).toHaveBeenCalledWith("zdoResponse", Zdo.ClusterId.END_DEVICE_ANNOUNCE, zdoResponse);
1513
+ });
1514
+
1515
+ it("Emits ZCL payload on incoming message", async () => {
1516
+ const spyResolveZCL = vi.spyOn(
1517
+ // @ts-expect-error private
1518
+ adapter.oneWaitress,
1519
+ "resolveZCL",
1520
+ );
1521
+ const spyEmit = vi.spyOn(adapter, "emit");
1522
+ const sender = 1234;
1523
+ const apsFrame: EmberApsFrame = {
1524
+ profileId: ZSpec.HA_PROFILE_ID,
1525
+ clusterId: Zcl.Clusters.genBasic.ID,
1526
+ sourceEndpoint: 2,
1527
+ destinationEndpoint: 1,
1528
+ options: 0,
1529
+ groupId: 0,
1530
+ sequence: 0,
1531
+ };
1532
+ const lastHopLqi = 252;
1533
+ // Received Zigbee message from '0x', type 'readResponse', cluster 'genBasic', data '{"zclVersion":3}' from endpoint 1 with groupID 0
1534
+ const messageContents = Buffer.from("1803010000002003", "hex");
1535
+
1536
+ mockEzspEmitter.emit("incomingMessage", EmberIncomingMessageType.UNICAST, apsFrame, lastHopLqi, sender, messageContents);
1537
+ await flushPromises();
1538
+
1539
+ const payload: ZclPayload = {
1540
+ clusterID: apsFrame.clusterId,
1541
+ header: Zcl.Header.fromBuffer(messageContents),
1542
+ address: sender,
1543
+ data: messageContents,
1544
+ endpoint: apsFrame.sourceEndpoint,
1545
+ linkquality: lastHopLqi,
1546
+ groupID: apsFrame.groupId,
1547
+ wasBroadcast: false,
1548
+ destinationEndpoint: apsFrame.destinationEndpoint,
1549
+ };
1550
+
1551
+ expect(spyResolveZCL).toHaveBeenCalledTimes(1);
1552
+ expect(spyResolveZCL).toHaveBeenCalledWith(payload);
1553
+ expect(spyEmit).toHaveBeenCalledWith("zclPayload", payload);
1554
+ });
1555
+
1556
+ it("Emits ZCL payload on touchlink message", async () => {
1557
+ const spyResolveZCL = vi.spyOn(
1558
+ // @ts-expect-error private
1559
+ adapter.oneWaitress,
1560
+ "resolveZCL",
1561
+ );
1562
+ const spyEmit = vi.spyOn(adapter, "emit");
1563
+ const sourcePanId: PanId = 0x1234;
1564
+ const sourceAddress: Eui64 = "0x1122334455aabbcc";
1565
+ const lastHopLqi = 252;
1566
+ const groupId: number = 0;
1567
+ const messageContents = Buffer.from("1803010000002003", "hex");
1568
+
1569
+ mockEzspEmitter.emit("touchlinkMessage", sourcePanId, sourceAddress, groupId, lastHopLqi, messageContents);
1570
+ await flushPromises();
1571
+
1572
+ const payload: ZclPayload = {
1573
+ clusterID: Zcl.Clusters.touchlink.ID,
1574
+ header: Zcl.Header.fromBuffer(messageContents),
1575
+ address: sourceAddress,
1576
+ data: messageContents,
1577
+ endpoint: 1,
1578
+ linkquality: lastHopLqi,
1579
+ groupID: groupId,
1580
+ wasBroadcast: true,
1581
+ destinationEndpoint: FIXED_ENDPOINTS[0].endpoint,
1582
+ };
1583
+
1584
+ expect(spyResolveZCL).toHaveBeenCalledTimes(1);
1585
+ expect(spyResolveZCL).toHaveBeenCalledWith(payload);
1586
+ expect(spyEmit).toHaveBeenCalledWith("zclPayload", payload);
1587
+ });
1588
+
1589
+ it("Emits ZCL payload on greenpower message", async () => {
1590
+ const spyResolveZCL = vi.spyOn(
1591
+ // @ts-expect-error private
1592
+ adapter.oneWaitress,
1593
+ "resolveZCL",
1594
+ );
1595
+ const spyEmit = vi.spyOn(adapter, "emit");
1596
+ const sourceId: number = 1234;
1597
+ const nwkAddress: NodeId = sourceId & 0xffff;
1598
+ const gpdLink: number = 123;
1599
+ const sequenceNumber: number = 1;
1600
+ const commandIdentifier: number = Zcl.Clusters.greenPower.commands.commissioningNotification.ID;
1601
+ const frameCounter: number = 102;
1602
+ const gpdCommandId: number = 0xe0;
1603
+ const gpdCommandPayload = Buffer.from([
1604
+ 0x02 /* deviceID */,
1605
+ 0x83 /* options */,
1606
+ 0xf2 /* extendedOptions */,
1607
+ ...[0xf1, 0xec, 0x92, 0xab, 0xff, 0x8f, 0x13, 0x63, 0xe1, 0x46, 0xbe, 0xb5, 0x18, 0xc9, 0x0c, 0xab] /* securityKey */,
1608
+ 0xa4,
1609
+ 0x46,
1610
+ 0xd4,
1611
+ 0xd5 /* keyMic */,
1612
+ 0xe4,
1613
+ 0x04,
1614
+ 0x00,
1615
+ 0x00 /* outgoingCounter */,
1616
+ ]);
1617
+ const gpdHeader = Buffer.alloc(15);
1618
+ gpdHeader.writeUInt8(0b00000001, 0);
1619
+ gpdHeader.writeUInt8(sequenceNumber, 1);
1620
+ gpdHeader.writeUInt8(commandIdentifier, 2);
1621
+ gpdHeader.writeUInt16LE(0, 3);
1622
+ gpdHeader.writeUInt32LE(sourceId, 5);
1623
+ gpdHeader.writeUInt32LE(frameCounter, 9);
1624
+ gpdHeader.writeUInt8(gpdCommandId, 13);
1625
+ gpdHeader.writeUInt8(gpdCommandPayload.length, 14);
1626
+ const messageContents = Buffer.concat([gpdHeader, gpdCommandPayload]);
1627
+ const apsFrame: EmberApsFrame = {
1628
+ profileId: ZSpec.GP_PROFILE_ID,
1629
+ clusterId: Zcl.Clusters.greenPower.ID,
1630
+ sourceEndpoint: ZSpec.GP_ENDPOINT,
1631
+ destinationEndpoint: ZSpec.GP_ENDPOINT,
1632
+ options: 0, // not used
1633
+ groupId: ZSpec.GP_GROUP_ID,
1634
+ sequence: 0, // not used
1635
+ };
1636
+
1637
+ mockEzspEmitter.emit("incomingMessage", EmberIncomingMessageType.BROADCAST, apsFrame, gpdLink, nwkAddress, messageContents);
1638
+ await flushPromises();
1639
+
1640
+ const payload: ZclPayload = {
1641
+ header: Zcl.Header.fromBuffer(messageContents),
1642
+ data: messageContents,
1643
+ clusterID: apsFrame.clusterId,
1644
+ address: nwkAddress,
1645
+ endpoint: apsFrame.sourceEndpoint,
1646
+ linkquality: gpdLink,
1647
+ groupID: apsFrame.groupId,
1648
+ wasBroadcast: true,
1649
+ destinationEndpoint: apsFrame.destinationEndpoint,
1650
+ };
1651
+
1652
+ expect(spyResolveZCL).toHaveBeenCalledTimes(1);
1653
+ expect(spyResolveZCL).toHaveBeenCalledWith(payload);
1654
+ expect(spyEmit).toHaveBeenCalledWith("zclPayload", payload);
1655
+ });
1656
+
1657
+ it("Emits device joined on trust center join", async () => {
1658
+ const spyEmit = vi.spyOn(adapter, "emit");
1659
+ const newNodeId: NodeId = 1234;
1660
+ const newNodeEui64: Eui64 = "0x11223344eebbccaa";
1661
+ const status: EmberDeviceUpdate = EmberDeviceUpdate.STANDARD_SECURITY_UNSECURED_JOIN;
1662
+ const policyDecision: EmberJoinDecision = EmberJoinDecision.USE_PRECONFIGURED_KEY;
1663
+ const parentOfNewNodeId: NodeId = 4321;
1664
+
1665
+ mockEzspEmitter.emit("trustCenterJoin", newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId);
1666
+ await flushPromises();
1667
+
1668
+ expect(spyEmit).toHaveBeenCalledWith("deviceJoined", {
1669
+ networkAddress: newNodeId,
1670
+ ieeeAddr: newNodeEui64,
1671
+ } as DeviceJoinedPayload);
1672
+ });
1673
+
1674
+ it("Emits device leave on trust center join", async () => {
1675
+ const spyEmit = vi.spyOn(adapter, "emit");
1676
+ const newNodeId: NodeId = 1234;
1677
+ const newNodeEui64: Eui64 = "0x11223344eebbccaa";
1678
+ const status: EmberDeviceUpdate = EmberDeviceUpdate.DEVICE_LEFT;
1679
+ const policyDecision: EmberJoinDecision = EmberJoinDecision.NO_ACTION;
1680
+ const parentOfNewNodeId: NodeId = 0xffff;
1681
+
1682
+ mockEzspEmitter.emit("trustCenterJoin", newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId);
1683
+ await flushPromises();
1684
+
1685
+ expect(spyEmit).toHaveBeenCalledWith("deviceLeave", {
1686
+ networkAddress: newNodeId,
1687
+ ieeeAddr: newNodeEui64,
1688
+ } as DeviceLeavePayload);
1689
+ });
1690
+
1691
+ it("Handles DENY_JOIN on trust center join", async () => {
1692
+ const newNodeId: NodeId = 1234;
1693
+ const newNodeEui64: Eui64 = "0x11223344eebbccaa";
1694
+ const status: EmberDeviceUpdate = EmberDeviceUpdate.STANDARD_SECURITY_UNSECURED_JOIN;
1695
+ const policyDecision: EmberJoinDecision = EmberJoinDecision.DENY_JOIN;
1696
+ const parentOfNewNodeId: NodeId = 4321;
1697
+
1698
+ mockEzspEmitter.emit("trustCenterJoin", newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId);
1699
+ await flushPromises();
1700
+
1701
+ expect(loggerSpies.warning).toHaveBeenCalledWith(
1702
+ `[TRUST CENTER] Device ${newNodeId}:${newNodeEui64} was denied joining via ${parentOfNewNodeId}.`,
1703
+ "zh:ember",
1704
+ );
1705
+ });
1706
+
1707
+ it("Handles device join workaround requiring specific manufacturer code", async () => {
1708
+ const spyEmit = vi.spyOn(adapter, "emit");
1709
+ const newNodeId: NodeId = 1234;
1710
+ const newNodeEui64: Eui64 = "0x54ef44ffeebbccaa";
1711
+ const status: EmberDeviceUpdate = EmberDeviceUpdate.STANDARD_SECURITY_UNSECURED_JOIN;
1712
+ const policyDecision: EmberJoinDecision = EmberJoinDecision.USE_PRECONFIGURED_KEY;
1713
+ const parentOfNewNodeId: NodeId = 4321;
1714
+
1715
+ mockEzspEmitter.emit("trustCenterJoin", newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId);
1716
+ await flushPromises();
1717
+
1718
+ expect(spyEmit).toHaveBeenCalledWith("deviceJoined", {
1719
+ networkAddress: newNodeId,
1720
+ ieeeAddr: newNodeEui64,
1721
+ } as DeviceJoinedPayload);
1722
+ expect(mockEzspSetManufacturerCode).toHaveBeenCalledWith(Zcl.ManufacturerCode.LUMI_UNITED_TECHOLOGY_LTD_SHENZHEN);
1723
+ expect(mockManufCode).toStrictEqual(Zcl.ManufacturerCode.LUMI_UNITED_TECHOLOGY_LTD_SHENZHEN);
1724
+ });
1725
+
1726
+ it("Triggers watchdog counters", async () => {
1727
+ await vi.advanceTimersByTimeAsync(3610000);
1728
+ expect(mockEzspReadAndClearCounters).toHaveBeenCalledTimes(1);
1729
+ expect(loggerSpies.info).toHaveBeenCalledTimes(2);
1730
+ expect(loggerSpies.info.mock.calls[0][0]).toMatch(/[NCP COUNTERS]/);
1731
+ expect(loggerSpies.info.mock.calls[1][0]).toMatch(/[ASH COUNTERS]/);
1732
+ });
1733
+
1734
+ it("Exports link keys", async () => {
1735
+ const k1Context: SecManContext = {
1736
+ coreKeyType: SecManKeyType.APP_LINK,
1737
+ keyIndex: 0,
1738
+ derivedType: SecManDerivedKeyType.NONE,
1739
+ eui64: "0x1122334455667788",
1740
+ multiNetworkIndex: 0,
1741
+ flags: SecManFlag.EUI_IS_VALID | SecManFlag.KEY_INDEX_IS_VALID,
1742
+ psaKeyAlgPermission: 0,
1743
+ };
1744
+ const k1 = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
1745
+ const k1Hashed = ZSpec.Utils.aes128MmoHash(k1);
1746
+ const k1Metadata: SecManAPSKeyMetadata = {
1747
+ bitmask: EmberKeyStructBitmask.HAS_INCOMING_FRAME_COUNTER | EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
1748
+ outgoingFrameCounter: 1,
1749
+ incomingFrameCounter: 2,
1750
+ ttlInSeconds: 0,
1751
+ };
1752
+ const k2Context: SecManContext = {
1753
+ coreKeyType: SecManKeyType.APP_LINK,
1754
+ keyIndex: 1,
1755
+ derivedType: SecManDerivedKeyType.NONE,
1756
+ eui64: "0x2233445566778899",
1757
+ multiNetworkIndex: 0,
1758
+ flags: SecManFlag.EUI_IS_VALID | SecManFlag.KEY_INDEX_IS_VALID,
1759
+ psaKeyAlgPermission: 0,
1760
+ };
1761
+ const k2 = Buffer.from([2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]);
1762
+ const k2Hashed = ZSpec.Utils.aes128MmoHash(k2);
1763
+ const k2Metadata: SecManAPSKeyMetadata = {
1764
+ bitmask: EmberKeyStructBitmask.HAS_INCOMING_FRAME_COUNTER | EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
1765
+ outgoingFrameCounter: 10,
1766
+ incomingFrameCounter: 20,
1767
+ ttlInSeconds: 0,
1768
+ };
1769
+ const k3Context: SecManContext = {
1770
+ coreKeyType: SecManKeyType.APP_LINK,
1771
+ keyIndex: 2,
1772
+ derivedType: SecManDerivedKeyType.NONE,
1773
+ eui64: "0x3344556677889900",
1774
+ multiNetworkIndex: 0,
1775
+ flags: SecManFlag.EUI_IS_VALID | SecManFlag.KEY_INDEX_IS_VALID,
1776
+ psaKeyAlgPermission: 0,
1777
+ };
1778
+ const k3 = Buffer.from([3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]);
1779
+ const k3Hashed = ZSpec.Utils.aes128MmoHash(k3);
1780
+ const k3Metadata: SecManAPSKeyMetadata = {
1781
+ bitmask: EmberKeyStructBitmask.HAS_INCOMING_FRAME_COUNTER | EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
1782
+ outgoingFrameCounter: 100,
1783
+ incomingFrameCounter: 200,
1784
+ ttlInSeconds: 0,
1785
+ };
1786
+
1787
+ mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.OK, 3]);
1788
+ mockEzspExportLinkKeyByIndex
1789
+ .mockResolvedValueOnce([SLStatus.OK, k1Context, {contents: k1} as SecManKey, k1Metadata])
1790
+ .mockResolvedValueOnce([SLStatus.OK, k2Context, {contents: k2} as SecManKey, k2Metadata])
1791
+ .mockResolvedValueOnce([SLStatus.OK, k3Context, {contents: k3} as SecManKey, k3Metadata]);
1792
+
1793
+ const keys = await adapter.exportLinkKeys();
1794
+
1795
+ expect(mockEzspExportLinkKeyByIndex).toHaveBeenCalledTimes(3);
1796
+ expect(keys).toStrictEqual([
1797
+ {
1798
+ deviceEui64: k1Context.eui64,
1799
+ key: {contents: k1Hashed},
1800
+ outgoingFrameCounter: k1Metadata.outgoingFrameCounter,
1801
+ incomingFrameCounter: k1Metadata.incomingFrameCounter,
1802
+ } as LinkKeyBackupData,
1803
+ {
1804
+ deviceEui64: k2Context.eui64,
1805
+ key: {contents: k2Hashed},
1806
+ outgoingFrameCounter: k2Metadata.outgoingFrameCounter,
1807
+ incomingFrameCounter: k2Metadata.incomingFrameCounter,
1808
+ } as LinkKeyBackupData,
1809
+ {
1810
+ deviceEui64: k3Context.eui64,
1811
+ key: {contents: k3Hashed},
1812
+ outgoingFrameCounter: k3Metadata.outgoingFrameCounter,
1813
+ incomingFrameCounter: k3Metadata.incomingFrameCounter,
1814
+ } as LinkKeyBackupData,
1815
+ ]);
1816
+ });
1817
+
1818
+ it("Exports zero link keys", async () => {
1819
+ mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.OK, 0]);
1820
+ const keys = await adapter.exportLinkKeys();
1821
+
1822
+ expect(keys).toStrictEqual([]);
1823
+ });
1824
+
1825
+ it("Fails to export link keys due to failed table size retrieval", async () => {
1826
+ mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.FAIL, 0]);
1827
+
1828
+ await expect(adapter.exportLinkKeys()).rejects.toThrow("[BACKUP] Failed to retrieve key table size from NCP with status=FAIL.");
1829
+ });
1830
+
1831
+ it("Imports link keys", async () => {
1832
+ const k1Context: SecManContext = {
1833
+ coreKeyType: SecManKeyType.APP_LINK,
1834
+ keyIndex: 0,
1835
+ derivedType: SecManDerivedKeyType.NONE,
1836
+ eui64: "0x1122334455667788",
1837
+ multiNetworkIndex: 0,
1838
+ flags: SecManFlag.EUI_IS_VALID | SecManFlag.KEY_INDEX_IS_VALID,
1839
+ psaKeyAlgPermission: 0,
1840
+ };
1841
+ const k1 = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
1842
+ const k1Metadata: SecManAPSKeyMetadata = {
1843
+ bitmask: EmberKeyStructBitmask.HAS_INCOMING_FRAME_COUNTER | EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
1844
+ outgoingFrameCounter: 1,
1845
+ incomingFrameCounter: 2,
1846
+ ttlInSeconds: 0,
1847
+ };
1848
+ const k2Context: SecManContext = {
1849
+ coreKeyType: SecManKeyType.APP_LINK,
1850
+ keyIndex: 1,
1851
+ derivedType: SecManDerivedKeyType.NONE,
1852
+ eui64: "0x2233445566778899",
1853
+ multiNetworkIndex: 0,
1854
+ flags: SecManFlag.EUI_IS_VALID | SecManFlag.KEY_INDEX_IS_VALID,
1855
+ psaKeyAlgPermission: 0,
1856
+ };
1857
+ const k2 = Buffer.from([2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]);
1858
+ const k2Metadata: SecManAPSKeyMetadata = {
1859
+ bitmask: EmberKeyStructBitmask.HAS_INCOMING_FRAME_COUNTER | EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
1860
+ outgoingFrameCounter: 10,
1861
+ incomingFrameCounter: 20,
1862
+ ttlInSeconds: 0,
1863
+ };
1864
+ const k3Context: SecManContext = {
1865
+ coreKeyType: SecManKeyType.APP_LINK,
1866
+ keyIndex: 2,
1867
+ derivedType: SecManDerivedKeyType.NONE,
1868
+ eui64: "0x3344556677889900",
1869
+ multiNetworkIndex: 0,
1870
+ flags: SecManFlag.EUI_IS_VALID | SecManFlag.KEY_INDEX_IS_VALID,
1871
+ psaKeyAlgPermission: 0,
1872
+ };
1873
+ const k3 = Buffer.from([3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]);
1874
+ const k3Metadata: SecManAPSKeyMetadata = {
1875
+ bitmask: EmberKeyStructBitmask.HAS_INCOMING_FRAME_COUNTER | EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
1876
+ outgoingFrameCounter: 100,
1877
+ incomingFrameCounter: 200,
1878
+ ttlInSeconds: 0,
1879
+ };
1880
+ mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.OK, 4]);
1881
+ mockEzspNetworkState.mockResolvedValueOnce(EmberNetworkStatus.NO_NETWORK);
1882
+
1883
+ await adapter.importLinkKeys([
1884
+ {
1885
+ deviceEui64: k1Context.eui64,
1886
+ key: {contents: k1},
1887
+ outgoingFrameCounter: k1Metadata.outgoingFrameCounter,
1888
+ incomingFrameCounter: k1Metadata.incomingFrameCounter,
1889
+ },
1890
+ {
1891
+ deviceEui64: k2Context.eui64,
1892
+ key: {contents: k2},
1893
+ outgoingFrameCounter: k2Metadata.outgoingFrameCounter,
1894
+ incomingFrameCounter: k2Metadata.incomingFrameCounter,
1895
+ },
1896
+ {
1897
+ deviceEui64: k3Context.eui64,
1898
+ key: {contents: k3},
1899
+ outgoingFrameCounter: k3Metadata.outgoingFrameCounter,
1900
+ incomingFrameCounter: k3Metadata.incomingFrameCounter,
1901
+ },
1902
+ ]);
1903
+
1904
+ expect(mockEzspImportLinkKey).toHaveBeenCalledTimes(3);
1905
+ expect(mockEzspEraseKeyTableEntry).toHaveBeenCalledTimes(1);
1906
+ });
1907
+
1908
+ it("Imports zero link keys", async () => {
1909
+ await expect(adapter.importLinkKeys([])).resolves.toStrictEqual(undefined);
1910
+ });
1911
+
1912
+ it("Failed to import link keys due to failed table size retrieval", async () => {
1913
+ mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.FAIL, 0]);
1914
+
1915
+ await expect(
1916
+ adapter.importLinkKeys([
1917
+ // @ts-expect-error mock, unnecessary
1918
+ {},
1919
+ ]),
1920
+ ).rejects.toThrow("[BACKUP] Failed to retrieve key table size from NCP with status=FAIL.");
1921
+ });
1922
+
1923
+ it("Failed to import link keys due to insufficient table size", async () => {
1924
+ mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.OK, 0]);
1925
+
1926
+ await expect(
1927
+ adapter.importLinkKeys([
1928
+ // @ts-expect-error mock, unnecessary
1929
+ {},
1930
+ ]),
1931
+ ).rejects.toThrow("[BACKUP] Current key table of 0 is too small to import backup of 1!");
1932
+ });
1933
+
1934
+ it("Failed to import link keys due to improper network state", async () => {
1935
+ mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.OK, 3]);
1936
+ mockEzspNetworkState.mockResolvedValueOnce(EmberNetworkStatus.JOINED_NETWORK);
1937
+
1938
+ await expect(
1939
+ adapter.importLinkKeys([
1940
+ // @ts-expect-error mock, unnecessary
1941
+ {},
1942
+ ]),
1943
+ ).rejects.toThrow(
1944
+ `[BACKUP] Cannot import TC data while network is up, networkStatus=${EmberNetworkStatus[EmberNetworkStatus.JOINED_NETWORK]}.`,
1945
+ );
1946
+ });
1947
+
1948
+ it("Failed to import link keys due to failed key set", async () => {
1949
+ const k1Context: SecManContext = {
1950
+ coreKeyType: SecManKeyType.APP_LINK,
1951
+ keyIndex: 0,
1952
+ derivedType: SecManDerivedKeyType.NONE,
1953
+ eui64: "0x1122334455667788",
1954
+ multiNetworkIndex: 0,
1955
+ flags: SecManFlag.EUI_IS_VALID | SecManFlag.KEY_INDEX_IS_VALID,
1956
+ psaKeyAlgPermission: 0,
1957
+ };
1958
+ const k1 = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
1959
+ const k1Metadata: SecManAPSKeyMetadata = {
1960
+ bitmask: EmberKeyStructBitmask.HAS_INCOMING_FRAME_COUNTER | EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
1961
+ outgoingFrameCounter: 1,
1962
+ incomingFrameCounter: 2,
1963
+ ttlInSeconds: 0,
1964
+ };
1965
+
1966
+ mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.OK, 3]);
1967
+ mockEzspNetworkState.mockResolvedValueOnce(EmberNetworkStatus.NO_NETWORK);
1968
+ mockEzspImportLinkKey.mockResolvedValueOnce(SLStatus.FAIL);
1969
+
1970
+ await expect(
1971
+ adapter.importLinkKeys([
1972
+ {
1973
+ deviceEui64: k1Context.eui64,
1974
+ key: {contents: k1},
1975
+ outgoingFrameCounter: k1Metadata.outgoingFrameCounter,
1976
+ incomingFrameCounter: k1Metadata.incomingFrameCounter,
1977
+ },
1978
+ ]),
1979
+ ).rejects.toThrow("[BACKUP] Failed to set key table entry at index 0 with status=FAIL.");
1980
+ });
1981
+
1982
+ it("Failed to import link keys due to failed key erase", async () => {
1983
+ const k1Context: SecManContext = {
1984
+ coreKeyType: SecManKeyType.APP_LINK,
1985
+ keyIndex: 0,
1986
+ derivedType: SecManDerivedKeyType.NONE,
1987
+ eui64: "0x1122334455667788",
1988
+ multiNetworkIndex: 0,
1989
+ flags: SecManFlag.EUI_IS_VALID | SecManFlag.KEY_INDEX_IS_VALID,
1990
+ psaKeyAlgPermission: 0,
1991
+ };
1992
+ const k1 = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
1993
+ const k1Metadata: SecManAPSKeyMetadata = {
1994
+ bitmask: EmberKeyStructBitmask.HAS_INCOMING_FRAME_COUNTER | EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
1995
+ outgoingFrameCounter: 1,
1996
+ incomingFrameCounter: 2,
1997
+ ttlInSeconds: 0,
1998
+ };
1999
+
2000
+ mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.OK, 3]);
2001
+ mockEzspNetworkState.mockResolvedValueOnce(EmberNetworkStatus.NO_NETWORK);
2002
+ mockEzspEraseKeyTableEntry.mockResolvedValueOnce(SLStatus.FAIL);
2003
+
2004
+ await expect(
2005
+ adapter.importLinkKeys([
2006
+ {
2007
+ deviceEui64: k1Context.eui64,
2008
+ key: {contents: k1},
2009
+ outgoingFrameCounter: k1Metadata.outgoingFrameCounter,
2010
+ incomingFrameCounter: k1Metadata.incomingFrameCounter,
2011
+ },
2012
+ ]),
2013
+ ).rejects.toThrow("[BACKUP] Failed to erase key table entry at index 1 with status=FAIL.");
2014
+ });
2015
+
2016
+ it("Broadcasts network key update", async () => {
2017
+ const p = adapter.broadcastNetworkKeyUpdate();
2018
+
2019
+ await vi.advanceTimersByTimeAsync(100000);
2020
+ await expect(p).resolves.toStrictEqual(undefined);
2021
+ expect(mockEzspBroadcastNextNetworkKey).toHaveBeenCalledTimes(1);
2022
+ expect(mockEzspBroadcastNetworkKeySwitch).toHaveBeenCalledTimes(1);
2023
+ });
2024
+
2025
+ it("Fails to broadcast network key update due to failed next key broadcast", async () => {
2026
+ mockEzspBroadcastNextNetworkKey.mockResolvedValueOnce(SLStatus.FAIL);
2027
+
2028
+ const p = defuseRejection(adapter.broadcastNetworkKeyUpdate());
2029
+
2030
+ await vi.advanceTimersByTimeAsync(100000);
2031
+ await expect(p).rejects.toThrow("[TRUST CENTER] Failed to broadcast next network key with status=FAIL.");
2032
+ expect(mockEzspBroadcastNextNetworkKey).toHaveBeenCalledTimes(1);
2033
+ expect(mockEzspBroadcastNetworkKeySwitch).toHaveBeenCalledTimes(0);
2034
+ });
2035
+
2036
+ it("Fails to broadcast network key update due to failed switch broadcast", async () => {
2037
+ mockEzspBroadcastNetworkKeySwitch.mockResolvedValueOnce(SLStatus.FAIL);
2038
+
2039
+ const p = defuseRejection(adapter.broadcastNetworkKeyUpdate());
2040
+
2041
+ await vi.advanceTimersByTimeAsync(100000);
2042
+ await expect(p).rejects.toThrow("[TRUST CENTER] Failed to broadcast network key switch with status=FAIL.");
2043
+ expect(mockEzspBroadcastNextNetworkKey).toHaveBeenCalledTimes(1);
2044
+ expect(mockEzspBroadcastNetworkKeySwitch).toHaveBeenCalledTimes(1);
2045
+ });
2046
+
2047
+ it("Handles NCP needing reset & init", async () => {
2048
+ const spyEmit = vi.spyOn(adapter, "emit");
2049
+
2050
+ mockEzspEmitter.emit("ncpNeedsResetAndInit", EzspStatus.ERROR_SERIAL_INIT);
2051
+ await vi.advanceTimersByTimeAsync(5000);
2052
+
2053
+ expect(spyEmit).toHaveBeenCalledTimes(1);
2054
+ expect(spyEmit).toHaveBeenCalledWith("disconnected");
2055
+ });
2056
+
2057
+ it("Emits adapter disconnected when NCP needs reset & init but queue is too high", async () => {
2058
+ vi.spyOn(
2059
+ // @ts-expect-error private
2060
+ adapter.queue,
2061
+ "count",
2062
+ ).mockReturnValueOnce(999);
2063
+ const spyEmit = vi.spyOn(adapter, "emit");
2064
+
2065
+ mockEzspEmitter.emit("ncpNeedsResetAndInit", EzspStatus.ERROR_SERIAL_INIT);
2066
+ await flushPromises();
2067
+
2068
+ expect(spyEmit).toHaveBeenCalledWith("disconnected");
2069
+ });
2070
+
2071
+ it("Emits adapter disconnected when failed to reset & init NCP", async () => {
2072
+ vi.spyOn(adapter, "stop").mockRejectedValueOnce(new Error("mock error"));
2073
+ const spyEmit = vi.spyOn(adapter, "emit");
2074
+
2075
+ mockEzspEmitter.emit("ncpNeedsResetAndInit", EzspStatus.ERROR_SERIAL_INIT);
2076
+ await flushPromises();
2077
+
2078
+ expect(spyEmit).toHaveBeenCalledWith("disconnected");
2079
+ });
2080
+
2081
+ it("Handles channel changed stack status", async () => {
2082
+ mockEzspEmitter.emit("stackStatus", SLStatus.ZIGBEE_CHANNEL_CHANGED);
2083
+ await flushPromises();
2084
+ expect(loggerSpies.info).toHaveBeenCalledWith("[STACK STATUS] Channel changed.", "zh:ember");
2085
+ });
2086
+
2087
+ it.each([
2088
+ ["getCoordinatorIEEE", []],
2089
+ ["getNetworkParameters", []],
2090
+ ["permitJoin", [250, 1234]],
2091
+ ["permitJoin", [250]],
2092
+ [
2093
+ "sendZclFrameToEndpoint",
2094
+ [
2095
+ "0x1122334455667788",
2096
+ 1234,
2097
+ 1,
2098
+ Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {}),
2099
+ 10000,
2100
+ true,
2101
+ false,
2102
+ 1,
2103
+ ],
2104
+ ],
2105
+ [
2106
+ "sendZclFrameToGroup",
2107
+ [32, Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {}), 1],
2108
+ ],
2109
+ [
2110
+ "sendZclFrameToAll",
2111
+ [
2112
+ 1,
2113
+ Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {}),
2114
+ 1,
2115
+ ZSpec.BroadcastAddress.DEFAULT,
2116
+ ],
2117
+ ],
2118
+ ])("Adapter impl: throws when using non-InterPAN function %s while in InterPAN mode", async (funcName, args) => {
2119
+ await adapter.setChannelInterPAN(15);
2120
+
2121
+ await expect(
2122
+ // @ts-expect-error mock
2123
+ adapter[funcName](...args),
2124
+ ).rejects.toThrow("[INTERPAN MODE] Cannot execute non-InterPAN commands.");
2125
+ });
2126
+
2127
+ it("Adapter impl: getCoordinatorIEEE", async () => {
2128
+ await expect(adapter.getCoordinatorIEEE()).resolves.toStrictEqual(DEFAULT_COORDINATOR_IEEE);
2129
+ });
2130
+
2131
+ it("Adapter impl: getCoordinatorVersion", async () => {
2132
+ await expect(adapter.getCoordinatorVersion()).resolves.toStrictEqual({
2133
+ type: "EmberZNet",
2134
+ meta: {
2135
+ ezsp: EZSP_PROTOCOL_VERSION,
2136
+ revision: `8.0.0 [${EmberVersionType[EmberVersionType.GA]}]`,
2137
+ build: 135,
2138
+ major: 8,
2139
+ minor: 0,
2140
+ patch: 0,
2141
+ special: 0,
2142
+ type: EmberVersionType.GA,
2143
+ },
2144
+ } as TsType.CoordinatorVersion);
2145
+ });
2146
+
2147
+ it("Adapter impl: reset soft", async () => {
2148
+ await expect(adapter.reset("soft")).rejects.toThrow(`Not supported 'soft'.`);
2149
+ });
2150
+
2151
+ it("Adapter impl: reset hard", async () => {
2152
+ await expect(adapter.reset("hard")).rejects.toThrow(`Not supported 'hard'.`);
2153
+ });
2154
+
2155
+ it("Adapter impl: supportsBackup", async () => {
2156
+ await expect(adapter.supportsBackup()).resolves.toStrictEqual(true);
2157
+ });
2158
+
2159
+ it("Adapter impl: backup", async () => {
2160
+ await expect(adapter.backup([])).resolves.toStrictEqual({
2161
+ networkOptions: {
2162
+ panId: DEFAULT_NETWORK_OPTIONS.panID, // uint16_t
2163
+ extendedPanId: Buffer.from(DEFAULT_NETWORK_OPTIONS.extendedPanID!),
2164
+ channelList: ZSpec.ALL_802_15_4_CHANNELS.slice(),
2165
+ networkKey: Buffer.from(DEFAULT_BACKUP.network_key.key, "hex"),
2166
+ networkKeyDistribute: false,
2167
+ },
2168
+ logicalChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
2169
+ networkKeyInfo: {
2170
+ sequenceNumber: DEFAULT_BACKUP.network_key.sequence_number,
2171
+ frameCounter: DEFAULT_BACKUP.network_key.frame_counter,
2172
+ },
2173
+ securityLevel: SECURITY_LEVEL_Z3,
2174
+ networkUpdateId: 0,
2175
+ coordinatorIeeeAddress: Buffer.from(DEFAULT_BACKUP.coordinator_ieee, "hex"),
2176
+ devices: [],
2177
+ ezsp: {
2178
+ version: EZSP_PROTOCOL_VERSION,
2179
+ hashed_tclk: Buffer.from(DEFAULT_BACKUP.stack_specific!.ezsp!.hashed_tclk!, "hex"),
2180
+ },
2181
+ } as Backup);
2182
+ });
2183
+
2184
+ it.each([
2185
+ [
2186
+ "failed get network parameters",
2187
+ () => {
2188
+ mockEzspGetNetworkParameters.mockResolvedValueOnce([SLStatus.FAIL, 0, {}]);
2189
+ },
2190
+ "[BACKUP] Failed to get network parameters with status=FAIL.",
2191
+ ],
2192
+ [
2193
+ "failed get network key info",
2194
+ () => {
2195
+ mockEzspGetNetworkKeyInfo.mockResolvedValueOnce([SLStatus.FAIL, {}]);
2196
+ },
2197
+ "[BACKUP] Failed to get network keys info with status=FAIL.",
2198
+ ],
2199
+ // [
2200
+ // 'failed get TC APS key info',
2201
+ // () => {
2202
+ // mockEzspGetNetworkKeyInfo.mockResolvedValueOnce([SLStatus.FAIL, {}]);
2203
+ // },
2204
+ // `[BACKUP] Failed to get TC APS key info with status=FAIL.`,
2205
+ // ],
2206
+ [
2207
+ "no network key set",
2208
+ () => {
2209
+ mockEzspGetNetworkKeyInfo.mockResolvedValueOnce([
2210
+ SLStatus.OK,
2211
+ {
2212
+ networkKeySet: false,
2213
+ alternateNetworkKeySet: false,
2214
+ networkKeySequenceNumber: 123,
2215
+ altNetworkKeySequenceNumber: 0,
2216
+ networkKeyFrameCounter: 456,
2217
+ } as SecManNetworkKeyInfo,
2218
+ ]);
2219
+ },
2220
+ "[BACKUP] No network key set.",
2221
+ ],
2222
+ [
2223
+ "failed export TC link key",
2224
+ () => {
2225
+ mockEzspExportKey.mockResolvedValueOnce([SLStatus.FAIL, {}]);
2226
+ },
2227
+ "[BACKUP] Failed to export TC Link Key with status=FAIL.",
2228
+ ],
2229
+ [
2230
+ "failed export network key",
2231
+ () => {
2232
+ mockEzspExportKey
2233
+ .mockResolvedValueOnce([
2234
+ SLStatus.OK,
2235
+ {contents: Buffer.from(DEFAULT_BACKUP.stack_specific!.ezsp!.hashed_tclk!, "hex")} as SecManKey,
2236
+ ])
2237
+ .mockResolvedValueOnce([SLStatus.FAIL, {}]);
2238
+ },
2239
+ "[BACKUP] Failed to export Network Key with status=FAIL.",
2240
+ ],
2241
+ ])("Adapter impl: throws when backup fails due to %s", async (_command, setup, error) => {
2242
+ setup();
2243
+
2244
+ await expect(adapter.backup([])).rejects.toThrow(error);
2245
+ });
2246
+
2247
+ it("Adapter impl: getNetworkParameters from cache", async () => {
2248
+ await expect(adapter.getNetworkParameters()).resolves.toStrictEqual({
2249
+ panID: DEFAULT_NETWORK_OPTIONS.panID,
2250
+ extendedPanID: ZSpec.Utils.eui64LEBufferToHex(Buffer.from(DEFAULT_NETWORK_OPTIONS.extendedPanID!)),
2251
+ channel: DEFAULT_NETWORK_OPTIONS.channelList[0],
2252
+ nwkUpdateID: 0,
2253
+ } as TsType.NetworkParameters);
2254
+ expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(0);
2255
+ });
2256
+
2257
+ it("Adapter impl: getNetworkParameters from NCP", async () => {
2258
+ adapter.clearNetworkCache();
2259
+
2260
+ await expect(adapter.getNetworkParameters()).resolves.toStrictEqual({
2261
+ panID: DEFAULT_NETWORK_OPTIONS.panID,
2262
+ extendedPanID: ZSpec.Utils.eui64LEBufferToHex(Buffer.from(DEFAULT_NETWORK_OPTIONS.extendedPanID!)),
2263
+ channel: DEFAULT_NETWORK_OPTIONS.channelList[0],
2264
+ nwkUpdateID: 0,
2265
+ } as TsType.NetworkParameters);
2266
+ expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(1);
2267
+ });
2268
+
2269
+ it("Adapter impl: addInstallCode", async () => {
2270
+ await expect(
2271
+ adapter.addInstallCode("0x1122334455667788", Buffer.from("DD7ED5CDAA8E2C708B67D2B1573DB6843A5F", "hex"), false),
2272
+ ).resolves.toStrictEqual(undefined);
2273
+ expect(mockEzspImportTransientKey).toHaveBeenCalledTimes(1);
2274
+ expect(loggerSpies.debug).toHaveBeenCalledWith(`[ADD INSTALL CODE] Success for '0x1122334455667788'.`, "zh:ember");
2275
+ });
2276
+
2277
+ it("Adapter impl: throw when addInstallCode fails import transient key", async () => {
2278
+ mockEzspImportTransientKey.mockResolvedValueOnce(SLStatus.FAIL);
2279
+
2280
+ await expect(adapter.addInstallCode("0x1122334455667788", Buffer.alloc(16), true)).rejects.toThrow(
2281
+ `[ADD INSTALL CODE] Failed for '0x1122334455667788' with status=FAIL.`,
2282
+ );
2283
+ expect(mockEzspImportTransientKey).toHaveBeenCalledTimes(1);
2284
+ });
2285
+
2286
+ it("Adapter impl: waitFor", () => {
2287
+ const waiter = adapter.waitFor(1234, 1, Zcl.FrameType.GLOBAL, Zcl.Direction.CLIENT_TO_SERVER, 10, 0, 1, 15000);
2288
+ const spyCancel = vi.spyOn(waiter, "cancel");
2289
+
2290
+ expect(typeof waiter.cancel).toStrictEqual("function");
2291
+ expect(waiter.promise).toBeDefined();
2292
+
2293
+ waiter.cancel();
2294
+
2295
+ expect(spyCancel).toHaveReturned();
2296
+ });
2297
+
2298
+ it("Adapter impl: permitJoin on all", async () => {
2299
+ const spyResolveEvent = vi.spyOn(
2300
+ // @ts-expect-error private
2301
+ adapter.oneWaitress,
2302
+ "resolveEvent",
2303
+ );
2304
+
2305
+ await adapter.permitJoin(250);
2306
+ await vi.advanceTimersByTimeAsync(1000);
2307
+ expect(mockEzspPermitJoining).toHaveBeenCalledWith(250);
2308
+ expect(mockEzspSendBroadcast).toHaveBeenCalledTimes(1);
2309
+ expect(spyResolveEvent).toHaveBeenCalledWith(OneWaitressEvents.STACK_STATUS_NETWORK_OPENED);
2310
+
2311
+ await adapter.permitJoin(0);
2312
+ await vi.advanceTimersByTimeAsync(1000);
2313
+ expect(mockEzspPermitJoining).toHaveBeenCalledWith(0);
2314
+ expect(mockEzspSendBroadcast).toHaveBeenCalledTimes(2);
2315
+ expect(spyResolveEvent).toHaveBeenCalledWith(OneWaitressEvents.STACK_STATUS_NETWORK_CLOSED);
2316
+
2317
+ expect(mockEzspSetPolicy).toHaveBeenNthCalledWith(
2318
+ 1,
2319
+ EzspPolicyId.TRUST_CENTER_POLICY,
2320
+ EzspDecisionBitmask.ALLOW_JOINS | EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS,
2321
+ );
2322
+ expect(mockEzspSetPolicy).toHaveBeenNthCalledWith(2, EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS);
2323
+ });
2324
+
2325
+ it("Adapter impl: permitJoin on coordinator", async () => {
2326
+ const spyResolveEvent = vi.spyOn(
2327
+ // @ts-expect-error private
2328
+ adapter.oneWaitress,
2329
+ "resolveEvent",
2330
+ );
2331
+
2332
+ await adapter.permitJoin(250, ZSpec.COORDINATOR_ADDRESS);
2333
+ await vi.advanceTimersByTimeAsync(1000);
2334
+ expect(mockEzspPermitJoining).toHaveBeenCalledWith(250);
2335
+ expect(mockEzspSendBroadcast).toHaveBeenCalledTimes(0);
2336
+ expect(spyResolveEvent).toHaveBeenCalledWith(OneWaitressEvents.STACK_STATUS_NETWORK_OPENED);
2337
+
2338
+ await adapter.permitJoin(0, ZSpec.COORDINATOR_ADDRESS);
2339
+ await vi.advanceTimersByTimeAsync(1000);
2340
+ expect(mockEzspPermitJoining).toHaveBeenCalledWith(0);
2341
+ expect(mockEzspSendBroadcast).toHaveBeenCalledTimes(0);
2342
+ expect(spyResolveEvent).toHaveBeenCalledWith(OneWaitressEvents.STACK_STATUS_NETWORK_CLOSED);
2343
+
2344
+ expect(mockEzspSetPolicy).toHaveBeenNthCalledWith(
2345
+ 1,
2346
+ EzspPolicyId.TRUST_CENTER_POLICY,
2347
+ EzspDecisionBitmask.ALLOW_JOINS | EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS,
2348
+ );
2349
+ expect(mockEzspSetPolicy).toHaveBeenNthCalledWith(2, EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS);
2350
+ });
2351
+
2352
+ it("Adapter impl: permitJoin on router", async () => {
2353
+ const spyResolveZDO = vi.spyOn(
2354
+ // @ts-expect-error private
2355
+ adapter.oneWaitress,
2356
+ "resolveZDO",
2357
+ );
2358
+ const sender = 1234;
2359
+ const apsFrame: EmberApsFrame = {
2360
+ profileId: Zdo.ZDO_PROFILE_ID,
2361
+ clusterId: Zdo.ClusterId.PERMIT_JOINING_RESPONSE,
2362
+ sourceEndpoint: Zdo.ZDO_ENDPOINT,
2363
+ destinationEndpoint: Zdo.ZDO_ENDPOINT,
2364
+ options: 0,
2365
+ groupId: 0,
2366
+ sequence: 0,
2367
+ };
2368
+ const emitResponse = () => {
2369
+ setTimeout(async () => {
2370
+ mockEzspEmitter.emit("zdoResponse", apsFrame, sender, Buffer.from([1, Zdo.Status.SUCCESS]));
2371
+ await flushPromises();
2372
+ }, 300);
2373
+
2374
+ return [SLStatus.OK, ++mockAPSSequence];
2375
+ };
2376
+
2377
+ mockEzspSendUnicast.mockImplementationOnce(emitResponse).mockImplementationOnce(emitResponse);
2378
+
2379
+ const zdoResponse = [Zdo.Status.SUCCESS, undefined];
2380
+ let p = adapter.permitJoin(250, sender);
2381
+ await vi.advanceTimersByTimeAsync(1000);
2382
+ await p;
2383
+ expect(mockEzspSendUnicast).toHaveBeenCalledTimes(1);
2384
+ expect(spyResolveZDO).toHaveBeenCalledTimes(1);
2385
+ expect(spyResolveZDO).toHaveBeenCalledWith(sender, apsFrame, zdoResponse);
2386
+
2387
+ p = adapter.permitJoin(0, sender);
2388
+ await vi.advanceTimersByTimeAsync(1000);
2389
+ await p;
2390
+ expect(mockEzspSendUnicast).toHaveBeenCalledTimes(2);
2391
+ expect(spyResolveZDO).toHaveBeenCalledTimes(2);
2392
+ expect(spyResolveZDO).toHaveBeenCalledWith(sender, apsFrame, zdoResponse);
2393
+
2394
+ expect(mockEzspSetPolicy).toHaveBeenNthCalledWith(
2395
+ 1,
2396
+ EzspPolicyId.TRUST_CENTER_POLICY,
2397
+ EzspDecisionBitmask.ALLOW_JOINS | EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS,
2398
+ );
2399
+ expect(mockEzspSetPolicy).toHaveBeenNthCalledWith(2, EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS);
2400
+ });
2401
+
2402
+ it("Adapter impl: permitJoin restores temp manufacturer code", async () => {
2403
+ const spyResolveEvent = vi.spyOn(
2404
+ // @ts-expect-error private
2405
+ adapter.oneWaitress,
2406
+ "resolveEvent",
2407
+ );
2408
+
2409
+ const newNodeId: NodeId = 1234;
2410
+ const newNodeEui64: Eui64 = "0x54ef44ffeebbccaa";
2411
+ const status: EmberDeviceUpdate = EmberDeviceUpdate.STANDARD_SECURITY_UNSECURED_JOIN;
2412
+ const policyDecision: EmberJoinDecision = EmberJoinDecision.USE_PRECONFIGURED_KEY;
2413
+ const parentOfNewNodeId: NodeId = 4321;
2414
+
2415
+ mockEzspEmitter.emit("trustCenterJoin", newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId);
2416
+ await flushPromises();
2417
+
2418
+ expect(mockEzspSetManufacturerCode).toHaveBeenCalledWith(Zcl.ManufacturerCode.LUMI_UNITED_TECHOLOGY_LTD_SHENZHEN);
2419
+ expect(mockManufCode).toStrictEqual(Zcl.ManufacturerCode.LUMI_UNITED_TECHOLOGY_LTD_SHENZHEN);
2420
+
2421
+ await adapter.permitJoin(0, ZSpec.COORDINATOR_ADDRESS);
2422
+ await vi.advanceTimersByTimeAsync(1000);
2423
+ expect(mockEzspPermitJoining).toHaveBeenCalledWith(0);
2424
+ expect(mockEzspSendBroadcast).toHaveBeenCalledTimes(0);
2425
+ expect(spyResolveEvent).toHaveBeenCalledWith(OneWaitressEvents.STACK_STATUS_NETWORK_CLOSED);
2426
+ expect(mockEzspSetManufacturerCode).toHaveBeenCalledWith(Zcl.ManufacturerCode.SILICON_LABORATORIES);
2427
+ expect(mockManufCode).toStrictEqual(Zcl.ManufacturerCode.SILICON_LABORATORIES);
2428
+ });
2429
+
2430
+ it("Adapter impl: throws when permitJoin request on coordinator fails", async () => {
2431
+ mockEzspPermitJoining.mockResolvedValueOnce(SLStatus.FAIL);
2432
+
2433
+ await expect(adapter.permitJoin(250, 0)).rejects.toThrow("[ZDO] Failed coordinator permit joining request with status=FAIL.");
2434
+ });
2435
+
2436
+ it("Adapter impl: throws when permitJoin broadcast request fails", async () => {
2437
+ mockEzspSendBroadcast.mockResolvedValueOnce([SLStatus.FAIL, 0]);
2438
+
2439
+ await expect(defuseRejection(adapter.permitJoin(250, undefined))).rejects.toThrow(
2440
+ "~x~> [ZDO PERMIT_JOINING_REQUEST BROADCAST to=65532 messageTag=1] Failed to send request with status=FAIL.",
2441
+ );
2442
+ });
2443
+
2444
+ it("Adapter impl: resolves undefined when permitJoin on router fails due to failed ZDO status", async () => {
2445
+ const spyResolveZDO = vi.spyOn(
2446
+ // @ts-expect-error private
2447
+ adapter.oneWaitress,
2448
+ "resolveZDO",
2449
+ );
2450
+ const sender = 1234;
2451
+ const apsFrame: EmberApsFrame = {
2452
+ profileId: Zdo.ZDO_PROFILE_ID,
2453
+ clusterId: Zdo.ClusterId.PERMIT_JOINING_RESPONSE,
2454
+ sourceEndpoint: Zdo.ZDO_ENDPOINT,
2455
+ destinationEndpoint: Zdo.ZDO_ENDPOINT,
2456
+ options: 0,
2457
+ groupId: 0,
2458
+ sequence: 0,
2459
+ };
2460
+
2461
+ mockEzspEmitter.emit("zdoResponse", apsFrame, sender, Buffer.from([1, Zdo.Status.NOT_AUTHORIZED]));
2462
+ await flushPromises();
2463
+
2464
+ const zdoResponse = [Zdo.Status.NOT_AUTHORIZED, undefined];
2465
+ expect(spyResolveZDO).toHaveBeenCalledTimes(1);
2466
+ expect(spyResolveZDO).toHaveBeenCalledWith(sender, apsFrame, zdoResponse);
2467
+ });
2468
+
2469
+ it("Adapter impl: throws when permitJoin request on router fails", async () => {
2470
+ mockEzspSendUnicast.mockResolvedValueOnce([SLStatus.FAIL, 0]);
2471
+
2472
+ await expect(adapter.permitJoin(250, 1234)).rejects.toThrow(
2473
+ "~x~> [ZDO PERMIT_JOINING_REQUEST UNICAST to=0xffffffffffffffff:1234 messageTag=1] Failed to send request with status=FAIL.",
2474
+ );
2475
+ });
2476
+
2477
+ it("Adapter impl: throws when permitJoin fails to import ZIGBEE_PROFILE_INTEROPERABILITY_LINK_KEY", async () => {
2478
+ mockEzspImportTransientKey.mockResolvedValueOnce(SLStatus.FAIL);
2479
+
2480
+ await expect(adapter.permitJoin(250)).rejects.toThrow("[ZDO] Failed import transient key with status=FAIL.");
2481
+ });
2482
+
2483
+ it("Adapter impl: throws when permitJoin fails to set TC policy", async () => {
2484
+ mockEzspSetPolicy.mockResolvedValueOnce(SLStatus.FAIL);
2485
+
2486
+ await expect(adapter.permitJoin(250)).rejects.toThrow("[ZDO] Failed set join policy with status=FAIL.");
2487
+ });
2488
+
2489
+ it("Adapter impl: throws when stop permitJoin fails to restore TC policy", async () => {
2490
+ mockEzspSetPolicy.mockResolvedValueOnce(SLStatus.FAIL);
2491
+
2492
+ await expect(adapter.permitJoin(0)).rejects.toThrow("[ZDO] Failed set join policy with status=FAIL.");
2493
+ });
2494
+
2495
+ it("Adapter impl: sendZclFrameToEndpoint with command response with fixed source endpoint", async () => {
2496
+ const networkAddress: NodeId = 1234;
2497
+ const endpoint: number = 1;
2498
+ const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
2499
+ const zclFrame = Zcl.Frame.create(
2500
+ Zcl.FrameType.GLOBAL,
2501
+ Zcl.Direction.CLIENT_TO_SERVER,
2502
+ true,
2503
+ undefined,
2504
+ 3,
2505
+ "read",
2506
+ "genBasic",
2507
+ [{attrId: 0}],
2508
+ {},
2509
+ );
2510
+ const apsFrame: EmberApsFrame = {
2511
+ profileId: FIXED_ENDPOINTS[0].profileId,
2512
+ clusterId: zclFrame.cluster.ID,
2513
+ sourceEndpoint,
2514
+ destinationEndpoint: endpoint,
2515
+ options: DEFAULT_APS_OPTIONS,
2516
+ groupId: 0,
2517
+ sequence: 0, // set by stack
2518
+ };
2519
+ const lastHopLqi: number = 234;
2520
+ // Received Zigbee message from '0x', type 'readResponse', cluster 'genBasic', data '{"zclVersion":3}' from endpoint 1 with groupID 0
2521
+ const messageContents = Buffer.from("1803010000002003", "hex");
2522
+
2523
+ mockEzspSend.mockImplementationOnce(() => {
2524
+ setTimeout(async () => {
2525
+ mockEzspEmitter.emit(
2526
+ "incomingMessage",
2527
+ EmberIncomingMessageType.UNICAST,
2528
+ reverseApsFrame(apsFrame),
2529
+ lastHopLqi,
2530
+ networkAddress,
2531
+ messageContents,
2532
+ );
2533
+ await flushPromises();
2534
+ }, 300);
2535
+
2536
+ return [SLStatus.OK, ++mockAPSSequence];
2537
+ });
2538
+
2539
+ const p = adapter.sendZclFrameToEndpoint("0x1122334455667788", networkAddress, endpoint, zclFrame, 10000, false, false, sourceEndpoint);
2540
+
2541
+ await vi.advanceTimersByTimeAsync(5000);
2542
+ await expect(p).resolves.toStrictEqual({
2543
+ clusterID: apsFrame.clusterId,
2544
+ header: Zcl.Header.fromBuffer(messageContents),
2545
+ address: networkAddress,
2546
+ data: messageContents,
2547
+ endpoint: apsFrame.destinationEndpoint,
2548
+ linkquality: lastHopLqi,
2549
+ groupID: apsFrame.groupId,
2550
+ wasBroadcast: false,
2551
+ destinationEndpoint: apsFrame.sourceEndpoint,
2552
+ } as ZclPayload);
2553
+ expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
2554
+ });
2555
+
2556
+ it("Adapter impl: sendZclFrameToEndpoint with command response with other source endpoint", async () => {
2557
+ const networkAddress: NodeId = 1234;
2558
+ const endpoint: number = 1;
2559
+ const sourceEndpoint = 3;
2560
+ const zclFrame = Zcl.Frame.create(
2561
+ Zcl.FrameType.GLOBAL,
2562
+ Zcl.Direction.CLIENT_TO_SERVER,
2563
+ true,
2564
+ undefined,
2565
+ 3,
2566
+ "read",
2567
+ "genBasic",
2568
+ [{attrId: 0}],
2569
+ {},
2570
+ );
2571
+ const apsFrame: EmberApsFrame = {
2572
+ profileId: FIXED_ENDPOINTS[0].profileId,
2573
+ clusterId: zclFrame.cluster.ID,
2574
+ sourceEndpoint,
2575
+ destinationEndpoint: endpoint,
2576
+ options: DEFAULT_APS_OPTIONS,
2577
+ groupId: 0,
2578
+ sequence: 0, // set by stack
2579
+ };
2580
+ const lastHopLqi: number = 234;
2581
+ // Received Zigbee message from '0x', type 'readResponse', cluster 'genBasic', data '{"zclVersion":3}' from endpoint 1 with groupID 0
2582
+ const messageContents = Buffer.from("1803010000002003", "hex");
2583
+
2584
+ mockEzspSend.mockImplementationOnce(() => {
2585
+ setTimeout(async () => {
2586
+ mockEzspEmitter.emit(
2587
+ "incomingMessage",
2588
+ EmberIncomingMessageType.UNICAST,
2589
+ reverseApsFrame(apsFrame),
2590
+ lastHopLqi,
2591
+ networkAddress,
2592
+ messageContents,
2593
+ );
2594
+ await flushPromises();
2595
+ }, 300);
2596
+
2597
+ return [SLStatus.OK, ++mockAPSSequence];
2598
+ });
2599
+
2600
+ const p = adapter.sendZclFrameToEndpoint("0x1122334455667788", networkAddress, endpoint, zclFrame, 10000, false, false, sourceEndpoint);
2601
+
2602
+ await vi.advanceTimersByTimeAsync(5000);
2603
+ await expect(p).resolves.toStrictEqual({
2604
+ clusterID: apsFrame.clusterId,
2605
+ header: Zcl.Header.fromBuffer(messageContents),
2606
+ address: networkAddress,
2607
+ data: messageContents,
2608
+ endpoint: apsFrame.destinationEndpoint,
2609
+ linkquality: lastHopLqi,
2610
+ groupID: apsFrame.groupId,
2611
+ wasBroadcast: false,
2612
+ destinationEndpoint: apsFrame.sourceEndpoint,
2613
+ } as ZclPayload);
2614
+ expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
2615
+ });
2616
+
2617
+ it("Adapter impl: sendZclFrameToEndpoint with command response with no source endpoint", async () => {
2618
+ const networkAddress: NodeId = 1234;
2619
+ const endpoint: number = 1;
2620
+ const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
2621
+ const zclFrame = Zcl.Frame.create(
2622
+ Zcl.FrameType.GLOBAL,
2623
+ Zcl.Direction.CLIENT_TO_SERVER,
2624
+ true,
2625
+ undefined,
2626
+ 3,
2627
+ "read",
2628
+ "genBasic",
2629
+ [{attrId: 0}],
2630
+ {},
2631
+ );
2632
+ const apsFrame: EmberApsFrame = {
2633
+ profileId: FIXED_ENDPOINTS[0].profileId,
2634
+ clusterId: zclFrame.cluster.ID,
2635
+ sourceEndpoint,
2636
+ destinationEndpoint: endpoint,
2637
+ options: DEFAULT_APS_OPTIONS,
2638
+ groupId: 0,
2639
+ sequence: 0, // set by stack
2640
+ };
2641
+ const lastHopLqi: number = 234;
2642
+ // Received Zigbee message from '0x', type 'readResponse', cluster 'genBasic', data '{"zclVersion":3}' from endpoint 1 with groupID 0
2643
+ const messageContents = Buffer.from("1803010000002003", "hex");
2644
+
2645
+ mockEzspSend.mockImplementationOnce(() => {
2646
+ setTimeout(async () => {
2647
+ mockEzspEmitter.emit(
2648
+ "incomingMessage",
2649
+ EmberIncomingMessageType.UNICAST,
2650
+ reverseApsFrame(apsFrame),
2651
+ lastHopLqi,
2652
+ networkAddress,
2653
+ messageContents,
2654
+ );
2655
+ await flushPromises();
2656
+ }, 300);
2657
+
2658
+ return [SLStatus.OK, ++mockAPSSequence];
2659
+ });
2660
+
2661
+ const p = adapter.sendZclFrameToEndpoint("0x1122334455667788", networkAddress, endpoint, zclFrame, 10000, false, false);
2662
+
2663
+ await vi.advanceTimersByTimeAsync(5000);
2664
+ await expect(p).resolves.toStrictEqual({
2665
+ clusterID: apsFrame.clusterId,
2666
+ header: Zcl.Header.fromBuffer(messageContents),
2667
+ address: networkAddress,
2668
+ data: messageContents,
2669
+ endpoint: apsFrame.destinationEndpoint,
2670
+ linkquality: lastHopLqi,
2671
+ groupID: apsFrame.groupId,
2672
+ wasBroadcast: false,
2673
+ destinationEndpoint: apsFrame.sourceEndpoint,
2674
+ } as ZclPayload);
2675
+ expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
2676
+ });
2677
+
2678
+ it.each([
2679
+ ["NO_TX_SPACE", EzspStatus.NO_TX_SPACE],
2680
+ ["NOT_CONNECTED", EzspStatus.NOT_CONNECTED],
2681
+ ])("Adapter impl: recovers when sendZclFrameToEndpoint throws %s status", async (_statusName, status) => {
2682
+ const networkAddress: NodeId = 1234;
2683
+ const endpoint: number = 1;
2684
+ const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
2685
+ const zclFrame = Zcl.Frame.create(
2686
+ Zcl.FrameType.GLOBAL,
2687
+ Zcl.Direction.CLIENT_TO_SERVER,
2688
+ true,
2689
+ undefined,
2690
+ 3,
2691
+ "read",
2692
+ "genBasic",
2693
+ [{attrId: 0}],
2694
+ {},
2695
+ );
2696
+ const apsFrame: EmberApsFrame = {
2697
+ profileId: FIXED_ENDPOINTS[0].profileId,
2698
+ clusterId: zclFrame.cluster.ID,
2699
+ sourceEndpoint,
2700
+ destinationEndpoint: endpoint,
2701
+ options: DEFAULT_APS_OPTIONS,
2702
+ groupId: 0,
2703
+ sequence: 0, // set by stack
2704
+ };
2705
+ const lastHopLqi: number = 234;
2706
+ // Received Zigbee message from '0x', type 'readResponse', cluster 'genBasic', data '{"zclVersion":3}' from endpoint 1 with groupID 0
2707
+ const messageContents = Buffer.from("1803010000002003", "hex");
2708
+
2709
+ mockEzspSend.mockRejectedValueOnce(new EzspError(status)).mockImplementationOnce(() => {
2710
+ setTimeout(async () => {
2711
+ mockEzspEmitter.emit(
2712
+ "incomingMessage",
2713
+ EmberIncomingMessageType.UNICAST,
2714
+ reverseApsFrame(apsFrame),
2715
+ lastHopLqi,
2716
+ networkAddress,
2717
+ messageContents,
2718
+ );
2719
+ await flushPromises();
2720
+ }, 300);
2721
+
2722
+ return [SLStatus.OK, ++mockAPSSequence];
2723
+ });
2724
+
2725
+ const p = adapter.sendZclFrameToEndpoint("0x1122334455667788", networkAddress, endpoint, zclFrame, 10000, false, false, sourceEndpoint);
2726
+
2727
+ await vi.advanceTimersByTimeAsync(10000);
2728
+ await expect(p).resolves.toStrictEqual({
2729
+ clusterID: apsFrame.clusterId,
2730
+ header: Zcl.Header.fromBuffer(messageContents),
2731
+ address: networkAddress,
2732
+ data: messageContents,
2733
+ endpoint: apsFrame.destinationEndpoint,
2734
+ linkquality: lastHopLqi,
2735
+ groupID: apsFrame.groupId,
2736
+ wasBroadcast: false,
2737
+ destinationEndpoint: apsFrame.sourceEndpoint,
2738
+ } as ZclPayload);
2739
+ expect(mockEzspSend).toHaveBeenCalledTimes(2);
2740
+ expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
2741
+ });
2742
+
2743
+ it.each([
2744
+ ["ZIGBEE_MAX_MESSAGE_LIMIT_REACHED", SLStatus.ZIGBEE_MAX_MESSAGE_LIMIT_REACHED],
2745
+ ["BUSY", SLStatus.BUSY],
2746
+ ["NETWORK_DOWN", SLStatus.NETWORK_DOWN],
2747
+ ])("Adapter impl: recovers when sendZclFrameToEndpoint get %s status from NCP", async (_statusName, status) => {
2748
+ const networkAddress: NodeId = 1234;
2749
+ const endpoint: number = 1;
2750
+ const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
2751
+ const zclFrame = Zcl.Frame.create(
2752
+ Zcl.FrameType.GLOBAL,
2753
+ Zcl.Direction.CLIENT_TO_SERVER,
2754
+ true,
2755
+ undefined,
2756
+ 3,
2757
+ "read",
2758
+ "genBasic",
2759
+ [{attrId: 0}],
2760
+ {},
2761
+ );
2762
+ const apsFrame: EmberApsFrame = {
2763
+ profileId: FIXED_ENDPOINTS[0].profileId,
2764
+ clusterId: zclFrame.cluster.ID,
2765
+ sourceEndpoint,
2766
+ destinationEndpoint: endpoint,
2767
+ options: DEFAULT_APS_OPTIONS,
2768
+ groupId: 0,
2769
+ sequence: 0, // set by stack
2770
+ };
2771
+ const lastHopLqi: number = 234;
2772
+ // Received Zigbee message from '0x', type 'readResponse', cluster 'genBasic', data '{"zclVersion":3}' from endpoint 1 with groupID 0
2773
+ const messageContents = Buffer.from("1803010000002003", "hex");
2774
+
2775
+ mockEzspSend.mockResolvedValueOnce([status, 0]).mockImplementationOnce(() => {
2776
+ setTimeout(async () => {
2777
+ mockEzspEmitter.emit(
2778
+ "incomingMessage",
2779
+ EmberIncomingMessageType.UNICAST,
2780
+ reverseApsFrame(apsFrame),
2781
+ lastHopLqi,
2782
+ networkAddress,
2783
+ messageContents,
2784
+ );
2785
+ await flushPromises();
2786
+ }, 300);
2787
+
2788
+ return [SLStatus.OK, ++mockAPSSequence];
2789
+ });
2790
+
2791
+ const p = adapter.sendZclFrameToEndpoint("0x1122334455667788", networkAddress, endpoint, zclFrame, 10000, false, false, sourceEndpoint);
2792
+
2793
+ await vi.advanceTimersByTimeAsync(10000);
2794
+ await expect(p).resolves.toStrictEqual({
2795
+ clusterID: apsFrame.clusterId,
2796
+ header: Zcl.Header.fromBuffer(messageContents),
2797
+ address: networkAddress,
2798
+ data: messageContents,
2799
+ endpoint: apsFrame.destinationEndpoint,
2800
+ linkquality: lastHopLqi,
2801
+ groupID: apsFrame.groupId,
2802
+ wasBroadcast: false,
2803
+ destinationEndpoint: apsFrame.sourceEndpoint,
2804
+ } as ZclPayload);
2805
+ expect(mockEzspSend).toHaveBeenCalledTimes(2);
2806
+ expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
2807
+ });
2808
+
2809
+ it("Adapter impl: throws when sendZclFrameToEndpoint throws NO_TX_SPACE status and recovery disabled", async () => {
2810
+ const networkAddress: NodeId = 1234;
2811
+ const endpoint: number = 1;
2812
+ const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
2813
+ const zclFrame = Zcl.Frame.create(
2814
+ Zcl.FrameType.GLOBAL,
2815
+ Zcl.Direction.CLIENT_TO_SERVER,
2816
+ true,
2817
+ undefined,
2818
+ 3,
2819
+ "read",
2820
+ "genBasic",
2821
+ [{attrId: 0}],
2822
+ {},
2823
+ );
2824
+ const apsFrame: EmberApsFrame = {
2825
+ profileId: FIXED_ENDPOINTS[0].profileId,
2826
+ clusterId: zclFrame.cluster.ID,
2827
+ sourceEndpoint,
2828
+ destinationEndpoint: endpoint,
2829
+ options: DEFAULT_APS_OPTIONS,
2830
+ groupId: 0,
2831
+ sequence: 0, // set by stack
2832
+ };
2833
+
2834
+ mockEzspSend.mockRejectedValueOnce(new EzspError(EzspStatus.NO_TX_SPACE));
2835
+
2836
+ const p = defuseRejection(
2837
+ adapter.sendZclFrameToEndpoint(
2838
+ "0x1122334455667788",
2839
+ networkAddress,
2840
+ endpoint,
2841
+ zclFrame,
2842
+ 10000,
2843
+ false,
2844
+ true, // disable recovery
2845
+ sourceEndpoint,
2846
+ ),
2847
+ );
2848
+
2849
+ await vi.advanceTimersByTimeAsync(10000);
2850
+ await expect(p).rejects.toThrow(
2851
+ `~x~> [ZCL to=0x1122334455667788:1234 apsFrame={"profileId":260,"clusterId":0,"sourceEndpoint":1,"destinationEndpoint":1,"options":4416,"groupId":0,"sequence":0}] Failed to send request with status=${SLStatus[SLStatus.BUSY]}.`,
2852
+ );
2853
+ expect(mockEzspSend).toHaveBeenCalledTimes(1);
2854
+ expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
2855
+ });
2856
+
2857
+ it("Adapter impl: throws when sendZclFrameToEndpoint get BUSY status from NCP and recovery disabled", async () => {
2858
+ const networkAddress: NodeId = 1234;
2859
+ const endpoint: number = 1;
2860
+ const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
2861
+ const zclFrame = Zcl.Frame.create(
2862
+ Zcl.FrameType.GLOBAL,
2863
+ Zcl.Direction.CLIENT_TO_SERVER,
2864
+ true,
2865
+ undefined,
2866
+ 3,
2867
+ "read",
2868
+ "genBasic",
2869
+ [{attrId: 0}],
2870
+ {},
2871
+ );
2872
+ const apsFrame: EmberApsFrame = {
2873
+ profileId: FIXED_ENDPOINTS[0].profileId,
2874
+ clusterId: zclFrame.cluster.ID,
2875
+ sourceEndpoint,
2876
+ destinationEndpoint: endpoint,
2877
+ options: DEFAULT_APS_OPTIONS,
2878
+ groupId: 0,
2879
+ sequence: 0, // set by stack
2880
+ };
2881
+
2882
+ mockEzspSend.mockResolvedValueOnce([SLStatus.BUSY, 0]);
2883
+
2884
+ const p = defuseRejection(
2885
+ adapter.sendZclFrameToEndpoint(
2886
+ "0x1122334455667788",
2887
+ networkAddress,
2888
+ endpoint,
2889
+ zclFrame,
2890
+ 10000,
2891
+ false,
2892
+ true, // disable recovery
2893
+ sourceEndpoint,
2894
+ ),
2895
+ );
2896
+
2897
+ await vi.advanceTimersByTimeAsync(10000);
2898
+ await expect(p).rejects.toThrow(
2899
+ `~x~> [ZCL to=0x1122334455667788:1234 apsFrame={"profileId":260,"clusterId":0,"sourceEndpoint":1,"destinationEndpoint":1,"options":4416,"groupId":0,"sequence":0}] Failed to send request with status=${SLStatus[SLStatus.BUSY]}.`,
2900
+ );
2901
+ expect(mockEzspSend).toHaveBeenCalledTimes(1);
2902
+ expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
2903
+ });
2904
+
2905
+ it("Adapter impl: throws when sendZclFrameToEndpoint get BUSY status from NCP and exceeded max attempts", async () => {
2906
+ const networkAddress: NodeId = 1234;
2907
+ const endpoint: number = 1;
2908
+ const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
2909
+ const zclFrame = Zcl.Frame.create(
2910
+ Zcl.FrameType.GLOBAL,
2911
+ Zcl.Direction.CLIENT_TO_SERVER,
2912
+ true,
2913
+ undefined,
2914
+ 3,
2915
+ "read",
2916
+ "genBasic",
2917
+ [{attrId: 0}],
2918
+ {},
2919
+ );
2920
+ const apsFrame: EmberApsFrame = {
2921
+ profileId: FIXED_ENDPOINTS[0].profileId,
2922
+ clusterId: zclFrame.cluster.ID,
2923
+ sourceEndpoint,
2924
+ destinationEndpoint: endpoint,
2925
+ options: DEFAULT_APS_OPTIONS,
2926
+ groupId: 0,
2927
+ sequence: 0, // set by stack
2928
+ };
2929
+
2930
+ mockEzspSend
2931
+ .mockResolvedValueOnce([SLStatus.BUSY, 0])
2932
+ .mockResolvedValueOnce([SLStatus.BUSY, 0])
2933
+ .mockResolvedValueOnce([SLStatus.BUSY, 0]);
2934
+
2935
+ const p = defuseRejection(
2936
+ adapter.sendZclFrameToEndpoint(
2937
+ "0x1122334455667788",
2938
+ networkAddress,
2939
+ endpoint,
2940
+ zclFrame,
2941
+ 10000,
2942
+ false,
2943
+ false, // disable recovery
2944
+ sourceEndpoint,
2945
+ ),
2946
+ );
2947
+
2948
+ await vi.advanceTimersByTimeAsync(10000);
2949
+ await expect(p).rejects.toThrow(
2950
+ `~x~> [ZCL to=0x1122334455667788:1234 apsFrame={"profileId":260,"clusterId":0,"sourceEndpoint":1,"destinationEndpoint":1,"options":4416,"groupId":0,"sequence":0}] Failed to send request with status=${SLStatus[SLStatus.BUSY]}.`,
2951
+ );
2952
+ expect(mockEzspSend).toHaveBeenCalledTimes(3);
2953
+ expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
2954
+ });
2955
+
2956
+ it("Adapter impl: throws when sendZclFrameToEndpoint request fails", async () => {
2957
+ const networkAddress: NodeId = 1234;
2958
+ const endpoint: number = 1;
2959
+ const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
2960
+ const zclFrame = Zcl.Frame.create(
2961
+ Zcl.FrameType.GLOBAL,
2962
+ Zcl.Direction.CLIENT_TO_SERVER,
2963
+ true,
2964
+ undefined,
2965
+ 3,
2966
+ "read",
2967
+ "genBasic",
2968
+ [{attrId: 0}],
2969
+ {},
2970
+ );
2971
+ const apsFrame: EmberApsFrame = {
2972
+ profileId: FIXED_ENDPOINTS[0].profileId,
2973
+ clusterId: zclFrame.cluster.ID,
2974
+ sourceEndpoint,
2975
+ destinationEndpoint: endpoint,
2976
+ options: DEFAULT_APS_OPTIONS,
2977
+ groupId: 0,
2978
+ sequence: 0, // set by stack
2979
+ };
2980
+
2981
+ mockEzspSend.mockResolvedValueOnce([SLStatus.FAIL, 0]);
2982
+
2983
+ const p = defuseRejection(
2984
+ adapter.sendZclFrameToEndpoint(
2985
+ "0x1122334455667788",
2986
+ networkAddress,
2987
+ endpoint,
2988
+ zclFrame,
2989
+ 10000,
2990
+ false,
2991
+ false, // disable recovery
2992
+ sourceEndpoint,
2993
+ ),
2994
+ );
2995
+
2996
+ await vi.advanceTimersByTimeAsync(10000);
2997
+ await expect(p).rejects.toThrow(
2998
+ `~x~> [ZCL to=0x1122334455667788:1234 apsFrame={"profileId":260,"clusterId":0,"sourceEndpoint":1,"destinationEndpoint":1,"options":4416,"groupId":0,"sequence":0}] Failed to send request with status=FAIL.`,
2999
+ );
3000
+ expect(mockEzspSend).toHaveBeenCalledTimes(1);
3001
+ expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
3002
+ });
3003
+
3004
+ it("Adapter impl: sendZdo with EUI64", async () => {
3005
+ const sender: NodeId = 0x6789;
3006
+ const senderEUI64: Eui64 = "0x1122334455667788";
3007
+ const apsFrame: EmberApsFrame = {
3008
+ profileId: Zdo.ZDO_PROFILE_ID,
3009
+ clusterId: Zdo.ClusterId.NETWORK_ADDRESS_RESPONSE,
3010
+ sourceEndpoint: Zdo.ZDO_ENDPOINT,
3011
+ destinationEndpoint: Zdo.ZDO_ENDPOINT,
3012
+ options: 0,
3013
+ groupId: 0,
3014
+ sequence: 0,
3015
+ };
3016
+
3017
+ mockEzspSendBroadcast.mockImplementationOnce(() => {
3018
+ setTimeout(async () => {
3019
+ mockEzspEmitter.emit(
3020
+ "zdoResponse",
3021
+ apsFrame,
3022
+ sender,
3023
+ Buffer.from([
3024
+ 1,
3025
+ Zdo.Status.SUCCESS,
3026
+ 0x88,
3027
+ 0x77,
3028
+ 0x66,
3029
+ 0x55,
3030
+ 0x44,
3031
+ 0x33,
3032
+ 0x22,
3033
+ 0x11,
3034
+ 0x89, // nwkAddress
3035
+ 0x67, // nwkAddress
3036
+ ]),
3037
+ );
3038
+ await flushPromises();
3039
+ }, 300);
3040
+
3041
+ return [SLStatus.OK, ++mockAPSSequence];
3042
+ });
3043
+
3044
+ const zdoPayload = Zdo.Buffalo.buildRequest(false, Zdo.ClusterId.NETWORK_ADDRESS_REQUEST, senderEUI64, false, 0);
3045
+ const p = adapter.sendZdo(
3046
+ senderEUI64,
3047
+ ZSpec.NULL_NODE_ID /* same as broadcast SLEEPY */,
3048
+ Zdo.ClusterId.NETWORK_ADDRESS_REQUEST,
3049
+ zdoPayload,
3050
+ false,
3051
+ );
3052
+
3053
+ await vi.advanceTimersByTimeAsync(1000);
3054
+ await expect(p).resolves.toStrictEqual([
3055
+ Zdo.Status.SUCCESS,
3056
+ {
3057
+ eui64: senderEUI64,
3058
+ nwkAddress: sender,
3059
+ startIndex: 0,
3060
+ assocDevList: [],
3061
+ } as ZdoTypes.NetworkAddressResponse,
3062
+ ]);
3063
+ });
3064
+
3065
+ it("Adapter impl: sendZclFrameToEndpoint with default response", async () => {
3066
+ const networkAddress: NodeId = 1234;
3067
+ const endpoint: number = 3;
3068
+ const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
3069
+ const zclFrame = Zcl.Frame.create(
3070
+ Zcl.FrameType.GLOBAL,
3071
+ Zcl.Direction.CLIENT_TO_SERVER,
3072
+ false,
3073
+ undefined,
3074
+ 3,
3075
+ "read",
3076
+ "genBasic",
3077
+ [{attrId: 0}],
3078
+ {},
3079
+ );
3080
+ const apsFrame: EmberApsFrame = {
3081
+ profileId: FIXED_ENDPOINTS[0].profileId,
3082
+ clusterId: zclFrame.cluster.ID,
3083
+ sourceEndpoint,
3084
+ destinationEndpoint: endpoint,
3085
+ options: DEFAULT_APS_OPTIONS,
3086
+ groupId: 0,
3087
+ sequence: 0, // set by stack
3088
+ };
3089
+ const lastHopLqi: number = 234;
3090
+ // defaultRsp with cmdId=0, status=0
3091
+ const messageContents = Buffer.from("18030b0000", "hex");
3092
+
3093
+ mockEzspSend.mockImplementationOnce(() => {
3094
+ setTimeout(async () => {
3095
+ mockEzspEmitter.emit(
3096
+ "incomingMessage",
3097
+ EmberIncomingMessageType.UNICAST,
3098
+ reverseApsFrame(apsFrame),
3099
+ lastHopLqi,
3100
+ networkAddress,
3101
+ messageContents,
3102
+ );
3103
+ await flushPromises();
3104
+ }, 300);
3105
+
3106
+ return [SLStatus.OK, ++mockAPSSequence];
3107
+ });
3108
+
3109
+ const p = adapter.sendZclFrameToEndpoint("0x1122334455667788", networkAddress, endpoint, zclFrame, 10000, true, false, sourceEndpoint);
3110
+
3111
+ await vi.advanceTimersByTimeAsync(5000);
3112
+ await expect(p).resolves.toStrictEqual({
3113
+ clusterID: apsFrame.clusterId,
3114
+ header: Zcl.Header.fromBuffer(messageContents),
3115
+ address: networkAddress,
3116
+ data: messageContents,
3117
+ endpoint: apsFrame.destinationEndpoint,
3118
+ linkquality: lastHopLqi,
3119
+ groupID: apsFrame.groupId,
3120
+ wasBroadcast: false,
3121
+ destinationEndpoint: apsFrame.sourceEndpoint,
3122
+ } as ZclPayload);
3123
+ expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
3124
+ });
3125
+
3126
+ it("Adapter impl: sendZclFrameToEndpoint without response", async () => {
3127
+ const networkAddress: NodeId = 1234;
3128
+ const endpoint: number = 3;
3129
+ const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
3130
+ const zclFrame = Zcl.Frame.create(
3131
+ Zcl.FrameType.GLOBAL,
3132
+ Zcl.Direction.CLIENT_TO_SERVER,
3133
+ true,
3134
+ undefined,
3135
+ 3,
3136
+ "read",
3137
+ "genBasic",
3138
+ [{attrId: 0}],
3139
+ {},
3140
+ );
3141
+
3142
+ const p = adapter.sendZclFrameToEndpoint("0x1122334455667788", networkAddress, endpoint, zclFrame, 10000, true, false, sourceEndpoint);
3143
+
3144
+ await vi.advanceTimersByTimeAsync(5000);
3145
+ await expect(p).resolves.toStrictEqual(undefined);
3146
+
3147
+ const apsFrame: EmberApsFrame = {
3148
+ profileId: FIXED_ENDPOINTS[0].profileId,
3149
+ clusterId: zclFrame.cluster.ID,
3150
+ sourceEndpoint,
3151
+ destinationEndpoint: endpoint,
3152
+ options: DEFAULT_APS_OPTIONS & ~EmberApsOption.RETRY,
3153
+ groupId: 0,
3154
+ sequence: 0, // set by stack
3155
+ };
3156
+
3157
+ expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
3158
+ });
3159
+
3160
+ it("Adapter impl: sendZclFrameToGroup with source endpoint", async () => {
3161
+ const groupId: number = 32;
3162
+ const zclFrame = Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {});
3163
+ const p = adapter.sendZclFrameToGroup(groupId, zclFrame, 2);
3164
+
3165
+ await vi.advanceTimersByTimeAsync(5000);
3166
+ await expect(p).resolves.toStrictEqual(undefined);
3167
+
3168
+ const apsFrame: EmberApsFrame = {
3169
+ profileId: FIXED_ENDPOINTS[0].profileId,
3170
+ clusterId: zclFrame.cluster.ID,
3171
+ sourceEndpoint: 2,
3172
+ destinationEndpoint: 0xff,
3173
+ options: DEFAULT_APS_OPTIONS,
3174
+ groupId,
3175
+ sequence: 0, // set by stack
3176
+ };
3177
+
3178
+ expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.MULTICAST, groupId, apsFrame, zclFrame.toBuffer(), 0, 0);
3179
+ });
3180
+
3181
+ it("Adapter impl: sendZclFrameToGroup with default source endpoint", async () => {
3182
+ const groupId: number = 32;
3183
+ const zclFrame = Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {});
3184
+ const p = adapter.sendZclFrameToGroup(groupId, zclFrame);
3185
+
3186
+ await vi.advanceTimersByTimeAsync(5000);
3187
+ await expect(p).resolves.toStrictEqual(undefined);
3188
+
3189
+ const apsFrame: EmberApsFrame = {
3190
+ profileId: FIXED_ENDPOINTS[0].profileId,
3191
+ clusterId: zclFrame.cluster.ID,
3192
+ sourceEndpoint: FIXED_ENDPOINTS[0].endpoint,
3193
+ destinationEndpoint: 0xff,
3194
+ options: DEFAULT_APS_OPTIONS,
3195
+ groupId,
3196
+ sequence: 0, // set by stack
3197
+ };
3198
+
3199
+ expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.MULTICAST, groupId, apsFrame, zclFrame.toBuffer(), 0, 0);
3200
+ });
3201
+
3202
+ it("Adapter impl: throws when sendZclFrameToGroup fails request", async () => {
3203
+ mockEzspSend.mockResolvedValueOnce([SLStatus.FAIL, 0]);
3204
+
3205
+ const groupId: number = 32;
3206
+ const zclFrame = Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {});
3207
+ const p = defuseRejection(adapter.sendZclFrameToGroup(groupId, zclFrame, 1));
3208
+
3209
+ await vi.advanceTimersByTimeAsync(5000);
3210
+ await expect(p).rejects.toThrow("~x~> [ZCL GROUP groupId=32] Failed to send with status=FAIL.");
3211
+ expect(mockEzspSend).toHaveBeenCalledTimes(1);
3212
+ });
3213
+
3214
+ it("Adapter impl: sendZclFrameToAll with fixed endpoint", async () => {
3215
+ const endpoint: number = 32;
3216
+ const zclFrame = Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {});
3217
+ const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
3218
+ const p = adapter.sendZclFrameToAll(endpoint, zclFrame, sourceEndpoint, ZSpec.BroadcastAddress.DEFAULT);
3219
+
3220
+ await vi.advanceTimersByTimeAsync(5000);
3221
+ await expect(p).resolves.toStrictEqual(undefined);
3222
+
3223
+ const apsFrame: EmberApsFrame = {
3224
+ profileId: FIXED_ENDPOINTS[0].profileId,
3225
+ clusterId: zclFrame.cluster.ID,
3226
+ sourceEndpoint,
3227
+ destinationEndpoint: endpoint,
3228
+ options: DEFAULT_APS_OPTIONS,
3229
+ groupId: ZSpec.BroadcastAddress.DEFAULT,
3230
+ sequence: 0, // set by stack
3231
+ };
3232
+
3233
+ expect(mockEzspSend).toHaveBeenCalledWith(
3234
+ EmberOutgoingMessageType.BROADCAST,
3235
+ ZSpec.BroadcastAddress.DEFAULT,
3236
+ apsFrame,
3237
+ zclFrame.toBuffer(),
3238
+ 0,
3239
+ 0,
3240
+ );
3241
+ });
3242
+
3243
+ it("Adapter impl: sendZclFrameToAll with other endpoint", async () => {
3244
+ const endpoint: number = 32;
3245
+ const zclFrame = Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {});
3246
+ const sourceEndpoint = 3;
3247
+ const p = adapter.sendZclFrameToAll(endpoint, zclFrame, sourceEndpoint, ZSpec.BroadcastAddress.DEFAULT);
3248
+
3249
+ await vi.advanceTimersByTimeAsync(5000);
3250
+ await expect(p).resolves.toStrictEqual(undefined);
3251
+
3252
+ const apsFrame: EmberApsFrame = {
3253
+ profileId: FIXED_ENDPOINTS[0].profileId,
3254
+ clusterId: zclFrame.cluster.ID,
3255
+ sourceEndpoint,
3256
+ destinationEndpoint: endpoint,
3257
+ options: DEFAULT_APS_OPTIONS,
3258
+ groupId: ZSpec.BroadcastAddress.DEFAULT,
3259
+ sequence: 0, // set by stack
3260
+ };
3261
+
3262
+ expect(mockEzspSend).toHaveBeenCalledWith(
3263
+ EmberOutgoingMessageType.BROADCAST,
3264
+ ZSpec.BroadcastAddress.DEFAULT,
3265
+ apsFrame,
3266
+ zclFrame.toBuffer(),
3267
+ 0,
3268
+ 0,
3269
+ );
3270
+ });
3271
+
3272
+ it("Adapter impl: throws when sendZclFrameToAll fails request", async () => {
3273
+ mockEzspSend.mockResolvedValueOnce([SLStatus.FAIL, 0]);
3274
+
3275
+ const endpoint: number = 32;
3276
+ const zclFrame = Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {});
3277
+ const p = defuseRejection(adapter.sendZclFrameToAll(endpoint, zclFrame, 1, ZSpec.BroadcastAddress.DEFAULT));
3278
+
3279
+ await vi.advanceTimersByTimeAsync(5000);
3280
+ await expect(p).rejects.toThrow("~x~> [ZCL BROADCAST destination=65532] Failed to send with status=FAIL.");
3281
+ expect(mockEzspSend).toHaveBeenCalledTimes(1);
3282
+ });
3283
+
3284
+ it("Adapter impl: setChannelInterPAN", async () => {
3285
+ await expect(adapter.setChannelInterPAN(15)).resolves.toStrictEqual(undefined);
3286
+ expect(mockEzspSetLogicalAndRadioChannel).toHaveBeenCalledWith(15);
3287
+ });
3288
+
3289
+ it("Adapter impl: throws when setChannelInterPAN fails request", async () => {
3290
+ mockEzspSetLogicalAndRadioChannel.mockResolvedValueOnce(SLStatus.FAIL);
3291
+
3292
+ await expect(adapter.setChannelInterPAN(15)).rejects.toThrow(`Failed to set InterPAN channel to '15' with status=FAIL.`);
3293
+ expect(mockEzspSetLogicalAndRadioChannel).toHaveBeenCalledWith(15);
3294
+ });
3295
+
3296
+ it("Adapter impl: sendZclFrameInterPANToIeeeAddr", async () => {
3297
+ const ieee: Eui64 = "0x1122334455667788";
3298
+ const zclFrame = Zcl.Frame.create(
3299
+ Zcl.FrameType.GLOBAL,
3300
+ Zcl.Direction.CLIENT_TO_SERVER,
3301
+ false,
3302
+ undefined,
3303
+ 3,
3304
+ "read",
3305
+ "genBasic",
3306
+ [{attrId: 0}],
3307
+ {},
3308
+ );
3309
+
3310
+ await expect(adapter.sendZclFrameInterPANToIeeeAddr(zclFrame, ieee)).resolves.toStrictEqual(undefined);
3311
+ expect(mockEzspSendRawMessage).toHaveBeenCalledTimes(1);
3312
+ expect(mockEzspSendRawMessage).toHaveBeenCalledWith(expect.any(Buffer), 1, true);
3313
+ });
3314
+
3315
+ it("Adapter impl: throws when sendZclFrameInterPANToIeeeAddr request fails", async () => {
3316
+ mockEzspSendRawMessage.mockResolvedValueOnce(SLStatus.BUSY);
3317
+
3318
+ const ieee: Eui64 = "0x1122334455667788";
3319
+ const zclFrame = Zcl.Frame.create(
3320
+ Zcl.FrameType.GLOBAL,
3321
+ Zcl.Direction.CLIENT_TO_SERVER,
3322
+ false,
3323
+ undefined,
3324
+ 3,
3325
+ "read",
3326
+ "genBasic",
3327
+ [{attrId: 0}],
3328
+ {},
3329
+ );
3330
+
3331
+ await expect(adapter.sendZclFrameInterPANToIeeeAddr(zclFrame, ieee)).rejects.toThrow(
3332
+ `~x~> [ZCL TOUCHLINK to=${ieee}] Failed to send with status=${SLStatus[SLStatus.BUSY]}.`,
3333
+ );
3334
+ expect(mockEzspSendRawMessage).toHaveBeenCalledTimes(1);
3335
+ expect(mockEzspSendRawMessage).toHaveBeenCalledWith(expect.any(Buffer), 1, true);
3336
+ });
3337
+
3338
+ it("Adapter impl: sendZclFrameInterPANBroadcast", async () => {
3339
+ const zclFrame = Zcl.Frame.create(
3340
+ Zcl.FrameType.SPECIFIC,
3341
+ Zcl.Direction.CLIENT_TO_SERVER,
3342
+ true,
3343
+ undefined,
3344
+ 0,
3345
+ "scanRequest",
3346
+ Zcl.Clusters.touchlink.ID,
3347
+ {transactionID: 1, zigbeeInformation: 4, touchlinkInformation: 18},
3348
+ {},
3349
+ );
3350
+ const sourcePanId: PanId = 0x1234;
3351
+ const sourceAddress: Eui64 = "0x1122334455aabbcc";
3352
+ const groupId: number = ZSpec.BroadcastAddress.SLEEPY;
3353
+ const lastHopLqi = 252;
3354
+ // Received Zigbee message from '0x', type 'readResponse', cluster 'genBasic', data '{"zclVersion":3}' from endpoint 1 with groupID 0
3355
+ const messageContents = Buffer.from("1800010000000100000000000000000088776655443322110154466341200", "hex");
3356
+
3357
+ mockEzspSendRawMessage.mockImplementationOnce(() => {
3358
+ setTimeout(async () => {
3359
+ mockEzspEmitter.emit("touchlinkMessage", sourcePanId, sourceAddress, groupId, lastHopLqi, messageContents);
3360
+ await flushPromises();
3361
+ }, 300);
3362
+
3363
+ return SLStatus.OK;
3364
+ });
3365
+
3366
+ const p = adapter.sendZclFrameInterPANBroadcast(zclFrame, 10000, false);
3367
+
3368
+ await vi.advanceTimersByTimeAsync(5000);
3369
+
3370
+ const payload: ZclPayload = {
3371
+ clusterID: Zcl.Clusters.touchlink.ID,
3372
+ header: Zcl.Header.fromBuffer(messageContents),
3373
+ address: sourceAddress,
3374
+ data: messageContents,
3375
+ endpoint: FIXED_ENDPOINTS[0].endpoint,
3376
+ linkquality: lastHopLqi,
3377
+ groupID: groupId,
3378
+ wasBroadcast: true,
3379
+ destinationEndpoint: FIXED_ENDPOINTS[0].endpoint,
3380
+ };
3381
+
3382
+ await expect(p).resolves.toStrictEqual(payload);
3383
+ expect(mockEzspSendRawMessage).toHaveBeenCalledTimes(1);
3384
+ expect(mockEzspSendRawMessage).toHaveBeenCalledWith(expect.any(Buffer), 1, true);
3385
+ });
3386
+
3387
+ it("Adapter impl: throws when sendZclFrameInterPANBroadcast command has no response", async () => {
3388
+ const commandName = "readRsp";
3389
+ const zclFrame = Zcl.Frame.create(
3390
+ Zcl.FrameType.GLOBAL,
3391
+ Zcl.Direction.CLIENT_TO_SERVER,
3392
+ false,
3393
+ undefined,
3394
+ 3,
3395
+ commandName,
3396
+ "genBasic",
3397
+ [{attrId: 0}],
3398
+ {},
3399
+ );
3400
+
3401
+ await expect(adapter.sendZclFrameInterPANBroadcast(zclFrame, 10000, false)).rejects.toThrow(
3402
+ `Command '${commandName}' has no response, cannot wait for response.`,
3403
+ );
3404
+ expect(mockEzspSendRawMessage).toHaveBeenCalledTimes(0);
3405
+ });
3406
+
3407
+ it("Adapter impl: throws when sendZclFrameInterPANBroadcast request fails", async () => {
3408
+ mockEzspSendRawMessage.mockResolvedValueOnce(SLStatus.BUSY);
3409
+
3410
+ const zclFrame = Zcl.Frame.create(
3411
+ Zcl.FrameType.GLOBAL,
3412
+ Zcl.Direction.CLIENT_TO_SERVER,
3413
+ false,
3414
+ undefined,
3415
+ 3,
3416
+ "read",
3417
+ "genBasic",
3418
+ [{attrId: 0}],
3419
+ {},
3420
+ );
3421
+
3422
+ await expect(adapter.sendZclFrameInterPANBroadcast(zclFrame, 10000, false)).rejects.toThrow(
3423
+ `~x~> [ZCL TOUCHLINK BROADCAST] Failed to send with status=${SLStatus[SLStatus.BUSY]}.`,
3424
+ );
3425
+ expect(mockEzspSendRawMessage).toHaveBeenCalledTimes(1);
3426
+ expect(mockEzspSendRawMessage).toHaveBeenCalledWith(expect.any(Buffer), 1, true);
3427
+ });
3428
+
3429
+ it("Adapter impl: restoreChannelInterPAN", async () => {
3430
+ const p = adapter.restoreChannelInterPAN();
3431
+
3432
+ await vi.advanceTimersByTimeAsync(10000);
3433
+ await expect(p).resolves.toStrictEqual(undefined);
3434
+ expect(mockEzspSetLogicalAndRadioChannel).toHaveBeenCalledWith(DEFAULT_NETWORK_OPTIONS.channelList[0]);
3435
+ });
3436
+
3437
+ it("Adapter impl: throws when restoreChannelInterPAN fails request", async () => {
3438
+ mockEzspSetLogicalAndRadioChannel.mockResolvedValueOnce(SLStatus.FAIL);
3439
+
3440
+ const p = defuseRejection(adapter.restoreChannelInterPAN());
3441
+
3442
+ await vi.advanceTimersByTimeAsync(10000);
3443
+ await expect(p).rejects.toThrow(`Failed to restore InterPAN channel to '${DEFAULT_NETWORK_OPTIONS.channelList[0]}' with status=FAIL.`);
3444
+ expect(mockEzspSetLogicalAndRadioChannel).toHaveBeenCalledWith(DEFAULT_NETWORK_OPTIONS.channelList[0]);
3445
+ });
3446
+ });
3447
+ });