@willieee802/zigbee-herdsman 0.19.21 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (523) hide show
  1. package/.babelrc.js +0 -4
  2. package/.release-please-manifest.json +1 -2
  3. package/CHANGELOG.md +462 -0
  4. package/README.md +1 -1
  5. package/dist/adapter/adapter.d.ts +61 -61
  6. package/dist/adapter/adapter.d.ts.map +1 -1
  7. package/dist/adapter/adapter.js +158 -153
  8. package/dist/adapter/adapter.js.map +1 -1
  9. package/dist/adapter/deconz/adapter/deconzAdapter.d.ts +68 -68
  10. package/dist/adapter/deconz/adapter/deconzAdapter.d.ts.map +1 -1
  11. package/dist/adapter/deconz/adapter/deconzAdapter.js +1081 -1060
  12. package/dist/adapter/deconz/adapter/deconzAdapter.js.map +1 -1
  13. package/dist/adapter/deconz/adapter/index.d.ts +2 -2
  14. package/dist/adapter/deconz/adapter/index.js +10 -10
  15. package/dist/adapter/deconz/driver/constants.d.ts +104 -104
  16. package/dist/adapter/deconz/driver/constants.js +55 -55
  17. package/dist/adapter/deconz/driver/driver.d.ts +81 -81
  18. package/dist/adapter/deconz/driver/driver.d.ts.map +1 -1
  19. package/dist/adapter/deconz/driver/driver.js +750 -732
  20. package/dist/adapter/deconz/driver/driver.js.map +1 -1
  21. package/dist/adapter/deconz/driver/frame.d.ts +6 -6
  22. package/dist/adapter/deconz/driver/frame.js +13 -13
  23. package/dist/adapter/deconz/driver/frameParser.d.ts +2 -2
  24. package/dist/adapter/deconz/driver/frameParser.js +443 -443
  25. package/dist/adapter/deconz/driver/frameParser.js.map +1 -1
  26. package/dist/adapter/deconz/driver/parser.d.ts +12 -12
  27. package/dist/adapter/deconz/driver/parser.js +63 -61
  28. package/dist/adapter/deconz/driver/parser.js.map +1 -1
  29. package/dist/adapter/deconz/driver/writer.d.ts +8 -8
  30. package/dist/adapter/deconz/driver/writer.js +44 -44
  31. package/dist/adapter/ember/adapter/emberAdapter.d.ts +810 -0
  32. package/dist/adapter/ember/adapter/emberAdapter.d.ts.map +1 -0
  33. package/dist/adapter/ember/adapter/emberAdapter.js +2970 -0
  34. package/dist/adapter/ember/adapter/emberAdapter.js.map +1 -0
  35. package/dist/adapter/ember/adapter/endpoints.d.ts +25 -0
  36. package/dist/adapter/ember/adapter/endpoints.d.ts.map +1 -0
  37. package/dist/adapter/ember/adapter/endpoints.js +66 -0
  38. package/dist/adapter/ember/adapter/endpoints.js.map +1 -0
  39. package/dist/adapter/ember/adapter/index.d.ts +3 -0
  40. package/dist/adapter/ember/adapter/index.d.ts.map +1 -0
  41. package/dist/adapter/ember/adapter/index.js +6 -0
  42. package/dist/adapter/ember/adapter/index.js.map +1 -0
  43. package/dist/adapter/ember/adapter/oneWaitress.d.ts +100 -0
  44. package/dist/adapter/ember/adapter/oneWaitress.d.ts.map +1 -0
  45. package/dist/adapter/ember/adapter/oneWaitress.js +227 -0
  46. package/dist/adapter/ember/adapter/oneWaitress.js.map +1 -0
  47. package/dist/adapter/ember/adapter/requestQueue.d.ts +59 -0
  48. package/dist/adapter/ember/adapter/requestQueue.d.ts.map +1 -0
  49. package/dist/adapter/ember/adapter/requestQueue.js +144 -0
  50. package/dist/adapter/ember/adapter/requestQueue.js.map +1 -0
  51. package/dist/adapter/ember/adapter/tokensManager.d.ts +69 -0
  52. package/dist/adapter/ember/adapter/tokensManager.d.ts.map +1 -0
  53. package/dist/adapter/ember/adapter/tokensManager.js +685 -0
  54. package/dist/adapter/ember/adapter/tokensManager.js.map +1 -0
  55. package/dist/adapter/ember/consts.d.ts +198 -0
  56. package/dist/adapter/ember/consts.d.ts.map +1 -0
  57. package/dist/adapter/ember/consts.js +253 -0
  58. package/dist/adapter/ember/consts.js.map +1 -0
  59. package/dist/adapter/ember/enums.d.ts +2174 -0
  60. package/dist/adapter/ember/enums.d.ts.map +1 -0
  61. package/dist/adapter/ember/enums.js +2377 -0
  62. package/dist/adapter/ember/enums.js.map +1 -0
  63. package/dist/adapter/ember/ezsp/buffalo.d.ts +156 -0
  64. package/dist/adapter/ember/ezsp/buffalo.d.ts.map +1 -0
  65. package/dist/adapter/ember/ezsp/buffalo.js +1033 -0
  66. package/dist/adapter/ember/ezsp/buffalo.js.map +1 -0
  67. package/dist/adapter/ember/ezsp/consts.d.ts +116 -0
  68. package/dist/adapter/ember/ezsp/consts.d.ts.map +1 -0
  69. package/dist/adapter/ember/ezsp/consts.js +128 -0
  70. package/dist/adapter/ember/ezsp/consts.js.map +1 -0
  71. package/dist/adapter/ember/ezsp/enums.d.ts +879 -0
  72. package/dist/adapter/ember/ezsp/enums.d.ts.map +1 -0
  73. package/dist/adapter/ember/ezsp/enums.js +948 -0
  74. package/dist/adapter/ember/ezsp/enums.js.map +1 -0
  75. package/dist/adapter/ember/ezsp/ezsp.d.ts +2663 -0
  76. package/dist/adapter/ember/ezsp/ezsp.d.ts.map +1 -0
  77. package/dist/adapter/ember/ezsp/ezsp.js +6435 -0
  78. package/dist/adapter/ember/ezsp/ezsp.js.map +1 -0
  79. package/dist/adapter/ember/types.d.ts +733 -0
  80. package/dist/adapter/ember/types.d.ts.map +1 -0
  81. package/dist/adapter/ember/types.js +3 -0
  82. package/dist/adapter/ember/types.js.map +1 -0
  83. package/dist/adapter/ember/uart/ash.d.ts +458 -0
  84. package/dist/adapter/ember/uart/ash.d.ts.map +1 -0
  85. package/dist/adapter/ember/uart/ash.js +1601 -0
  86. package/dist/adapter/ember/uart/ash.js.map +1 -0
  87. package/dist/adapter/ember/uart/consts.d.ts +91 -0
  88. package/dist/adapter/ember/uart/consts.d.ts.map +1 -0
  89. package/dist/adapter/ember/uart/consts.js +100 -0
  90. package/dist/adapter/ember/uart/consts.js.map +1 -0
  91. package/dist/adapter/ember/uart/enums.d.ts +191 -0
  92. package/dist/adapter/ember/uart/enums.d.ts.map +1 -0
  93. package/dist/adapter/ember/uart/enums.js +197 -0
  94. package/dist/adapter/ember/uart/enums.js.map +1 -0
  95. package/dist/adapter/ember/uart/parser.d.ts +10 -0
  96. package/dist/adapter/ember/uart/parser.d.ts.map +1 -0
  97. package/dist/adapter/ember/uart/parser.js +41 -0
  98. package/dist/adapter/ember/uart/parser.js.map +1 -0
  99. package/dist/adapter/ember/uart/queues.d.ts +85 -0
  100. package/dist/adapter/ember/uart/queues.d.ts.map +1 -0
  101. package/dist/adapter/ember/uart/queues.js +212 -0
  102. package/dist/adapter/ember/uart/queues.js.map +1 -0
  103. package/dist/adapter/ember/uart/writer.d.ts +15 -0
  104. package/dist/adapter/ember/uart/writer.d.ts.map +1 -0
  105. package/dist/adapter/ember/uart/writer.js +48 -0
  106. package/dist/adapter/ember/uart/writer.js.map +1 -0
  107. package/dist/adapter/ember/utils/initters.d.ts +20 -0
  108. package/dist/adapter/ember/utils/initters.d.ts.map +1 -0
  109. package/dist/adapter/ember/utils/initters.js +58 -0
  110. package/dist/adapter/ember/utils/initters.js.map +1 -0
  111. package/dist/adapter/ember/utils/math.d.ts +51 -0
  112. package/dist/adapter/ember/utils/math.d.ts.map +1 -0
  113. package/dist/adapter/ember/utils/math.js +102 -0
  114. package/dist/adapter/ember/utils/math.js.map +1 -0
  115. package/dist/adapter/ember/zdo.d.ts +921 -0
  116. package/dist/adapter/ember/zdo.d.ts.map +1 -0
  117. package/dist/adapter/ember/zdo.js +723 -0
  118. package/dist/adapter/ember/zdo.js.map +1 -0
  119. package/dist/adapter/events.d.ts +47 -47
  120. package/dist/adapter/events.js +13 -14
  121. package/dist/adapter/events.js.map +1 -1
  122. package/dist/adapter/ezsp/adapter/backup.d.ts +13 -9
  123. package/dist/adapter/ezsp/adapter/backup.d.ts.map +1 -1
  124. package/dist/adapter/ezsp/adapter/backup.js +104 -53
  125. package/dist/adapter/ezsp/adapter/backup.js.map +1 -1
  126. package/dist/adapter/ezsp/adapter/ezspAdapter.d.ts +61 -60
  127. package/dist/adapter/ezsp/adapter/ezspAdapter.d.ts.map +1 -1
  128. package/dist/adapter/ezsp/adapter/ezspAdapter.js +626 -608
  129. package/dist/adapter/ezsp/adapter/ezspAdapter.js.map +1 -1
  130. package/dist/adapter/ezsp/adapter/index.d.ts +2 -2
  131. package/dist/adapter/ezsp/adapter/index.js +10 -10
  132. package/dist/adapter/ezsp/driver/commands.d.ts +36 -36
  133. package/dist/adapter/ezsp/driver/commands.d.ts.map +1 -1
  134. package/dist/adapter/ezsp/driver/commands.js +2388 -2359
  135. package/dist/adapter/ezsp/driver/commands.js.map +1 -1
  136. package/dist/adapter/ezsp/driver/consts.d.ts +10 -10
  137. package/dist/adapter/ezsp/driver/consts.js +13 -13
  138. package/dist/adapter/ezsp/driver/driver.d.ts +111 -101
  139. package/dist/adapter/ezsp/driver/driver.d.ts.map +1 -1
  140. package/dist/adapter/ezsp/driver/driver.js +816 -638
  141. package/dist/adapter/ezsp/driver/driver.js.map +1 -1
  142. package/dist/adapter/ezsp/driver/ezsp.d.ts +105 -96
  143. package/dist/adapter/ezsp/driver/ezsp.d.ts.map +1 -1
  144. package/dist/adapter/ezsp/driver/ezsp.js +652 -586
  145. package/dist/adapter/ezsp/driver/ezsp.js.map +1 -1
  146. package/dist/adapter/ezsp/driver/frame.d.ts +40 -0
  147. package/dist/adapter/ezsp/driver/frame.d.ts.map +1 -0
  148. package/dist/adapter/ezsp/driver/frame.js +101 -0
  149. package/dist/adapter/ezsp/driver/frame.js.map +1 -0
  150. package/dist/adapter/ezsp/driver/index.d.ts +3 -3
  151. package/dist/adapter/ezsp/driver/index.js +8 -8
  152. package/dist/adapter/ezsp/driver/multicast.d.ts +12 -12
  153. package/dist/adapter/ezsp/driver/multicast.js +77 -72
  154. package/dist/adapter/ezsp/driver/multicast.js.map +1 -1
  155. package/dist/adapter/ezsp/driver/parser.d.ts +11 -12
  156. package/dist/adapter/ezsp/driver/parser.d.ts.map +1 -1
  157. package/dist/adapter/ezsp/driver/parser.js +104 -111
  158. package/dist/adapter/ezsp/driver/parser.js.map +1 -1
  159. package/dist/adapter/ezsp/driver/types/basic.d.ts +62 -62
  160. package/dist/adapter/ezsp/driver/types/basic.js +208 -208
  161. package/dist/adapter/ezsp/driver/types/basic.js.map +1 -1
  162. package/dist/adapter/ezsp/driver/types/index.d.ts +9 -9
  163. package/dist/adapter/ezsp/driver/types/index.d.ts.map +1 -1
  164. package/dist/adapter/ezsp/driver/types/index.js +138 -133
  165. package/dist/adapter/ezsp/driver/types/index.js.map +1 -1
  166. package/dist/adapter/ezsp/driver/types/named.d.ts +1287 -697
  167. package/dist/adapter/ezsp/driver/types/named.d.ts.map +1 -1
  168. package/dist/adapter/ezsp/driver/types/named.js +2329 -1726
  169. package/dist/adapter/ezsp/driver/types/named.js.map +1 -1
  170. package/dist/adapter/ezsp/driver/types/struct.d.ts +270 -251
  171. package/dist/adapter/ezsp/driver/types/struct.d.ts.map +1 -1
  172. package/dist/adapter/ezsp/driver/types/struct.js +803 -708
  173. package/dist/adapter/ezsp/driver/types/struct.js.map +1 -1
  174. package/dist/adapter/ezsp/driver/uart.d.ts +48 -44
  175. package/dist/adapter/ezsp/driver/uart.d.ts.map +1 -1
  176. package/dist/adapter/ezsp/driver/uart.js +382 -368
  177. package/dist/adapter/ezsp/driver/uart.js.map +1 -1
  178. package/dist/adapter/ezsp/driver/utils/crc16ccitt.d.ts +2 -2
  179. package/dist/adapter/ezsp/driver/utils/crc16ccitt.js +55 -55
  180. package/dist/adapter/ezsp/driver/utils/crc16ccitt.js.map +1 -1
  181. package/dist/adapter/ezsp/driver/utils/index.d.ts +19 -18
  182. package/dist/adapter/ezsp/driver/utils/index.d.ts.map +1 -1
  183. package/dist/adapter/ezsp/driver/utils/index.js +72 -67
  184. package/dist/adapter/ezsp/driver/utils/index.js.map +1 -1
  185. package/dist/adapter/ezsp/driver/writer.d.ts +13 -13
  186. package/dist/adapter/ezsp/driver/writer.d.ts.map +1 -1
  187. package/dist/adapter/ezsp/driver/writer.js +85 -88
  188. package/dist/adapter/ezsp/driver/writer.js.map +1 -1
  189. package/dist/adapter/index.d.ts +4 -4
  190. package/dist/adapter/index.js +35 -35
  191. package/dist/adapter/serialPort.d.ts +13 -8
  192. package/dist/adapter/serialPort.d.ts.map +1 -1
  193. package/dist/adapter/serialPort.js +46 -22
  194. package/dist/adapter/serialPort.js.map +1 -1
  195. package/dist/adapter/serialPortUtils.d.ts +12 -12
  196. package/dist/adapter/serialPortUtils.js +18 -18
  197. package/dist/adapter/serialPortUtils.js.map +1 -1
  198. package/dist/adapter/socketPortUtils.d.ts +10 -10
  199. package/dist/adapter/socketPortUtils.js +16 -16
  200. package/dist/adapter/tstype.d.ts +85 -85
  201. package/dist/adapter/tstype.d.ts.map +1 -1
  202. package/dist/adapter/tstype.js +2 -2
  203. package/dist/adapter/z-stack/adapter/adapter-backup.d.ts +62 -62
  204. package/dist/adapter/z-stack/adapter/adapter-backup.js +462 -461
  205. package/dist/adapter/z-stack/adapter/adapter-backup.js.map +1 -1
  206. package/dist/adapter/z-stack/adapter/adapter-nv-memory.d.ts +150 -150
  207. package/dist/adapter/z-stack/adapter/adapter-nv-memory.js +258 -258
  208. package/dist/adapter/z-stack/adapter/adapter-nv-memory.js.map +1 -1
  209. package/dist/adapter/z-stack/adapter/endpoints.d.ts +11 -11
  210. package/dist/adapter/z-stack/adapter/endpoints.js +73 -73
  211. package/dist/adapter/z-stack/adapter/index.d.ts +2 -2
  212. package/dist/adapter/z-stack/adapter/index.js +8 -8
  213. package/dist/adapter/z-stack/adapter/manager.d.ts +86 -86
  214. package/dist/adapter/z-stack/adapter/manager.d.ts.map +1 -1
  215. package/dist/adapter/z-stack/adapter/manager.js +482 -476
  216. package/dist/adapter/z-stack/adapter/manager.js.map +1 -1
  217. package/dist/adapter/z-stack/adapter/tstype.d.ts +6 -6
  218. package/dist/adapter/z-stack/adapter/tstype.js +9 -10
  219. package/dist/adapter/z-stack/adapter/tstype.js.map +1 -1
  220. package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts +81 -81
  221. package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts.map +1 -1
  222. package/dist/adapter/z-stack/adapter/zStackAdapter.js +885 -868
  223. package/dist/adapter/z-stack/adapter/zStackAdapter.js.map +1 -1
  224. package/dist/adapter/z-stack/constants/af.d.ts +23 -23
  225. package/dist/adapter/z-stack/constants/af.js +27 -27
  226. package/dist/adapter/z-stack/constants/common.d.ts +278 -278
  227. package/dist/adapter/z-stack/constants/common.d.ts.map +1 -1
  228. package/dist/adapter/z-stack/constants/common.js +292 -289
  229. package/dist/adapter/z-stack/constants/common.js.map +1 -1
  230. package/dist/adapter/z-stack/constants/dbg.d.ts +22 -22
  231. package/dist/adapter/z-stack/constants/dbg.js +24 -24
  232. package/dist/adapter/z-stack/constants/index.d.ts +10 -10
  233. package/dist/adapter/z-stack/constants/index.js +47 -47
  234. package/dist/adapter/z-stack/constants/mac.d.ts +127 -127
  235. package/dist/adapter/z-stack/constants/mac.js +129 -129
  236. package/dist/adapter/z-stack/constants/sapi.d.ts +24 -24
  237. package/dist/adapter/z-stack/constants/sapi.js +26 -26
  238. package/dist/adapter/z-stack/constants/sys.d.ts +71 -71
  239. package/dist/adapter/z-stack/constants/sys.js +73 -73
  240. package/dist/adapter/z-stack/constants/util.d.ts +81 -81
  241. package/dist/adapter/z-stack/constants/util.js +83 -83
  242. package/dist/adapter/z-stack/constants/utils.d.ts +4 -4
  243. package/dist/adapter/z-stack/constants/utils.js +14 -14
  244. package/dist/adapter/z-stack/constants/zdo.d.ts +102 -102
  245. package/dist/adapter/z-stack/constants/zdo.js +104 -104
  246. package/dist/adapter/z-stack/models/index.d.ts +1 -1
  247. package/dist/adapter/z-stack/models/index.js +17 -17
  248. package/dist/adapter/z-stack/models/startup-options.d.ts +12 -12
  249. package/dist/adapter/z-stack/models/startup-options.js +2 -2
  250. package/dist/adapter/z-stack/structs/entries/address-manager-entry.d.ts +23 -23
  251. package/dist/adapter/z-stack/structs/entries/address-manager-entry.js +45 -45
  252. package/dist/adapter/z-stack/structs/entries/address-manager-entry.js.map +1 -1
  253. package/dist/adapter/z-stack/structs/entries/address-manager-table.d.ts +10 -10
  254. package/dist/adapter/z-stack/structs/entries/address-manager-table.js +22 -22
  255. package/dist/adapter/z-stack/structs/entries/aps-link-key-data-entry.d.ts +10 -10
  256. package/dist/adapter/z-stack/structs/entries/aps-link-key-data-entry.js +21 -21
  257. package/dist/adapter/z-stack/structs/entries/aps-link-key-data-table.d.ts +10 -10
  258. package/dist/adapter/z-stack/structs/entries/aps-link-key-data-table.js +23 -23
  259. package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-entry.d.ts +10 -10
  260. package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-entry.js +24 -24
  261. package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-table.d.ts +10 -10
  262. package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-table.js +23 -23
  263. package/dist/adapter/z-stack/structs/entries/channel-list.d.ts +8 -8
  264. package/dist/adapter/z-stack/structs/entries/channel-list.js +15 -15
  265. package/dist/adapter/z-stack/structs/entries/has-configured.d.ts +8 -8
  266. package/dist/adapter/z-stack/structs/entries/has-configured.js +16 -16
  267. package/dist/adapter/z-stack/structs/entries/index.d.ts +16 -16
  268. package/dist/adapter/z-stack/structs/entries/index.js +32 -32
  269. package/dist/adapter/z-stack/structs/entries/nib.d.ts +10 -10
  270. package/dist/adapter/z-stack/structs/entries/nib.js +68 -68
  271. package/dist/adapter/z-stack/structs/entries/nwk-key-descriptor.d.ts +10 -10
  272. package/dist/adapter/z-stack/structs/entries/nwk-key-descriptor.js +18 -18
  273. package/dist/adapter/z-stack/structs/entries/nwk-key.d.ts +8 -8
  274. package/dist/adapter/z-stack/structs/entries/nwk-key.js +15 -15
  275. package/dist/adapter/z-stack/structs/entries/nwk-pan-id.d.ts +8 -8
  276. package/dist/adapter/z-stack/structs/entries/nwk-pan-id.js +15 -15
  277. package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-entry.d.ts +13 -13
  278. package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-entry.js +23 -23
  279. package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-table.d.ts +10 -10
  280. package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-table.js +22 -22
  281. package/dist/adapter/z-stack/structs/entries/security-manager-entry.d.ts +20 -20
  282. package/dist/adapter/z-stack/structs/entries/security-manager-entry.js +36 -36
  283. package/dist/adapter/z-stack/structs/entries/security-manager-entry.js.map +1 -1
  284. package/dist/adapter/z-stack/structs/entries/security-manager-table.d.ts +10 -10
  285. package/dist/adapter/z-stack/structs/entries/security-manager-table.js +24 -24
  286. package/dist/adapter/z-stack/structs/index.d.ts +4 -4
  287. package/dist/adapter/z-stack/structs/index.js +20 -20
  288. package/dist/adapter/z-stack/structs/serializable-memory-object.d.ts +13 -13
  289. package/dist/adapter/z-stack/structs/serializable-memory-object.js +2 -2
  290. package/dist/adapter/z-stack/structs/struct.d.ts +99 -99
  291. package/dist/adapter/z-stack/structs/struct.js +296 -295
  292. package/dist/adapter/z-stack/structs/struct.js.map +1 -1
  293. package/dist/adapter/z-stack/structs/table.d.ts +94 -94
  294. package/dist/adapter/z-stack/structs/table.js +163 -161
  295. package/dist/adapter/z-stack/structs/table.js.map +1 -1
  296. package/dist/adapter/z-stack/unpi/constants.d.ts +28 -28
  297. package/dist/adapter/z-stack/unpi/constants.js +39 -41
  298. package/dist/adapter/z-stack/unpi/constants.js.map +1 -1
  299. package/dist/adapter/z-stack/unpi/frame.d.ts +16 -16
  300. package/dist/adapter/z-stack/unpi/frame.js +54 -48
  301. package/dist/adapter/z-stack/unpi/frame.js.map +1 -1
  302. package/dist/adapter/z-stack/unpi/index.d.ts +5 -5
  303. package/dist/adapter/z-stack/unpi/index.js +37 -37
  304. package/dist/adapter/z-stack/unpi/parser.d.ts +10 -10
  305. package/dist/adapter/z-stack/unpi/parser.js +75 -74
  306. package/dist/adapter/z-stack/unpi/parser.js.map +1 -1
  307. package/dist/adapter/z-stack/unpi/writer.d.ts +10 -10
  308. package/dist/adapter/z-stack/unpi/writer.js +44 -44
  309. package/dist/adapter/z-stack/utils/channel-list.d.ts +20 -20
  310. package/dist/adapter/z-stack/utils/channel-list.js +40 -40
  311. package/dist/adapter/z-stack/utils/channel-list.js.map +1 -1
  312. package/dist/adapter/z-stack/utils/index.d.ts +2 -2
  313. package/dist/adapter/z-stack/utils/index.js +18 -18
  314. package/dist/adapter/z-stack/utils/network-options.d.ts +8 -8
  315. package/dist/adapter/z-stack/utils/network-options.js +22 -22
  316. package/dist/adapter/z-stack/znp/buffaloZnp.d.ts +11 -11
  317. package/dist/adapter/z-stack/znp/buffaloZnp.js +113 -113
  318. package/dist/adapter/z-stack/znp/buffaloZnp.js.map +1 -1
  319. package/dist/adapter/z-stack/znp/definition.d.ts +5 -5
  320. package/dist/adapter/z-stack/znp/definition.js +3050 -3050
  321. package/dist/adapter/z-stack/znp/index.d.ts +3 -3
  322. package/dist/adapter/z-stack/znp/index.js +10 -10
  323. package/dist/adapter/z-stack/znp/parameterType.d.ts +22 -22
  324. package/dist/adapter/z-stack/znp/parameterType.js +25 -25
  325. package/dist/adapter/z-stack/znp/tstype.d.ts +21 -21
  326. package/dist/adapter/z-stack/znp/tstype.js +2 -2
  327. package/dist/adapter/z-stack/znp/znp.d.ts +44 -43
  328. package/dist/adapter/z-stack/znp/znp.d.ts.map +1 -1
  329. package/dist/adapter/z-stack/znp/znp.js +326 -325
  330. package/dist/adapter/z-stack/znp/znp.js.map +1 -1
  331. package/dist/adapter/z-stack/znp/zpiObject.d.ts +19 -19
  332. package/dist/adapter/z-stack/znp/zpiObject.js +102 -96
  333. package/dist/adapter/z-stack/znp/zpiObject.js.map +1 -1
  334. package/dist/adapter/zigate/adapter/index.d.ts +2 -2
  335. package/dist/adapter/zigate/adapter/index.js +10 -10
  336. package/dist/adapter/zigate/adapter/zigateAdapter.d.ts +70 -70
  337. package/dist/adapter/zigate/adapter/zigateAdapter.js +689 -684
  338. package/dist/adapter/zigate/adapter/zigateAdapter.js.map +1 -1
  339. package/dist/adapter/zigate/debug.d.ts +7 -7
  340. package/dist/adapter/zigate/debug.d.ts.map +1 -1
  341. package/dist/adapter/zigate/debug.js +19 -22
  342. package/dist/adapter/zigate/debug.js.map +1 -1
  343. package/dist/adapter/zigate/driver/buffaloZiGate.d.ts +18 -18
  344. package/dist/adapter/zigate/driver/buffaloZiGate.js +139 -139
  345. package/dist/adapter/zigate/driver/buffaloZiGate.js.map +1 -1
  346. package/dist/adapter/zigate/driver/commandType.d.ts +41 -41
  347. package/dist/adapter/zigate/driver/commandType.js +385 -385
  348. package/dist/adapter/zigate/driver/commandType.js.map +1 -1
  349. package/dist/adapter/zigate/driver/constants.d.ts +276 -276
  350. package/dist/adapter/zigate/driver/constants.d.ts.map +1 -1
  351. package/dist/adapter/zigate/driver/constants.js +371 -371
  352. package/dist/adapter/zigate/driver/constants.js.map +1 -1
  353. package/dist/adapter/zigate/driver/frame.d.ts +26 -26
  354. package/dist/adapter/zigate/driver/frame.js +172 -172
  355. package/dist/adapter/zigate/driver/frame.js.map +1 -1
  356. package/dist/adapter/zigate/driver/messageType.d.ts +11 -11
  357. package/dist/adapter/zigate/driver/messageType.js +278 -278
  358. package/dist/adapter/zigate/driver/messageType.js.map +1 -1
  359. package/dist/adapter/zigate/driver/parameterType.d.ts +20 -20
  360. package/dist/adapter/zigate/driver/parameterType.js +23 -23
  361. package/dist/adapter/zigate/driver/ziGateObject.d.ts +23 -23
  362. package/dist/adapter/zigate/driver/ziGateObject.js +110 -106
  363. package/dist/adapter/zigate/driver/ziGateObject.js.map +1 -1
  364. package/dist/adapter/zigate/driver/zigate.d.ts +49 -49
  365. package/dist/adapter/zigate/driver/zigate.d.ts.map +1 -1
  366. package/dist/adapter/zigate/driver/zigate.js +296 -303
  367. package/dist/adapter/zigate/driver/zigate.js.map +1 -1
  368. package/dist/buffalo/buffalo.d.ts +50 -50
  369. package/dist/buffalo/buffalo.js +324 -322
  370. package/dist/buffalo/buffalo.js.map +1 -1
  371. package/dist/buffalo/index.d.ts +3 -3
  372. package/dist/buffalo/index.js +33 -33
  373. package/dist/buffalo/tstype.d.ts +8 -8
  374. package/dist/buffalo/tstype.js +2 -2
  375. package/dist/controller/controller.d.ts +113 -113
  376. package/dist/controller/controller.d.ts.map +1 -1
  377. package/dist/controller/controller.js +641 -619
  378. package/dist/controller/controller.js.map +1 -1
  379. package/dist/controller/database.d.ts +18 -18
  380. package/dist/controller/database.js +96 -93
  381. package/dist/controller/database.js.map +1 -1
  382. package/dist/controller/events.d.ts +58 -58
  383. package/dist/controller/events.d.ts.map +1 -1
  384. package/dist/controller/events.js +108 -102
  385. package/dist/controller/events.js.map +1 -1
  386. package/dist/controller/greenPower.d.ts +12 -12
  387. package/dist/controller/greenPower.js +221 -220
  388. package/dist/controller/greenPower.js.map +1 -1
  389. package/dist/controller/helpers/index.d.ts +2 -2
  390. package/dist/controller/helpers/index.js +28 -28
  391. package/dist/controller/helpers/request.d.ts +21 -22
  392. package/dist/controller/helpers/request.d.ts.map +1 -1
  393. package/dist/controller/helpers/request.js +77 -71
  394. package/dist/controller/helpers/request.js.map +1 -1
  395. package/dist/controller/helpers/requestQueue.d.ts +13 -0
  396. package/dist/controller/helpers/requestQueue.d.ts.map +1 -0
  397. package/dist/controller/helpers/requestQueue.js +116 -0
  398. package/dist/controller/helpers/requestQueue.js.map +1 -0
  399. package/dist/controller/helpers/zclFrameConverter.d.ts +7 -7
  400. package/dist/controller/helpers/zclFrameConverter.d.ts.map +1 -1
  401. package/dist/controller/helpers/zclFrameConverter.js +50 -31
  402. package/dist/controller/helpers/zclFrameConverter.js.map +1 -1
  403. package/dist/controller/helpers/zclTransactionSequenceNumber.d.ts +5 -5
  404. package/dist/controller/helpers/zclTransactionSequenceNumber.js +13 -13
  405. package/dist/controller/helpers/zclTransactionSequenceNumber.js.map +1 -1
  406. package/dist/controller/index.d.ts +5 -5
  407. package/dist/controller/index.js +8 -8
  408. package/dist/controller/logger-stub.d.ts +6 -6
  409. package/dist/controller/logger-stub.js +2 -2
  410. package/dist/controller/model/device.d.ts +132 -133
  411. package/dist/controller/model/device.d.ts.map +1 -1
  412. package/dist/controller/model/device.js +726 -717
  413. package/dist/controller/model/device.js.map +1 -1
  414. package/dist/controller/model/endpoint.d.ts +128 -131
  415. package/dist/controller/model/endpoint.d.ts.map +1 -1
  416. package/dist/controller/model/endpoint.js +755 -821
  417. package/dist/controller/model/endpoint.js.map +1 -1
  418. package/dist/controller/model/entity.d.ts +14 -14
  419. package/dist/controller/model/entity.js +26 -26
  420. package/dist/controller/model/entity.js.map +1 -1
  421. package/dist/controller/model/group.d.ts +38 -38
  422. package/dist/controller/model/group.d.ts.map +1 -1
  423. package/dist/controller/model/group.js +225 -221
  424. package/dist/controller/model/group.js.map +1 -1
  425. package/dist/controller/model/index.d.ts +5 -5
  426. package/dist/controller/model/index.js +14 -14
  427. package/dist/controller/touchlink.d.ts +19 -19
  428. package/dist/controller/touchlink.js +159 -157
  429. package/dist/controller/touchlink.js.map +1 -1
  430. package/dist/controller/tstype.d.ts +20 -21
  431. package/dist/controller/tstype.d.ts.map +1 -1
  432. package/dist/controller/tstype.js +8 -9
  433. package/dist/controller/tstype.js.map +1 -1
  434. package/dist/index.d.ts +3 -3
  435. package/dist/index.js +33 -33
  436. package/dist/models/backup-storage-legacy.d.ts +26 -26
  437. package/dist/models/backup-storage-legacy.js +2 -2
  438. package/dist/models/backup-storage-unified.d.ts +49 -49
  439. package/dist/models/backup-storage-unified.js +2 -2
  440. package/dist/models/backup.d.ts +37 -37
  441. package/dist/models/backup.js +2 -2
  442. package/dist/models/index.d.ts +4 -4
  443. package/dist/models/index.js +20 -20
  444. package/dist/models/network-options.d.ts +12 -12
  445. package/dist/models/network-options.js +2 -2
  446. package/dist/utils/assertString.d.ts +2 -2
  447. package/dist/utils/assertString.js +8 -8
  448. package/dist/utils/assertString.js.map +1 -1
  449. package/dist/utils/backup.d.ts +20 -20
  450. package/dist/utils/backup.d.ts.map +1 -1
  451. package/dist/utils/backup.js +189 -187
  452. package/dist/utils/backup.js.map +1 -1
  453. package/dist/utils/equalsPartial.d.ts +2 -2
  454. package/dist/utils/equalsPartial.js +11 -11
  455. package/dist/utils/index.d.ts +9 -9
  456. package/dist/utils/index.js +45 -45
  457. package/dist/utils/isNumberArray.d.ts +2 -2
  458. package/dist/utils/isNumberArray.js +6 -6
  459. package/dist/utils/queue.d.ts +11 -11
  460. package/dist/utils/queue.d.ts.map +1 -1
  461. package/dist/utils/queue.js +61 -50
  462. package/dist/utils/queue.js.map +1 -1
  463. package/dist/utils/realpathSync.d.ts +2 -2
  464. package/dist/utils/realpathSync.js +12 -12
  465. package/dist/utils/wait.d.ts +2 -2
  466. package/dist/utils/wait.js +8 -8
  467. package/dist/utils/waitress.d.ts +21 -21
  468. package/dist/utils/waitress.d.ts.map +1 -1
  469. package/dist/utils/waitress.js +68 -61
  470. package/dist/utils/waitress.js.map +1 -1
  471. package/dist/zcl/buffaloZcl.d.ts +41 -41
  472. package/dist/zcl/buffaloZcl.d.ts.map +1 -1
  473. package/dist/zcl/buffaloZcl.js +594 -591
  474. package/dist/zcl/buffaloZcl.js.map +1 -1
  475. package/dist/zcl/definition/buffaloZclDataType.d.ts +17 -17
  476. package/dist/zcl/definition/buffaloZclDataType.js +20 -20
  477. package/dist/zcl/definition/cluster.d.ts +29 -29
  478. package/dist/zcl/definition/cluster.d.ts.map +1 -1
  479. package/dist/zcl/definition/cluster.js +5530 -5335
  480. package/dist/zcl/definition/cluster.js.map +1 -1
  481. package/dist/zcl/definition/dataType.d.ts +59 -59
  482. package/dist/zcl/definition/dataType.js +64 -64
  483. package/dist/zcl/definition/direction.d.ts +5 -5
  484. package/dist/zcl/definition/direction.js +8 -8
  485. package/dist/zcl/definition/endpointDeviceType.d.ts +4 -4
  486. package/dist/zcl/definition/endpointDeviceType.js +15 -15
  487. package/dist/zcl/definition/foundation.d.ts +11 -11
  488. package/dist/zcl/definition/foundation.js +167 -167
  489. package/dist/zcl/definition/frameControl.d.ts +10 -10
  490. package/dist/zcl/definition/frameControl.js +2 -2
  491. package/dist/zcl/definition/frameType.d.ts +5 -5
  492. package/dist/zcl/definition/frameType.js +8 -8
  493. package/dist/zcl/definition/index.d.ts +13 -13
  494. package/dist/zcl/definition/index.js +51 -51
  495. package/dist/zcl/definition/manufacturerCode.d.ts +1077 -1074
  496. package/dist/zcl/definition/manufacturerCode.d.ts.map +1 -1
  497. package/dist/zcl/definition/manufacturerCode.js +1082 -1079
  498. package/dist/zcl/definition/manufacturerCode.js.map +1 -1
  499. package/dist/zcl/definition/powerSource.d.ts +4 -4
  500. package/dist/zcl/definition/powerSource.js +12 -12
  501. package/dist/zcl/definition/status.d.ts +38 -38
  502. package/dist/zcl/definition/status.js +41 -41
  503. package/dist/zcl/definition/tstype.d.ts +16 -16
  504. package/dist/zcl/definition/tstype.js +2 -2
  505. package/dist/zcl/index.d.ts +16 -16
  506. package/dist/zcl/index.js +55 -55
  507. package/dist/zcl/tstype.d.ts +56 -56
  508. package/dist/zcl/tstype.js +9 -10
  509. package/dist/zcl/tstype.js.map +1 -1
  510. package/dist/zcl/utils.d.ts +6 -6
  511. package/dist/zcl/utils.js +164 -165
  512. package/dist/zcl/utils.js.map +1 -1
  513. package/dist/zcl/zclFrame.d.ts +40 -40
  514. package/dist/zcl/zclFrame.js +351 -347
  515. package/dist/zcl/zclFrame.js.map +1 -1
  516. package/dist/zcl/zclHeader.d.ts +8 -8
  517. package/dist/zcl/zclHeader.js +2 -2
  518. package/dist/zcl/zclStatusError.d.ts +5 -5
  519. package/dist/zcl/zclStatusError.js +14 -13
  520. package/dist/zcl/zclStatusError.js.map +1 -1
  521. package/package.json +11 -11
  522. package/release-please-config.json +1 -5
  523. package/tsconfig.json +4 -2
@@ -0,0 +1,2970 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.EmberAdapter = void 0;
7
+ /* istanbul ignore file */
8
+ const debug_1 = __importDefault(require("debug"));
9
+ const es6_1 = __importDefault(require("fast-deep-equal/es6"));
10
+ const mz_1 = require("mz");
11
+ const serialPortUtils_1 = __importDefault(require("../../serialPortUtils"));
12
+ const socketPortUtils_1 = __importDefault(require("../../socketPortUtils"));
13
+ const utils_1 = require("../../../utils");
14
+ const __1 = require("../..");
15
+ const zcl_1 = require("../../../zcl");
16
+ const cluster_1 = __importDefault(require("../../../zcl/definition/cluster"));
17
+ const events_1 = require("../../events");
18
+ const math_1 = require("../utils/math");
19
+ const ezsp_1 = require("../ezsp/ezsp");
20
+ const consts_1 = require("../ezsp/consts");
21
+ const enums_1 = require("../ezsp/enums");
22
+ const buffalo_1 = require("../ezsp/buffalo");
23
+ const enums_2 = require("../enums");
24
+ const zdo_1 = require("../zdo");
25
+ const consts_2 = require("../consts");
26
+ const requestQueue_1 = require("./requestQueue");
27
+ const endpoints_1 = require("./endpoints");
28
+ const initters_1 = require("../utils/initters");
29
+ const crypto_1 = require("crypto");
30
+ const oneWaitress_1 = require("./oneWaitress");
31
+ const debug = (0, debug_1.default)('zigbee-herdsman:adapter:ember:adapter');
32
+ /** Enum to pass strings from numbers up to Z2M. */
33
+ var RoutingTableStatus;
34
+ (function (RoutingTableStatus) {
35
+ RoutingTableStatus[RoutingTableStatus["ACTIVE"] = 0] = "ACTIVE";
36
+ RoutingTableStatus[RoutingTableStatus["DISCOVERY_UNDERWAY"] = 1] = "DISCOVERY_UNDERWAY";
37
+ RoutingTableStatus[RoutingTableStatus["DISCOVERY_FAILED"] = 2] = "DISCOVERY_FAILED";
38
+ RoutingTableStatus[RoutingTableStatus["INACTIVE"] = 3] = "INACTIVE";
39
+ RoutingTableStatus[RoutingTableStatus["VALIDATION_UNDERWAY"] = 4] = "VALIDATION_UNDERWAY";
40
+ RoutingTableStatus[RoutingTableStatus["RESERVED1"] = 5] = "RESERVED1";
41
+ RoutingTableStatus[RoutingTableStatus["RESERVED2"] = 6] = "RESERVED2";
42
+ RoutingTableStatus[RoutingTableStatus["RESERVED3"] = 7] = "RESERVED3";
43
+ })(RoutingTableStatus || (RoutingTableStatus = {}));
44
+ ;
45
+ /** Events specific to OneWaitress usage. */
46
+ var OneWaitressEvents;
47
+ (function (OneWaitressEvents) {
48
+ OneWaitressEvents["STACK_STATUS_NETWORK_UP"] = "STACK_STATUS_NETWORK_UP";
49
+ OneWaitressEvents["STACK_STATUS_NETWORK_DOWN"] = "STACK_STATUS_NETWORK_DOWN";
50
+ OneWaitressEvents["STACK_STATUS_NETWORK_OPENED"] = "STACK_STATUS_NETWORK_OPENED";
51
+ OneWaitressEvents["STACK_STATUS_NETWORK_CLOSED"] = "STACK_STATUS_NETWORK_CLOSED";
52
+ })(OneWaitressEvents || (OneWaitressEvents = {}));
53
+ ;
54
+ var NetworkInitAction;
55
+ (function (NetworkInitAction) {
56
+ /** Ain't that nice! */
57
+ NetworkInitAction[NetworkInitAction["DONE"] = 0] = "DONE";
58
+ /** Config mismatch, must leave network. */
59
+ NetworkInitAction[NetworkInitAction["LEAVE"] = 1] = "LEAVE";
60
+ /** Config mismatched, left network. Will evaluate forming from backup or config next. */
61
+ NetworkInitAction[NetworkInitAction["LEFT"] = 2] = "LEFT";
62
+ /** Form the network using config. No backup, or backup mismatch. */
63
+ NetworkInitAction[NetworkInitAction["FORM_CONFIG"] = 3] = "FORM_CONFIG";
64
+ /** Re-form the network using full backed-up data. */
65
+ NetworkInitAction[NetworkInitAction["FORM_BACKUP"] = 4] = "FORM_BACKUP";
66
+ })(NetworkInitAction || (NetworkInitAction = {}));
67
+ ;
68
+ /** NOTE: Drivers can override `manufacturer`. Verify logic doesn't work in most cases anyway. */
69
+ const autoDetectDefinitions = [
70
+ /** NOTE: Manuf code "0x1321" for "Shenzhen Sonoff Technologies Co., Ltd." */
71
+ { manufacturer: 'ITEAD', vendorId: '1a86', productId: '55d4' }, // Sonoff ZBDongle-E
72
+ /** NOTE: Manuf code "0x134B" for "Nabu Casa, Inc." */
73
+ { manufacturer: 'Nabu Casa', vendorId: '10c4', productId: 'ea60' }, // Home Assistant SkyConnect
74
+ ];
75
+ /**
76
+ * Config for EMBER_LOW_RAM_CONCENTRATOR type concentrator.
77
+ *
78
+ * Based on ZigbeeMinimalHost/zigpc
79
+ */
80
+ const LOW_RAM_CONCENTRATOR_CONFIG = {
81
+ minTime: 5, // zigpc: 10
82
+ maxTime: 60, // zigpc: 60
83
+ routeErrorThreshold: 3, // zigpc: 3
84
+ deliveryFailureThreshold: 1, // zigpc: 1, ZigbeeMinimalHost: 3
85
+ mapHops: 0, // zigpc: 0
86
+ };
87
+ /**
88
+ * Config for EMBER_HIGH_RAM_CONCENTRATOR type concentrator.
89
+ *
90
+ * XXX: For now, same as low, until proper values can be determined.
91
+ */
92
+ const HIGH_RAM_CONCENTRATOR_CONFIG = {
93
+ minTime: 5,
94
+ maxTime: 60,
95
+ routeErrorThreshold: 3,
96
+ deliveryFailureThreshold: 1,
97
+ mapHops: 0,
98
+ };
99
+ /**
100
+ * Application generated ZDO messages use sequence numbers 0-127, and the stack
101
+ * uses sequence numbers 128-255. This simplifies life by eliminating the need
102
+ * for coordination between the two entities, and allows both to send ZDO
103
+ * messages with non-conflicting sequence numbers.
104
+ */
105
+ const APPLICATION_ZDO_SEQUENCE_MASK = 0x7F;
106
+ /** Current revision of the spec by zigbee alliance. XXX: what are `Zigbee Pro 2023` devices reporting?? */
107
+ const CURRENT_ZIGBEE_SPEC_REVISION = 23;
108
+ /** Each scan period is 15.36ms. Scan for at least 200ms (2^4 + 1 periods) to pick up WiFi beacon frames. */
109
+ const ENERGY_SCAN_DURATION = 4;
110
+ /** Oldest supported EZSP version for backups. Don't take the risk to restore a broken network until older backup versions can be investigated. */
111
+ const BACKUP_OLDEST_SUPPORTED_EZSP_VERSION = 12;
112
+ /**
113
+ * 9sec is minimum recommended for `ezspBroadcastNextNetworkKey` to have propagated throughout network.
114
+ * NOTE: This is blocking the request queue, so we shouldn't go crazy high.
115
+ */
116
+ const BROADCAST_NETWORK_KEY_SWITCH_WAIT_TIME = 15000;
117
+ /**
118
+ * Stack configuration values for various supported stacks.
119
+ */
120
+ const STACK_CONFIGS = {
121
+ "default": {
122
+ /** <1-250> (Default: 2) @see EzspConfigId.ADDRESS_TABLE_SIZE */
123
+ ADDRESS_TABLE_SIZE: 16, // zigpc: 32, darkxst: 16
124
+ /** <0-4> (Default: 2) @see EzspConfigId.TRUST_CENTER_ADDRESS_CACHE_SIZE */
125
+ TRUST_CENTER_ADDRESS_CACHE_SIZE: 2,
126
+ /** (Default: USE_TOKEN) @see EzspConfigId.TX_POWER_MODE */
127
+ TX_POWER_MODE: enums_2.EmberTXPowerMode.USE_TOKEN,
128
+ /** <-> (Default: 1) @see EzspConfigId.SUPPORTED_NETWORKS */
129
+ SUPPORTED_NETWORKS: 1,
130
+ /** <-> (Default: ) @see EzspConfigId.STACK_PROFILE */
131
+ STACK_PROFILE: consts_2.STACK_PROFILE_ZIGBEE_PRO,
132
+ /** <-> (Default: ) @see EzspConfigId.SECURITY_LEVEL */
133
+ SECURITY_LEVEL: consts_2.SECURITY_LEVEL_Z3,
134
+ /** (Default: KEEP_ALIVE_SUPPORT_ALL) @see EzspValueId.END_DEVICE_KEEP_ALIVE_SUPPORT_MODE */
135
+ END_DEVICE_KEEP_ALIVE_SUPPORT_MODE: enums_2.EmberKeepAliveMode.KEEP_ALIVE_SUPPORT_ALL, // zigpc: KEEP_ALIVE_SUPPORT_ALL
136
+ /** <-> (Default: MAXIMUM_APS_PAYLOAD_LENGTH) @see EzspValueId.MAXIMUM_INCOMING_TRANSFER_SIZE */
137
+ MAXIMUM_INCOMING_TRANSFER_SIZE: consts_2.MAXIMUM_APS_PAYLOAD_LENGTH,
138
+ /** <-> (Default: MAXIMUM_APS_PAYLOAD_LENGTH) @see EzspValueId.MAXIMUM_OUTGOING_TRANSFER_SIZE */
139
+ MAXIMUM_OUTGOING_TRANSFER_SIZE: consts_2.MAXIMUM_APS_PAYLOAD_LENGTH,
140
+ /** <-> (Default: 10000) @see EzspValueId.TRANSIENT_DEVICE_TIMEOUT */
141
+ TRANSIENT_DEVICE_TIMEOUT: 10000,
142
+ /** <0-127> (Default: 2) @see EzspConfigId.BINDING_TABLE_SIZE */
143
+ BINDING_TABLE_SIZE: 5, // zigpc: 2, Z3GatewayGPCombo: 5
144
+ /** <0-127> (Default: 0) @see EzspConfigId.KEY_TABLE_SIZE */
145
+ KEY_TABLE_SIZE: 0, // zigpc: 4
146
+ /** <6-64> (Default: 6) @see EzspConfigId.MAX_END_DEVICE_CHILDREN */
147
+ MAX_END_DEVICE_CHILDREN: 6, // zigpc: 6
148
+ /** <1-255> (Default: 10) @see EzspConfigId.APS_UNICAST_MESSAGE_COUNT */
149
+ APS_UNICAST_MESSAGE_COUNT: 20, // zigpc: 10, darkxst: 20
150
+ /** <15-254> (Default: 15) @see EzspConfigId.BROADCAST_TABLE_SIZE */
151
+ BROADCAST_TABLE_SIZE: 15, // zigpc: 15, Z3GatewayGPCombo: 35 - NOTE: Sonoff Dongle-E fails at 35
152
+ /** [1, 16, 26] (Default: 16). @see EzspConfigId.NEIGHBOR_TABLE_SIZE */
153
+ NEIGHBOR_TABLE_SIZE: 26, // zigpc: 16, darkxst: 26
154
+ /** (Default: 8) @see EzspConfigId.END_DEVICE_POLL_TIMEOUT */
155
+ END_DEVICE_POLL_TIMEOUT: 8, // zigpc: 8
156
+ /** <0-65535> (Default: 300) @see EzspConfigId.TRANSIENT_KEY_TIMEOUT_S */
157
+ TRANSIENT_KEY_TIMEOUT_S: 300, // zigpc: 65535
158
+ /** <-> (Default: 16) @see EzspConfigId.RETRY_QUEUE_SIZE */
159
+ RETRY_QUEUE_SIZE: 16,
160
+ /** <0-255> (Default: 0) @see EzspConfigId.SOURCE_ROUTE_TABLE_SIZE */
161
+ SOURCE_ROUTE_TABLE_SIZE: 200, // Z3GatewayGPCombo: 100, darkxst: 200
162
+ /** <1-250> (Default: 8) @see EzspConfigId.MULTICAST_TABLE_SIZE */
163
+ MULTICAST_TABLE_SIZE: 16, // darkxst: 16
164
+ },
165
+ "zigbeed": {
166
+ ADDRESS_TABLE_SIZE: 128,
167
+ TRUST_CENTER_ADDRESS_CACHE_SIZE: 2,
168
+ TX_POWER_MODE: enums_2.EmberTXPowerMode.USE_TOKEN,
169
+ SUPPORTED_NETWORKS: 1,
170
+ STACK_PROFILE: consts_2.STACK_PROFILE_ZIGBEE_PRO,
171
+ SECURITY_LEVEL: consts_2.SECURITY_LEVEL_Z3,
172
+ END_DEVICE_KEEP_ALIVE_SUPPORT_MODE: enums_2.EmberKeepAliveMode.KEEP_ALIVE_SUPPORT_ALL,
173
+ MAXIMUM_INCOMING_TRANSFER_SIZE: consts_2.MAXIMUM_APS_PAYLOAD_LENGTH,
174
+ MAXIMUM_OUTGOING_TRANSFER_SIZE: consts_2.MAXIMUM_APS_PAYLOAD_LENGTH,
175
+ TRANSIENT_DEVICE_TIMEOUT: 10000,
176
+ BINDING_TABLE_SIZE: 128,
177
+ KEY_TABLE_SIZE: 0, // zigbeed 128
178
+ MAX_END_DEVICE_CHILDREN: 64,
179
+ APS_UNICAST_MESSAGE_COUNT: 32,
180
+ BROADCAST_TABLE_SIZE: 15,
181
+ NEIGHBOR_TABLE_SIZE: 26,
182
+ END_DEVICE_POLL_TIMEOUT: 8,
183
+ TRANSIENT_KEY_TIMEOUT_S: 300,
184
+ RETRY_QUEUE_SIZE: 16,
185
+ SOURCE_ROUTE_TABLE_SIZE: 254,
186
+ MULTICAST_TABLE_SIZE: 128,
187
+ /*
188
+ ROUTE_TABLE_SIZE: 254,
189
+ DISCOVERY_TABLE_SIZE: 64,
190
+ PACKET_BUFFER_COUNT: 255,
191
+ CUSTOM_MAC_FILTER_TABLE_SIZE: 64,
192
+ MAC_FILTER_TABLE_SIZE: 32,
193
+ CHILD_TABLE_SIZE: 64,
194
+ PLUGIN_ZIGBEE_PRO_STACK_CHILD_TABLE_SIZE: 64,
195
+ APS_MESSAGE_COUNT: 64,
196
+ */
197
+ },
198
+ };
199
+ /**
200
+ * Enabling this allows to immediately reject requests that won't be able to get to their destination.
201
+ * However, it causes more NCP calls, notably to get the source route overhead.
202
+ * XXX: Needs further testing before enabling
203
+ */
204
+ const CHECK_APS_PAYLOAD_LENGTH = false;
205
+ /** Time for a ZDO request to get a callback response. ASH is 2400*6 for ACK timeout. */
206
+ const DEFAULT_ZDO_REQUEST_TIMEOUT = 15000; // msec
207
+ /** Time for a ZCL request to get a callback response. ASH is 2400*6 for ACK timeout. */
208
+ const DEFAULT_ZCL_REQUEST_TIMEOUT = 15000; //msec
209
+ /** Time for a network-related request to get a response (usually via event). */
210
+ const DEFAULT_NETWORK_REQUEST_TIMEOUT = 10000; // nothing on the network to bother requests, should be much faster than this
211
+ /** Time between watchdog counters reading/clearing */
212
+ const WATCHDOG_COUNTERS_FEED_INTERVAL = 3600000; // every hour...
213
+ /**
214
+ * Relay calls between Z2M and EZSP-layer and handle any error that might occur via queue & waitress.
215
+ *
216
+ * Anything post `start` that requests anything from the EZSP layer must run through the request queue for proper execution flow.
217
+ */
218
+ class EmberAdapter extends __1.Adapter {
219
+ /** Key in STACK_CONFIGS */
220
+ stackConfig;
221
+ /** EMBER_LOW_RAM_CONCENTRATOR or EMBER_HIGH_RAM_CONCENTRATOR. */
222
+ concentratorType;
223
+ ezsp;
224
+ version;
225
+ requestQueue;
226
+ oneWaitress;
227
+ /** Periodically retrieve counters then clear them. */
228
+ watchdogCountersHandle;
229
+ /** Hold ZDO request in process. */
230
+ zdoRequestBuffalo;
231
+ /** Sequence number used for ZDO requests. static uint8_t */
232
+ zdoRequestSequence;
233
+ /** Default radius used for broadcast ZDO requests. uint8_t */
234
+ zdoRequestRadius;
235
+ interpanLock;
236
+ /**
237
+ * Cached network params to avoid NCP calls. Prevents frequent EZSP transactions.
238
+ * NOTE: Do not use directly, use getter functions for it that check if valid or need retrieval from NCP.
239
+ */
240
+ networkCache;
241
+ defaultApsOptions;
242
+ /**
243
+ * Mirrors the NCP multicast table. null === not in use.
244
+ * Index 0 is Green Power and must always remain there.
245
+ */
246
+ multicastTable;
247
+ constructor(networkOptions, serialPortOptions, backupPath, adapterOptions, logger) {
248
+ super(networkOptions, serialPortOptions, backupPath, adapterOptions, logger);
249
+ // TODO config, should be fine like this for now?
250
+ this.stackConfig = socketPortUtils_1.default.isTcpPath(serialPortOptions.path) ? 'zigbeed' : 'default';
251
+ // TODO config
252
+ this.concentratorType = consts_2.EMBER_HIGH_RAM_CONCENTRATOR;
253
+ const delay = (typeof this.adapterOptions.delay === 'number') ? Math.min(Math.max(this.adapterOptions.delay, 5), 60) : 5;
254
+ this.requestQueue = new requestQueue_1.EmberRequestQueue(delay);
255
+ this.oneWaitress = new oneWaitress_1.EmberOneWaitress();
256
+ this.zdoRequestBuffalo = new buffalo_1.EzspBuffalo(Buffer.alloc(consts_1.EZSP_MAX_FRAME_LENGTH));
257
+ this.ezsp = new ezsp_1.Ezsp(delay, serialPortOptions);
258
+ this.ezsp.on(ezsp_1.EzspEvents.STACK_STATUS, this.onStackStatus.bind(this));
259
+ this.ezsp.on(ezsp_1.EzspEvents.MESSAGE_SENT_DELIVERY_FAILED, this.onMessageSentDeliveryFailed.bind(this));
260
+ this.ezsp.on(ezsp_1.EzspEvents.ZDO_RESPONSE, this.onZDOResponse.bind(this));
261
+ this.ezsp.on(ezsp_1.EzspEvents.END_DEVICE_ANNOUNCE, this.onEndDeviceAnnounce.bind(this));
262
+ this.ezsp.on(ezsp_1.EzspEvents.INCOMING_MESSAGE, this.onIncomingMessage.bind(this));
263
+ this.ezsp.on(ezsp_1.EzspEvents.TOUCHLINK_MESSAGE, this.onTouchlinkMessage.bind(this));
264
+ this.ezsp.on(ezsp_1.EzspEvents.GREENPOWER_MESSAGE, this.onGreenpowerMessage.bind(this));
265
+ this.ezsp.on(ezsp_1.EzspEvents.TRUST_CENTER_JOIN, this.onTrustCenterJoin.bind(this));
266
+ }
267
+ /**
268
+ * Emitted from @see Ezsp.ezspStackStatusHandler
269
+ * @param status
270
+ */
271
+ async onStackStatus(status) {
272
+ // to be extra careful, should clear network cache upon receiving this.
273
+ this.clearNetworkCache();
274
+ switch (status) {
275
+ case enums_2.EmberStatus.NETWORK_UP: {
276
+ this.oneWaitress.resolveEvent(OneWaitressEvents.STACK_STATUS_NETWORK_UP);
277
+ console.log(`[STACK STATUS] Network up.`);
278
+ break;
279
+ }
280
+ case enums_2.EmberStatus.NETWORK_DOWN: {
281
+ this.oneWaitress.resolveEvent(OneWaitressEvents.STACK_STATUS_NETWORK_DOWN);
282
+ console.log(`[STACK STATUS] Network down.`);
283
+ break;
284
+ }
285
+ case enums_2.EmberStatus.NETWORK_OPENED: {
286
+ this.oneWaitress.resolveEvent(OneWaitressEvents.STACK_STATUS_NETWORK_OPENED);
287
+ this.requestQueue.enqueue(async () => {
288
+ const setJPstatus = (await this.emberSetJoinPolicy(enums_2.EmberJoinDecision.USE_PRECONFIGURED_KEY));
289
+ if (setJPstatus !== enums_2.EzspStatus.SUCCESS) {
290
+ console.error(`[ZDO] Failed set join policy for with status=${enums_2.EzspStatus[setJPstatus]}.`);
291
+ return enums_2.EmberStatus.ERR_FATAL;
292
+ }
293
+ return enums_2.EmberStatus.SUCCESS;
294
+ }, console.error, // no reject, just log error if any
295
+ true);
296
+ console.log(`[STACK STATUS] Network opened.`);
297
+ break;
298
+ }
299
+ case enums_2.EmberStatus.NETWORK_CLOSED: {
300
+ this.oneWaitress.resolveEvent(OneWaitressEvents.STACK_STATUS_NETWORK_CLOSED);
301
+ console.log(`[STACK STATUS] Network closed.`);
302
+ break;
303
+ }
304
+ default: {
305
+ debug(`[STACK STATUS] ${enums_2.EmberStatus[status]}.`);
306
+ break;
307
+ }
308
+ }
309
+ }
310
+ /**
311
+ * Emitted from @see Ezsp.ezspMessageSentHandler
312
+ * WARNING: Cannot rely on `ezspMessageSentHandler` > `ezspIncomingMessageHandler` order, some devices mix it up!
313
+ *
314
+ * @param type
315
+ * @param indexOrDestination
316
+ * @param apsFrame
317
+ * @param messageTag
318
+ */
319
+ async onMessageSentDeliveryFailed(type, indexOrDestination, apsFrame, messageTag) {
320
+ switch (type) {
321
+ case enums_2.EmberOutgoingMessageType.BROADCAST:
322
+ case enums_2.EmberOutgoingMessageType.BROADCAST_WITH_ALIAS:
323
+ case enums_2.EmberOutgoingMessageType.MULTICAST:
324
+ case enums_2.EmberOutgoingMessageType.MULTICAST_WITH_ALIAS: {
325
+ // BC/MC not checking for message sent, avoid unnecessary waitress lookups
326
+ console.error(`Delivery of ${enums_2.EmberOutgoingMessageType[type]} failed for "${indexOrDestination}" `
327
+ + `[apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag}]`);
328
+ break;
329
+ }
330
+ default: {
331
+ // reject any waitress early (don't wait for timeout if we know we're gonna get there eventually)
332
+ this.oneWaitress.deliveryFailedFor(indexOrDestination, apsFrame);
333
+ break;
334
+ }
335
+ }
336
+ }
337
+ /**
338
+ * Emitted from @see Ezsp.ezspIncomingMessageHandler
339
+ *
340
+ * @param clusterId The ZDO response cluster ID.
341
+ * @param sender The sender of the response. Should match `payload.nodeId` in many responses.
342
+ * @param payload If null, the response indicated a failure.
343
+ */
344
+ async onZDOResponse(status, sender, apsFrame, payload) {
345
+ this.oneWaitress.resolveZDO(status, sender, apsFrame, payload);
346
+ }
347
+ /**
348
+ * Emitted from @see Ezsp.ezspIncomingMessageHandler
349
+ *
350
+ * @param sender
351
+ * @param nodeId
352
+ * @param eui64
353
+ * @param macCapFlags
354
+ */
355
+ async onEndDeviceAnnounce(sender, apsFrame, payload) {
356
+ // reduced function device
357
+ // if ((payload.capabilities.deviceType === 0)) {
358
+ // }
359
+ this.emit(events_1.Events.deviceAnnounce, { networkAddress: payload.nodeId, ieeeAddr: payload.eui64 });
360
+ }
361
+ /**
362
+ * Emitted from @see Ezsp.ezspIncomingMessageHandler
363
+ *
364
+ * @param type
365
+ * @param apsFrame
366
+ * @param lastHopLqi
367
+ * @param sender
368
+ * @param messageContents
369
+ */
370
+ async onIncomingMessage(type, apsFrame, lastHopLqi, sender, messageContents) {
371
+ try {
372
+ const payload = {
373
+ address: sender,
374
+ frame: zcl_1.ZclFrame.fromBuffer(apsFrame.clusterId, messageContents),
375
+ endpoint: apsFrame.sourceEndpoint,
376
+ linkquality: lastHopLqi,
377
+ groupID: apsFrame.groupId,
378
+ wasBroadcast: ((type === enums_2.EmberIncomingMessageType.BROADCAST) || (type === enums_2.EmberIncomingMessageType.BROADCAST_LOOPBACK)),
379
+ destinationEndpoint: apsFrame.destinationEndpoint,
380
+ };
381
+ this.oneWaitress.resolveZCL(payload);
382
+ this.emit(events_1.Events.zclData, payload);
383
+ }
384
+ catch (error) {
385
+ const payload = {
386
+ clusterID: apsFrame.clusterId,
387
+ address: sender,
388
+ data: messageContents,
389
+ endpoint: apsFrame.sourceEndpoint,
390
+ linkquality: lastHopLqi,
391
+ groupID: apsFrame.groupId,
392
+ wasBroadcast: ((type === enums_2.EmberIncomingMessageType.BROADCAST) || (type === enums_2.EmberIncomingMessageType.BROADCAST_LOOPBACK)),
393
+ destinationEndpoint: apsFrame.destinationEndpoint,
394
+ };
395
+ this.emit(events_1.Events.rawData, payload);
396
+ }
397
+ }
398
+ /**
399
+ * Emitted from @see Ezsp.ezspMacFilterMatchMessageHandler when the message is a valid InterPAN touchlink message.
400
+ *
401
+ * @param sourcePanId
402
+ * @param sourceAddress
403
+ * @param groupId
404
+ * @param lastHopLqi
405
+ * @param messageContents
406
+ */
407
+ async onTouchlinkMessage(sourcePanId, sourceAddress, groupId, lastHopLqi, messageContents) {
408
+ const payload = {
409
+ frame: zcl_1.ZclFrame.fromBuffer(cluster_1.default.touchlink.ID, messageContents),
410
+ address: sourceAddress,
411
+ endpoint: 1, // arbitrary since not sent over-the-air
412
+ linkquality: lastHopLqi,
413
+ groupID: groupId,
414
+ wasBroadcast: true, // XXX: since always sent broadcast atm...
415
+ destinationEndpoint: endpoints_1.FIXED_ENDPOINTS[0].endpoint,
416
+ };
417
+ this.oneWaitress.resolveZCL(payload);
418
+ this.emit(events_1.Events.zclData, payload);
419
+ }
420
+ /**
421
+ * Emitted from @see Ezsp.ezspGpepIncomingMessageHandler
422
+ *
423
+ * @param sequenceNumber
424
+ * @param commandIdentifier
425
+ * @param sourceId
426
+ * @param frameCounter
427
+ * @param gpdCommandId
428
+ * @param gpdCommandPayload
429
+ * @param gpdLink
430
+ */
431
+ async onGreenpowerMessage(sequenceNumber, commandIdentifier, sourceId, frameCounter, gpdCommandId, gpdCommandPayload, gpdLink) {
432
+ try {
433
+ const gpdHeader = Buffer.alloc(15);
434
+ gpdHeader.writeUInt8(0b00000001, 0); // frameControl: FrameType.SPECIFIC + Direction.CLIENT_TO_SERVER + disableDefaultResponse=false
435
+ gpdHeader.writeUInt8(sequenceNumber, 1); // transactionSequenceNumber
436
+ gpdHeader.writeUInt8(commandIdentifier, 2); // commandIdentifier
437
+ gpdHeader.writeUInt16LE(0, 3); // options XXX: bypassed, same as deconz https://github.com/Koenkk/zigbee-herdsman/pull/536
438
+ gpdHeader.writeUInt32LE(sourceId, 5); // srcID
439
+ // omitted: gpdIEEEAddr ieeeAddr
440
+ // omitted: gpdEndpoint uint8
441
+ gpdHeader.writeUInt32LE(frameCounter, 9); // frameCounter
442
+ gpdHeader.writeUInt8(gpdCommandId, 13); // commandID
443
+ gpdHeader.writeUInt8(gpdCommandPayload.length, 14); // payloadSize
444
+ const gpFrame = zcl_1.ZclFrame.fromBuffer(cluster_1.default.greenPower.ID, Buffer.concat([gpdHeader, gpdCommandPayload]));
445
+ const payload = {
446
+ frame: gpFrame,
447
+ address: sourceId,
448
+ endpoint: consts_2.GP_ENDPOINT,
449
+ linkquality: gpdLink,
450
+ groupID: this.greenPowerGroup,
451
+ // XXX: upstream sends to `gppNwkAddr` if `wasBroadcast` is false, even if `gppNwkAddr` is null
452
+ wasBroadcast: (gpFrame.Payload.gppNwkAddr != null) ? false : true,
453
+ destinationEndpoint: consts_2.GP_ENDPOINT,
454
+ };
455
+ this.oneWaitress.resolveZCL(payload);
456
+ this.emit(events_1.Events.zclData, payload);
457
+ }
458
+ catch (err) {
459
+ console.error(`<~x~ [GP] Failed creating ZCL payload. Skipping. ${err}`);
460
+ return;
461
+ }
462
+ }
463
+ /**
464
+ * Emitted from @see Ezsp.ezspTrustCenterJoinHandler
465
+ *
466
+ * @param newNodeId
467
+ * @param newNodeEui64
468
+ * @param status
469
+ * @param policyDecision
470
+ * @param parentOfNewNodeId
471
+ */
472
+ async onTrustCenterJoin(newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId) {
473
+ if (status === enums_2.EmberDeviceUpdate.DEVICE_LEFT) {
474
+ // NOTE: `policyDecision` here is NO_ACTION and `parentOfNewNodeId` is 65535
475
+ const payload = {
476
+ networkAddress: newNodeId,
477
+ ieeeAddr: newNodeEui64,
478
+ };
479
+ this.emit(events_1.Events.deviceLeave, payload);
480
+ }
481
+ else {
482
+ if (policyDecision !== enums_2.EmberJoinDecision.DENY_JOIN) {
483
+ const payload = {
484
+ networkAddress: newNodeId,
485
+ ieeeAddr: newNodeEui64,
486
+ };
487
+ this.emit(events_1.Events.deviceJoined, payload);
488
+ }
489
+ else {
490
+ console.log(`[TRUST CENTER] Device ${newNodeId}:${newNodeEui64} was denied joining via ${parentOfNewNodeId}.`);
491
+ }
492
+ }
493
+ }
494
+ async watchdogCounters() {
495
+ this.requestQueue.enqueue(async () => {
496
+ // listed as per EmberCounterType
497
+ const counters = (await this.ezsp.ezspReadAndClearCounters());
498
+ let countersLogString = "[NCP COUNTERS] ";
499
+ for (let i = 0; i < enums_2.EmberCounterType.COUNT; i++) {
500
+ countersLogString += `${enums_2.EmberCounterType[i]}: ${counters[i]} | `;
501
+ }
502
+ console.log(countersLogString);
503
+ return enums_2.EmberStatus.SUCCESS;
504
+ }, console.error);
505
+ }
506
+ initVariables() {
507
+ this.ezsp.removeAllListeners(ezsp_1.EzspEvents.ncpNeedsResetAndInit);
508
+ clearInterval(this.watchdogCountersHandle);
509
+ this.zdoRequestBuffalo.setPosition(0);
510
+ this.zdoRequestSequence = 0; // start at 1
511
+ this.zdoRequestRadius = 255;
512
+ this.interpanLock = false;
513
+ this.networkCache = (0, initters_1.initNetworkCache)();
514
+ this.defaultApsOptions = (enums_2.EmberApsOption.RETRY | enums_2.EmberApsOption.ENABLE_ROUTE_DISCOVERY | enums_2.EmberApsOption.ENABLE_ADDRESS_DISCOVERY);
515
+ // always at least length==1 because of allowed MULTICAST_TABLE_SIZE range
516
+ this.multicastTable = new Array(STACK_CONFIGS[this.stackConfig].MULTICAST_TABLE_SIZE).fill(null);
517
+ this.ezsp.once(ezsp_1.EzspEvents.ncpNeedsResetAndInit, this.onNcpNeedsResetAndInit.bind(this));
518
+ }
519
+ /**
520
+ * Proceed to execute the long list of commands required to setup comms between Host<>NCP.
521
+ * This is called by start and on internal reset.
522
+ */
523
+ async initEzsp() {
524
+ let result = "resumed";
525
+ await this.onNCPPreReset();
526
+ try {
527
+ // NOTE: something deep in this call can throw too
528
+ const result = (await this.ezsp.start());
529
+ if (result !== enums_2.EzspStatus.SUCCESS) {
530
+ throw new Error(`Failed to start EZSP layer with status=${enums_2.EzspStatus[result]}.`);
531
+ }
532
+ }
533
+ catch (err) {
534
+ throw err;
535
+ }
536
+ // call before any other command, else fails
537
+ await this.emberVersion();
538
+ await this.initNCPPreConfiguration();
539
+ await this.initNCPAddressTable();
540
+ await this.initNCPConfiguration();
541
+ // WARNING: From here on EZSP commands that affect memory allocation on the NCP should no longer be called (like resizing tables)
542
+ await this.onNCPPostReset();
543
+ await this.registerFixedEndpoints();
544
+ this.clearNetworkCache();
545
+ result = (await this.initTrustCenter());
546
+ // after network UP, as per SDK, ensures clean slate
547
+ await this.initNCPConcentrator();
548
+ // await (this.emberStartEnergyScan());// TODO: via config of some kind, better off waiting for UI supports though
549
+ // populate network cache info
550
+ const [status, , parameters] = (await this.ezsp.ezspGetNetworkParameters());
551
+ if (status !== enums_2.EmberStatus.SUCCESS) {
552
+ throw new Error(`Failed to get network parameters with status=${enums_2.EmberStatus[status]}.`);
553
+ }
554
+ this.networkCache.parameters = parameters;
555
+ this.networkCache.status = (await this.ezsp.ezspNetworkState());
556
+ this.networkCache.eui64 = (await this.ezsp.ezspGetEui64());
557
+ debug(`[INIT] Network Ready! ${JSON.stringify(this.networkCache)}`);
558
+ return result;
559
+ }
560
+ /**
561
+ * NCP Config init. Should always be called first in the init stack (after version cmd).
562
+ * @returns
563
+ */
564
+ async initNCPPreConfiguration() {
565
+ // this can only decrease, not increase, NCP-side value
566
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.ADDRESS_TABLE_SIZE, STACK_CONFIGS[this.stackConfig].ADDRESS_TABLE_SIZE);
567
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.TRUST_CENTER_ADDRESS_CACHE_SIZE, STACK_CONFIGS[this.stackConfig].TRUST_CENTER_ADDRESS_CACHE_SIZE);
568
+ if (STACK_CONFIGS[this.stackConfig].STACK_PROFILE === consts_2.STACK_PROFILE_ZIGBEE_PRO) {
569
+ // BUG 14222: If stack profile is 2 (ZigBee Pro), we need to enforce
570
+ // the standard stack configuration values for that feature set.
571
+ /** MAC indirect timeout should be 7.68 secs */
572
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.INDIRECT_TRANSMISSION_TIMEOUT, 7680);
573
+ /** Max hops should be 2 * nwkMaxDepth, where nwkMaxDepth is 15 */
574
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.MAX_HOPS, 30);
575
+ }
576
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.TX_POWER_MODE, STACK_CONFIGS[this.stackConfig].TX_POWER_MODE);
577
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.SUPPORTED_NETWORKS, STACK_CONFIGS[this.stackConfig].SUPPORTED_NETWORKS);
578
+ await this.emberSetEzspValue(enums_1.EzspValueId.END_DEVICE_KEEP_ALIVE_SUPPORT_MODE, 1, [STACK_CONFIGS[this.stackConfig].END_DEVICE_KEEP_ALIVE_SUPPORT_MODE]);
579
+ // allow other devices to modify the binding table
580
+ await this.emberSetEzspPolicy(enums_1.EzspPolicyId.BINDING_MODIFICATION_POLICY, enums_1.EzspDecisionId.CHECK_BINDING_MODIFICATIONS_ARE_VALID_ENDPOINT_CLUSTERS);
581
+ // return message tag and message contents in ezspMessageSentHandler()
582
+ await this.emberSetEzspPolicy(enums_1.EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY, enums_1.EzspDecisionId.MESSAGE_TAG_AND_CONTENTS_IN_CALLBACK);
583
+ await this.emberSetEzspValue(enums_1.EzspValueId.MAXIMUM_INCOMING_TRANSFER_SIZE, 2, (0, math_1.lowHighBytes)(STACK_CONFIGS[this.stackConfig].MAXIMUM_INCOMING_TRANSFER_SIZE));
584
+ await this.emberSetEzspValue(enums_1.EzspValueId.MAXIMUM_OUTGOING_TRANSFER_SIZE, 2, (0, math_1.lowHighBytes)(STACK_CONFIGS[this.stackConfig].MAXIMUM_OUTGOING_TRANSFER_SIZE));
585
+ await this.emberSetEzspValue(enums_1.EzspValueId.TRANSIENT_DEVICE_TIMEOUT, 2, (0, math_1.lowHighBytes)(STACK_CONFIGS[this.stackConfig].TRANSIENT_DEVICE_TIMEOUT));
586
+ // Set the manufacturing code. This is defined by ZigBee document 053874r10
587
+ // Ember's ID is 0x1002 and is the default, but this can be overridden in App Builder.
588
+ await this.ezsp.ezspSetManufacturerCode(consts_2.MANUFACTURER_CODE);
589
+ // network security init
590
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.STACK_PROFILE, STACK_CONFIGS[this.stackConfig].STACK_PROFILE);
591
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.SECURITY_LEVEL, STACK_CONFIGS[this.stackConfig].SECURITY_LEVEL);
592
+ }
593
+ /**
594
+ * NCP Address table init.
595
+ * @returns
596
+ */
597
+ async initNCPAddressTable() {
598
+ const desiredTableSize = STACK_CONFIGS[this.stackConfig].ADDRESS_TABLE_SIZE;
599
+ // If the host and the ncp disagree on the address table size, explode.
600
+ const [status, addressTableSize] = (await this.ezsp.ezspGetConfigurationValue(enums_1.EzspConfigId.ADDRESS_TABLE_SIZE));
601
+ // After the change of ncp memory model in UC, we can not increase the default NCP table sizes anymore.
602
+ // Therefore, checking for desiredTableSize == (ncp)addressTableSize might not be always true anymore
603
+ // assert(desiredTableSize <= addressTableSize);
604
+ if ((status !== enums_2.EzspStatus.SUCCESS) || (addressTableSize > desiredTableSize)) {
605
+ throw new Error(`[INIT] NCP (${addressTableSize}) disagrees with Host (min ${desiredTableSize}) on table size. status=${enums_2.EzspStatus[status]}`);
606
+ }
607
+ }
608
+ /**
609
+ * NCP configuration init
610
+ */
611
+ async initNCPConfiguration() {
612
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.BINDING_TABLE_SIZE, STACK_CONFIGS[this.stackConfig].BINDING_TABLE_SIZE);
613
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.KEY_TABLE_SIZE, STACK_CONFIGS[this.stackConfig].KEY_TABLE_SIZE);
614
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.MAX_END_DEVICE_CHILDREN, STACK_CONFIGS[this.stackConfig].MAX_END_DEVICE_CHILDREN);
615
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.APS_UNICAST_MESSAGE_COUNT, STACK_CONFIGS[this.stackConfig].APS_UNICAST_MESSAGE_COUNT);
616
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.BROADCAST_TABLE_SIZE, STACK_CONFIGS[this.stackConfig].BROADCAST_TABLE_SIZE);
617
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.NEIGHBOR_TABLE_SIZE, STACK_CONFIGS[this.stackConfig].NEIGHBOR_TABLE_SIZE);
618
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.END_DEVICE_POLL_TIMEOUT, STACK_CONFIGS[this.stackConfig].END_DEVICE_POLL_TIMEOUT);
619
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.TRANSIENT_KEY_TIMEOUT_S, STACK_CONFIGS[this.stackConfig].TRANSIENT_KEY_TIMEOUT_S);
620
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.RETRY_QUEUE_SIZE, STACK_CONFIGS[this.stackConfig].RETRY_QUEUE_SIZE);
621
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.SOURCE_ROUTE_TABLE_SIZE, STACK_CONFIGS[this.stackConfig].SOURCE_ROUTE_TABLE_SIZE);
622
+ await this.emberSetEzspConfigValue(enums_1.EzspConfigId.MULTICAST_TABLE_SIZE, STACK_CONFIGS[this.stackConfig].MULTICAST_TABLE_SIZE);
623
+ }
624
+ /**
625
+ * NCP concentrator init. Also enables source route discovery mode with RESCHEDULE.
626
+ *
627
+ * From AN1233:
628
+ * To function correctly in a Zigbee PRO network, a trust center also requires that:
629
+ *
630
+ * 1. The trust center application must act as a concentrator (either high or low RAM).
631
+ * 2. The trust center application must have support for source routing.
632
+ * It must record the source routes and properly handle requests by the stack for a particular source route.
633
+ * 3. The trust center application must use an address cache for security, in order to maintain a mapping of IEEE address to short ID.
634
+ *
635
+ * Failure to satisfy all of the above requirements may result in failures when joining/rejoining devices to the network across multiple hops
636
+ * (through a target node that is neither the trust center nor one of its neighboring routers.)
637
+ */
638
+ async initNCPConcentrator() {
639
+ const config = (this.concentratorType === consts_2.EMBER_HIGH_RAM_CONCENTRATOR) ? HIGH_RAM_CONCENTRATOR_CONFIG : LOW_RAM_CONCENTRATOR_CONFIG;
640
+ const status = (await this.ezsp.ezspSetConcentrator(true, this.concentratorType, config.minTime, config.maxTime, config.routeErrorThreshold, config.deliveryFailureThreshold, config.mapHops));
641
+ if (status !== enums_2.EmberStatus.SUCCESS) {
642
+ throw new Error(`[CONCENTRATOR] Failed to set concentrator with status=${status}.`);
643
+ }
644
+ const remainTilMTORR = (await this.ezsp.ezspSetSourceRouteDiscoveryMode(enums_2.EmberSourceRouteDiscoveryMode.RESCHEDULE));
645
+ console.log(`[CONCENTRATOR] Started source route discovery. ${remainTilMTORR}ms until next broadcast.`);
646
+ }
647
+ /**
648
+ * Register fixed endpoints and set any related multicast entries that need to be.
649
+ */
650
+ async registerFixedEndpoints() {
651
+ for (const ep of endpoints_1.FIXED_ENDPOINTS) {
652
+ if (ep.networkIndex !== 0x00) {
653
+ debug(`Multi-network not currently supported. Skipping endpoint ${JSON.stringify(ep)}.`);
654
+ continue;
655
+ }
656
+ const [epStatus,] = (await this.ezsp.ezspGetEndpointFlags(ep.endpoint));
657
+ // endpoint not already registered
658
+ if (epStatus !== enums_2.EzspStatus.SUCCESS) {
659
+ // check to see if ezspAddEndpoint needs to be called
660
+ // if ezspInit is called without NCP reset, ezspAddEndpoint is not necessary and will return an error
661
+ const status = (await this.ezsp.ezspAddEndpoint(ep.endpoint, ep.profileId, ep.deviceId, ep.deviceVersion, ep.inClusterList, ep.outClusterList));
662
+ if (status === enums_2.EzspStatus.SUCCESS) {
663
+ debug(`Registered endpoint "${ep.endpoint}" with status=${enums_2.EzspStatus[status]}.`);
664
+ }
665
+ else {
666
+ throw new Error(`Failed to register endpoint "${ep.endpoint}" with status=${enums_2.EzspStatus[status]}.`);
667
+ }
668
+ }
669
+ else {
670
+ debug(`Endpoint "${ep.endpoint}" already registered.`);
671
+ }
672
+ if (ep.endpoint === consts_2.GP_ENDPOINT) {
673
+ const gpMulticastEntry = {
674
+ multicastId: this.greenPowerGroup,
675
+ endpoint: ep.endpoint,
676
+ networkIndex: ep.networkIndex,
677
+ };
678
+ const status = (await this.ezsp.ezspSetMulticastTableEntry(0, gpMulticastEntry));
679
+ if (status !== enums_2.EmberStatus.SUCCESS) {
680
+ throw new Error(`Failed to register group "Green Power" in multicast table with status=${enums_2.EmberStatus[status]}.`);
681
+ }
682
+ // NOTE: ensure GP is always added first in the table
683
+ this.multicastTable[0] = gpMulticastEntry;
684
+ debug(`Registered multicast table entry: ${JSON.stringify(gpMulticastEntry)}.`);
685
+ }
686
+ }
687
+ }
688
+ /**
689
+ *
690
+ * @returns True if the network needed to be formed.
691
+ */
692
+ async initTrustCenter() {
693
+ // init TC policies
694
+ {
695
+ let status = (await this.emberSetEzspPolicy(enums_1.EzspPolicyId.TC_KEY_REQUEST_POLICY, enums_1.EzspDecisionId.ALLOW_TC_KEY_REQUESTS_AND_SEND_CURRENT_KEY));
696
+ if (status !== enums_2.EzspStatus.SUCCESS) {
697
+ throw new Error(`[INIT TC] Failed to set EzspPolicyId TC_KEY_REQUEST_POLICY to ALLOW_TC_KEY_REQUESTS_AND_SEND_CURRENT_KEY `
698
+ + `with status=${enums_2.EzspStatus[status]}.`);
699
+ }
700
+ const appKeyPolicy = STACK_CONFIGS[this.stackConfig].KEY_TABLE_SIZE
701
+ ? enums_1.EzspDecisionId.ALLOW_APP_KEY_REQUESTS : enums_1.EzspDecisionId.DENY_APP_KEY_REQUESTS;
702
+ status = (await this.emberSetEzspPolicy(enums_1.EzspPolicyId.APP_KEY_REQUEST_POLICY, appKeyPolicy));
703
+ if (status !== enums_2.EzspStatus.SUCCESS) {
704
+ throw new Error(`[INIT TC] Failed to set EzspPolicyId APP_KEY_REQUEST_POLICY to ${enums_1.EzspDecisionId[appKeyPolicy]} `
705
+ + `with status=${enums_2.EzspStatus[status]}.`);
706
+ }
707
+ status = (await this.emberSetJoinPolicy(enums_2.EmberJoinDecision.USE_PRECONFIGURED_KEY));
708
+ if (status !== enums_2.EzspStatus.SUCCESS) {
709
+ throw new Error(`[INIT TC] Failed to set join policy to USE_PRECONFIGURED_KEY with status=${enums_2.EzspStatus[status]}.`);
710
+ }
711
+ }
712
+ const configNetworkKey = Buffer.from(this.networkOptions.networkKey);
713
+ const networkInitStruct = {
714
+ bitmask: (enums_2.EmberNetworkInitBitmask.PARENT_INFO_IN_TOKEN | enums_2.EmberNetworkInitBitmask.END_DEVICE_REJOIN_ON_REBOOT)
715
+ };
716
+ const initStatus = (await this.ezsp.ezspNetworkInit(networkInitStruct));
717
+ debug(`[INIT TC] Network init status=${enums_2.EmberStatus[initStatus]}.`);
718
+ if ((initStatus !== enums_2.EmberStatus.SUCCESS) && (initStatus !== enums_2.EmberStatus.NOT_JOINED)) {
719
+ throw new Error(`[INIT TC] Failed network init request with status=${enums_2.EmberStatus[initStatus]}.`);
720
+ }
721
+ let action = NetworkInitAction.DONE;
722
+ if (initStatus === enums_2.EmberStatus.SUCCESS) {
723
+ // network
724
+ await this.oneWaitress.startWaitingForEvent({ eventName: OneWaitressEvents.STACK_STATUS_NETWORK_UP }, DEFAULT_NETWORK_REQUEST_TIMEOUT, '[INIT TC] Network init');
725
+ const [npStatus, nodeType, netParams] = (await this.ezsp.ezspGetNetworkParameters());
726
+ debug(`[INIT TC] Current network config=${JSON.stringify(this.networkOptions)}`);
727
+ debug(`[INIT TC] Current NCP network: nodeType=${enums_2.EmberNodeType[nodeType]} params=${JSON.stringify(netParams)}`);
728
+ // XXX: should not force a form when it's only a channel change, just change the channel, wait a sec, then continue the logic
729
+ if ((npStatus === enums_2.EmberStatus.SUCCESS) && (nodeType === enums_2.EmberNodeType.COORDINATOR) && (this.networkOptions.panID === netParams.panId)
730
+ && ((0, es6_1.default)(this.networkOptions.extendedPanID, netParams.extendedPanId))
731
+ && (this.networkOptions.channelList.includes(netParams.radioChannel))) {
732
+ // config matches adapter so far, no error, we can check the network key
733
+ const context = (0, initters_1.initSecurityManagerContext)();
734
+ context.coreKeyType = enums_2.SecManKeyType.NETWORK;
735
+ context.keyIndex = 0;
736
+ const [networkKey, nkStatus] = (await this.ezsp.ezspExportKey(context));
737
+ if (nkStatus !== enums_2.SLStatus.OK) {
738
+ throw new Error(`[BACKUP] Failed to export Network Key with status=${enums_2.SLStatus[nkStatus]}.`);
739
+ }
740
+ debug(`[INIT TC] Current NCP network: networkKey=${networkKey.contents.toString('hex')}`);
741
+ // config doesn't match adapter anymore
742
+ if (!networkKey.contents.equals(configNetworkKey)) {
743
+ action = NetworkInitAction.LEAVE;
744
+ }
745
+ }
746
+ else {
747
+ // config doesn't match adapter
748
+ action = NetworkInitAction.LEAVE;
749
+ }
750
+ if (action === NetworkInitAction.LEAVE) {
751
+ console.log(`[INIT TC] NCP network does not match config. Leaving network...`);
752
+ const leaveStatus = (await this.ezsp.ezspLeaveNetwork());
753
+ if (leaveStatus !== enums_2.EmberStatus.SUCCESS) {
754
+ throw new Error(`[INIT TC] Failed leave network request with status=${enums_2.EmberStatus[leaveStatus]}.`);
755
+ }
756
+ await this.oneWaitress.startWaitingForEvent({ eventName: OneWaitressEvents.STACK_STATUS_NETWORK_DOWN }, DEFAULT_NETWORK_REQUEST_TIMEOUT, '[INIT TC] Leave network');
757
+ await (0, utils_1.Wait)(200); // settle down
758
+ action = NetworkInitAction.LEFT;
759
+ }
760
+ }
761
+ const backup = (await this.getStoredBackup());
762
+ if ((initStatus === enums_2.EmberStatus.NOT_JOINED) || (action === NetworkInitAction.LEFT)) {
763
+ // no network
764
+ if (backup != null) {
765
+ if ((this.networkOptions.panID === backup.networkOptions.panId)
766
+ && (Buffer.from(this.networkOptions.extendedPanID).equals(backup.networkOptions.extendedPanId))
767
+ && (this.networkOptions.channelList.includes(backup.logicalChannel))
768
+ && (configNetworkKey.equals(backup.networkOptions.networkKey))) {
769
+ // config matches backup
770
+ action = NetworkInitAction.FORM_BACKUP;
771
+ }
772
+ else {
773
+ // config doesn't match backup
774
+ console.log(`[INIT TC] Config does not match backup.`);
775
+ action = NetworkInitAction.FORM_CONFIG;
776
+ }
777
+ }
778
+ else {
779
+ // no backup
780
+ console.log(`[INIT TC] No valid backup found.`);
781
+ action = NetworkInitAction.FORM_CONFIG;
782
+ }
783
+ }
784
+ else {
785
+ action = NetworkInitAction.DONE; // just to be clear
786
+ }
787
+ //---- from here on, we assume everything is in place for whatever decision was taken above
788
+ let result = 'resumed';
789
+ switch (action) {
790
+ case NetworkInitAction.FORM_BACKUP: {
791
+ console.log(`[INIT TC] Forming from backup.`);
792
+ const keyList = backup.devices.map((device) => {
793
+ const octets = Array.from(device.ieeeAddress.reverse());
794
+ const deviceEui64 = '0x' + octets.map(octet => octet.toString(16).padStart(2, '0')).join("");
795
+ const key = {
796
+ deviceEui64,
797
+ key: { contents: device.linkKey.key },
798
+ outgoingFrameCounter: device.linkKey.txCounter,
799
+ incomingFrameCounter: device.linkKey.rxCounter,
800
+ };
801
+ return key;
802
+ });
803
+ // before forming
804
+ await this.importLinkKeys(keyList);
805
+ await this.formNetwork(true, /*from backup*/ backup.networkOptions.networkKey, backup.networkKeyInfo.sequenceNumber, backup.networkOptions.panId, Array.from(backup.networkOptions.extendedPanId), backup.logicalChannel, backup.ezsp.hashed_tclk);
806
+ result = 'restored';
807
+ break;
808
+ }
809
+ case NetworkInitAction.FORM_CONFIG: {
810
+ console.log(`[INIT TC] Forming from config.`);
811
+ await this.formNetwork(false, /*from config*/ configNetworkKey, 0, this.networkOptions.panID, this.networkOptions.extendedPanID, this.networkOptions.channelList[0], (0, crypto_1.randomBytes)(consts_1.EMBER_ENCRYPTION_KEY_SIZE));
812
+ result = 'reset';
813
+ break;
814
+ }
815
+ case NetworkInitAction.DONE: {
816
+ console.log(`[INIT TC] NCP network matches config.`);
817
+ break;
818
+ }
819
+ default: {
820
+ throw new Error(`[INIT TC] Invalid action "${NetworkInitAction[action]}" for final stage.`);
821
+ }
822
+ }
823
+ // can't let frame counter wrap to zero (uint32_t), will force a broadcast after init if getting too close
824
+ if (backup != null && (backup.networkKeyInfo.frameCounter > 0xFEEEEEEE)) {
825
+ // XXX: while this remains a pretty low occurrence in most (small) networks,
826
+ // currently Z2M won't support the key update because of one-way config...
827
+ // need to investigate handling this properly
828
+ // console.warn(`[INIT TC] Network key frame counter is reaching its limit. Scheduling broadcast to update network key. `
829
+ // + `This may result in some devices (especially battery-powered) temporarily losing connection.`);
830
+ // // XXX: no idea here on the proper timer value, but this will block the network for several seconds on exec
831
+ // // (probably have to take the behavior of sleepy-end devices into account to improve chances of reaching everyone right away?)
832
+ // setTimeout(async () => {
833
+ // this.requestQueue.enqueue(async (): Promise<EmberStatus> => {
834
+ // await this.broadcastNetworkKeyUpdate();
835
+ // return EmberStatus.SUCCESS;
836
+ // }, console.error, true);// no reject just log error if any, will retry next start, & prioritize so we know it'll run when expected
837
+ // }, 300000);
838
+ console.warn(`[INIT TC] Network key frame counter is reaching its limit. A new network key will have to be instaured soon.`);
839
+ }
840
+ return result;
841
+ }
842
+ /**
843
+ * Form a network using given parameters.
844
+ */
845
+ async formNetwork(fromBackup, networkKey, networkKeySequenceNumber, panId, extendedPanId, radioChannel, tcLinkKey) {
846
+ const state = {
847
+ bitmask: (enums_2.EmberInitialSecurityBitmask.TRUST_CENTER_GLOBAL_LINK_KEY | enums_2.EmberInitialSecurityBitmask.HAVE_PRECONFIGURED_KEY
848
+ | enums_2.EmberInitialSecurityBitmask.HAVE_NETWORK_KEY | enums_2.EmberInitialSecurityBitmask.TRUST_CENTER_USES_HASHED_LINK_KEY
849
+ | enums_2.EmberInitialSecurityBitmask.REQUIRE_ENCRYPTED_KEY),
850
+ preconfiguredKey: { contents: tcLinkKey },
851
+ networkKey: { contents: networkKey },
852
+ networkKeySequenceNumber: networkKeySequenceNumber,
853
+ preconfiguredTrustCenterEui64: consts_2.BLANK_EUI64,
854
+ };
855
+ if (fromBackup) {
856
+ state.bitmask |= enums_2.EmberInitialSecurityBitmask.NO_FRAME_COUNTER_RESET;
857
+ }
858
+ let emberStatus = (await this.ezsp.ezspSetInitialSecurityState(state));
859
+ if (emberStatus !== enums_2.EmberStatus.SUCCESS) {
860
+ throw new Error(`[INIT FORM] Failed to set initial security state with status=${enums_2.EmberStatus[emberStatus]}.`);
861
+ }
862
+ const extended = (enums_2.EmberExtendedSecurityBitmask.JOINER_GLOBAL_LINK_KEY | enums_2.EmberExtendedSecurityBitmask.NWK_LEAVE_REQUEST_NOT_ALLOWED);
863
+ const extSecStatus = (await this.ezsp.ezspSetExtendedSecurityBitmask(extended));
864
+ if (extSecStatus !== enums_2.EzspStatus.SUCCESS) {
865
+ throw new Error(`[INIT FORM] Failed to set extended security bitmask to ${extended} with status=${enums_2.EzspStatus[extSecStatus]}.`);
866
+ }
867
+ if (!fromBackup && STACK_CONFIGS[this.stackConfig].KEY_TABLE_SIZE) {
868
+ emberStatus = await this.ezsp.ezspClearKeyTable();
869
+ if (emberStatus !== enums_2.EmberStatus.SUCCESS) {
870
+ throw new Error(`[INIT FORM] Failed to clear key table with status=${enums_2.EmberStatus[emberStatus]}.`);
871
+ }
872
+ }
873
+ const netParams = {
874
+ panId,
875
+ extendedPanId,
876
+ radioTxPower: 5,
877
+ radioChannel,
878
+ joinMethod: enums_2.EmberJoinMethod.MAC_ASSOCIATION,
879
+ nwkManagerId: consts_2.ZIGBEE_COORDINATOR_ADDRESS,
880
+ nwkUpdateId: 0,
881
+ channels: consts_2.EMBER_ALL_802_15_4_CHANNELS_MASK,
882
+ };
883
+ console.log(`[INIT FORM] Forming new network with: ${JSON.stringify(netParams)}`);
884
+ emberStatus = (await this.ezsp.ezspFormNetwork(netParams));
885
+ if (emberStatus !== enums_2.EmberStatus.SUCCESS) {
886
+ throw new Error(`[INIT FORM] Failed form network request with status=${enums_2.EmberStatus[emberStatus]}.`);
887
+ }
888
+ await this.oneWaitress.startWaitingForEvent({ eventName: OneWaitressEvents.STACK_STATUS_NETWORK_UP }, DEFAULT_NETWORK_REQUEST_TIMEOUT, '[INIT FORM] Form network');
889
+ const stStatus = await this.ezsp.ezspStartWritingStackTokens();
890
+ debug(`[INIT FORM] Start writing stack tokens status=${enums_2.EzspStatus[stStatus]}.`);
891
+ console.log(`[INIT FORM] New network formed!`);
892
+ }
893
+ /**
894
+ * Loads currently stored backup and returns it in internal backup model.
895
+ */
896
+ async getStoredBackup() {
897
+ try {
898
+ await mz_1.fs.access(this.backupPath);
899
+ }
900
+ catch (error) {
901
+ return null;
902
+ }
903
+ let data;
904
+ try {
905
+ data = JSON.parse((await mz_1.fs.readFile(this.backupPath)).toString());
906
+ }
907
+ catch (error) {
908
+ throw new Error(`[BACKUP] Coordinator backup is corrupted.`);
909
+ }
910
+ if (data.metadata?.format === "zigpy/open-coordinator-backup" && data.metadata?.version) {
911
+ if (data.metadata?.version !== 1) {
912
+ throw new Error(`[BACKUP] Unsupported open coordinator backup version (version=${data.metadata?.version}).`);
913
+ }
914
+ if (!data.stack_specific?.ezsp || !data.metadata.internal.ezspVersion) {
915
+ throw new Error(`[BACKUP] Specified backup is not EZSP.`);
916
+ }
917
+ if (data.metadata.internal.ezspVersion < BACKUP_OLDEST_SUPPORTED_EZSP_VERSION) {
918
+ throw new Error(`[BACKUP] Specified backup is not a supported EZSP version (min: ${BACKUP_OLDEST_SUPPORTED_EZSP_VERSION}).`);
919
+ }
920
+ return utils_1.BackupUtils.fromUnifiedBackup(data);
921
+ }
922
+ else {
923
+ throw new Error(`[BACKUP] Unknown backup format.`);
924
+ }
925
+ }
926
+ /**
927
+ * Export link keys for backup.
928
+ *
929
+ * @return List of keys data with AES hashed keys
930
+ */
931
+ async exportLinkKeys() {
932
+ const [confStatus, keyTableSize] = (await this.ezsp.ezspGetConfigurationValue(enums_1.EzspConfigId.KEY_TABLE_SIZE));
933
+ if (confStatus !== enums_2.EzspStatus.SUCCESS) {
934
+ throw new Error(`[BACKUP] Failed to retrieve key table size from NCP with status=${enums_2.EzspStatus[confStatus]}.`);
935
+ }
936
+ let deviceEui64;
937
+ let plaintextKey;
938
+ let apsKeyMeta;
939
+ let status;
940
+ const keyList = [];
941
+ for (let i = 0; i < keyTableSize; i++) {
942
+ [deviceEui64, plaintextKey, apsKeyMeta, status] = (await this.ezsp.ezspExportLinkKeyByIndex(i));
943
+ debug(`[BACKUP] Export link key at index ${i}, status=${enums_2.SLStatus[status]}.`);
944
+ // only include key if we could retrieve one at index and hash it properly
945
+ if (status === enums_2.SLStatus.OK) {
946
+ // Rather than give the real link key, the backup contains a hashed version of the key.
947
+ // This is done to prevent a compromise of the backup data from compromising the current link keys.
948
+ // This is per the Smart Energy spec.
949
+ const [hashStatus, hashedKey] = (await this.emberAesHashSimple(plaintextKey.contents));
950
+ if (hashStatus === enums_2.EmberStatus.SUCCESS) {
951
+ keyList.push({
952
+ deviceEui64,
953
+ key: { contents: hashedKey },
954
+ outgoingFrameCounter: apsKeyMeta.outgoingFrameCounter,
955
+ incomingFrameCounter: apsKeyMeta.incomingFrameCounter,
956
+ });
957
+ }
958
+ else {
959
+ // this should never happen?
960
+ console.error(`[BACKUP] Failed to hash link key at index ${i} with status=${enums_2.EmberStatus[hashStatus]}. Omitting from backup.`);
961
+ }
962
+ }
963
+ }
964
+ console.log(`[BACKUP] Retrieved ${keyList.length} link keys.`);
965
+ return keyList;
966
+ }
967
+ /**
968
+ * Import link keys from backup.
969
+ *
970
+ * @param backupData
971
+ */
972
+ async importLinkKeys(backupData) {
973
+ if (!backupData?.length) {
974
+ return;
975
+ }
976
+ const [confStatus, keyTableSize] = (await this.ezsp.ezspGetConfigurationValue(enums_1.EzspConfigId.KEY_TABLE_SIZE));
977
+ if (confStatus !== enums_2.EzspStatus.SUCCESS) {
978
+ throw new Error(`[BACKUP] Failed to retrieve key table size from NCP with status=${enums_2.EzspStatus[confStatus]}.`);
979
+ }
980
+ if (backupData.length > keyTableSize) {
981
+ throw new Error(`[BACKUP] Current key table of ${keyTableSize} is too small to import backup of ${backupData.length}!`);
982
+ }
983
+ const networkStatus = (await this.emberNetworkState());
984
+ if (networkStatus !== enums_2.EmberNetworkStatus.NO_NETWORK) {
985
+ throw new Error(`[BACKUP] Cannot import TC data while network is up, networkStatus=${enums_2.EmberNetworkStatus[networkStatus]}.`);
986
+ }
987
+ let status;
988
+ for (let i = 0; i < keyTableSize; i++) {
989
+ if (i >= backupData.length) {
990
+ // erase any key index not present in backup but available on the NCP
991
+ status = (await this.ezsp.ezspEraseKeyTableEntry(i));
992
+ }
993
+ else {
994
+ const importStatus = (await this.ezsp.ezspImportLinkKey(i, backupData[i].deviceEui64, backupData[i].key));
995
+ status = ((importStatus === enums_2.SLStatus.OK) ? enums_2.EmberStatus.SUCCESS : enums_2.EmberStatus.KEY_TABLE_INVALID_ADDRESS);
996
+ }
997
+ if (status !== enums_2.EmberStatus.SUCCESS) {
998
+ throw new Error(`[BACKUP] Failed to ${((i >= backupData.length) ? "erase" : "set")} key table entry at index ${i} `
999
+ + `with status=${enums_2.EmberStatus[status]}`);
1000
+ }
1001
+ }
1002
+ debug(`[BACKUP] Imported ${backupData.length} keys.`);
1003
+ }
1004
+ /**
1005
+ * Routine to update the network key and broadcast the update to the network after a set time.
1006
+ * NOTE: This should run at a large interval, but before the uint32_t of the frame counter is able to reach all Fs (can't wrap to 0).
1007
+ * This may disrupt sleepy end devices that miss the update, but they should be able to TC rejoin (in most cases...).
1008
+ * On the other hand, the more often this runs, the more secure the network is...
1009
+ */
1010
+ async broadcastNetworkKeyUpdate() {
1011
+ return new Promise((resolve, reject) => {
1012
+ this.requestQueue.enqueue(async () => {
1013
+ console.warn(`[TRUST CENTER] Performing a network key update. This might take a while and disrupt normal operation.`);
1014
+ // zero-filled = let stack generate new random network key
1015
+ let status = await this.ezsp.ezspBroadcastNextNetworkKey({ contents: Buffer.alloc(consts_1.EMBER_ENCRYPTION_KEY_SIZE) });
1016
+ if (status !== enums_2.EmberStatus.SUCCESS) {
1017
+ console.error(`[TRUST CENTER] Failed to broadcast next network key with status=${enums_2.EmberStatus[status]}.`);
1018
+ return status;
1019
+ }
1020
+ // XXX: this will block other requests for a while, but should ensure the key propagates without interference?
1021
+ // could also stop dispatching entirely and do this outside the queue if necessary/better
1022
+ await (0, utils_1.Wait)(BROADCAST_NETWORK_KEY_SWITCH_WAIT_TIME);
1023
+ status = (await this.ezsp.ezspBroadcastNetworkKeySwitch());
1024
+ if (status !== enums_2.EmberStatus.SUCCESS) {
1025
+ // XXX: Not sure how likely this is, but this is bad, probably should hard fail?
1026
+ console.error(`[TRUST CENTER] Failed to broadcast network key switch with status=${enums_2.EmberStatus[status]}.`);
1027
+ return status;
1028
+ }
1029
+ resolve();
1030
+ return status;
1031
+ }, reject);
1032
+ });
1033
+ }
1034
+ /**
1035
+ * Received when EZSP layer alerts of a problem that needs the NCP to be reset.
1036
+ * @param status
1037
+ */
1038
+ async onNcpNeedsResetAndInit(status) {
1039
+ console.error(`!!! NCP FATAL ERROR reason=${enums_2.EzspStatus[status]}. ATTEMPTING RESET... !!!`);
1040
+ try {
1041
+ await this.stop();
1042
+ await (0, utils_1.Wait)(500); // just because
1043
+ await this.start();
1044
+ }
1045
+ catch (err) {
1046
+ console.error(`Failed to reset and init NCP. ${err}`);
1047
+ this.emit(events_1.Events.disconnected);
1048
+ }
1049
+ }
1050
+ /**
1051
+ * Called right before a NCP reset.
1052
+ */
1053
+ async onNCPPreReset() {
1054
+ this.requestQueue.stopDispatching();
1055
+ }
1056
+ /**
1057
+ * Called right after a NCP reset, right before the creation of endpoints.
1058
+ */
1059
+ async onNCPPostReset() {
1060
+ this.requestQueue.startDispatching();
1061
+ this.watchdogCountersHandle = setInterval(this.watchdogCounters.bind(this), WATCHDOG_COUNTERS_FEED_INTERVAL);
1062
+ }
1063
+ /**
1064
+ * Handle changes in groups that needs to be propagated to the NCP multicast table.
1065
+ *
1066
+ * XXX: Since Z2M doesn't explicitly check-in downstream when groups are created/removed, we look at outgoing genGroups commands.
1067
+ * If the NCP doesn't know about groups, it can miss messages from some devices (remotes for example), so we add it...
1068
+ *
1069
+ * @param commandId
1070
+ * @param groupId
1071
+ */
1072
+ async onGroupChange(commandId, groupId) {
1073
+ switch (commandId) {
1074
+ case cluster_1.default.genGroups.commands.add.ID: {
1075
+ // check if group already in multicast table, should not happen...
1076
+ const existingIndex = this.multicastTable.findIndex((e) => ((e != null) && (e.multicastId === groupId)));
1077
+ if (existingIndex == -1) {
1078
+ // find first unused index
1079
+ const newEntryIndex = this.multicastTable.findIndex((e) => (!e));
1080
+ if (newEntryIndex != -1) {
1081
+ const newEntry = {
1082
+ multicastId: groupId,
1083
+ endpoint: endpoints_1.FIXED_ENDPOINTS[0].endpoint,
1084
+ networkIndex: endpoints_1.FIXED_ENDPOINTS[0].networkIndex,
1085
+ };
1086
+ const status = (await this.ezsp.ezspSetMulticastTableEntry(newEntryIndex, newEntry));
1087
+ if (status !== enums_2.EmberStatus.SUCCESS) {
1088
+ console.error(`Failed to register group "${groupId}" in multicast table at index "${newEntryIndex}" with status=${enums_2.EmberStatus[status]}.`);
1089
+ }
1090
+ else {
1091
+ debug(`Registered multicast table entry: ${JSON.stringify(newEntry)}.`);
1092
+ }
1093
+ // always assume "it worked" to keep sync with Z2M first, NCP second, otherwise trouble might arise... should always work anyway
1094
+ this.multicastTable[newEntryIndex] = newEntry;
1095
+ }
1096
+ else {
1097
+ console.warn(`Coordinator multicast table is full (max: ${STACK_CONFIGS[this.stackConfig].MULTICAST_TABLE_SIZE}). `
1098
+ + `Some devices in new groups may not work properly, including in group "${groupId}". `
1099
+ + `If that happens, please remove groups to be below the limit. `
1100
+ + `Removed groups are only removed from coordinator after a Zigbee2MQTT restart.`);
1101
+ }
1102
+ }
1103
+ else {
1104
+ debug(`Added group "${groupId}", but local table says it is already registered at index "${existingIndex}". Skipping.`);
1105
+ }
1106
+ break;
1107
+ }
1108
+ // NOTE: Can't remove groups, since we watch from command exec to group members, that would trigger from any removed member,
1109
+ // even though the group might still exist...
1110
+ // Leaving this here (since it's done...), just in case we get better notifications for groups from upstream.
1111
+ // case Cluster.genGroups.commands.remove.ID: {
1112
+ // const entryIndex = this.multicastTable.findIndex((e) => ((e != null) && (e.multicastId === groupId)));
1113
+ // // just in case, never remove GP at i zero, should never be the case...
1114
+ // if (entryIndex > 0) {
1115
+ // const entry = this.multicastTable[entryIndex];
1116
+ // entry.endpoint = 0;// signals "not in use" in the stack
1117
+ // const status = (await this.ezsp.ezspSetMulticastTableEntry(entryIndex, entry));
1118
+ // if (status !== EmberStatus.SUCCESS) {
1119
+ // console.error(`Failed to remove multicast table entry at index "${entryIndex}" for group "${groupId}".`);
1120
+ // } else {
1121
+ // debug(`Removed multicast table entry at index "${entryIndex}".`);
1122
+ // }
1123
+ // // always assume "it worked" to keep sync with Z2M first, NCP second, otherwise trouble might arise... should always work anyway
1124
+ // this.multicastTable[entryIndex] = null;
1125
+ // } else {
1126
+ // debug(`Removed group "${groupId}", but local table did not have a reference to it.`);
1127
+ // }
1128
+ // break;
1129
+ // }
1130
+ // case Cluster.genGroups.commands.removeAll.ID: {
1131
+ // // this can create quite a few NCP calls, but hopefully shouldn't happen often
1132
+ // // always skip green power at i==0
1133
+ // for (let i = 1; i < this.multicastTable.length; i++) {
1134
+ // const entry = this.multicastTable[i];
1135
+ // if (entry != null) {
1136
+ // entry.endpoint = 0;// signals "not in use" in the stack
1137
+ // const status = (await this.ezsp.ezspSetMulticastTableEntry(i, entry));
1138
+ // if (status !== EmberStatus.SUCCESS) {
1139
+ // console.error(`Failed to remove multicast entry at index "${i}" with status=${EmberStatus[status]}.`);
1140
+ // } else {
1141
+ // debug(`Removed multicast table entry at index "${i}".`);
1142
+ // }
1143
+ // }
1144
+ // this.multicastTable[i] = null;
1145
+ // }
1146
+ // break;
1147
+ // }
1148
+ }
1149
+ }
1150
+ //---- START Events
1151
+ //---- END Events
1152
+ //---- START Cache-enabled EZSP wrappers
1153
+ /**
1154
+ * Clear the cached network values (set to invalid values).
1155
+ */
1156
+ clearNetworkCache() {
1157
+ this.networkCache = (0, initters_1.initNetworkCache)();
1158
+ }
1159
+ /**
1160
+ * Return the current network state.
1161
+ * This call caches the results on the host to prevent frequent EZSP transactions.
1162
+ * Check against UNKNOWN_NETWORK_STATE for validity.
1163
+ */
1164
+ async emberNetworkState() {
1165
+ if (this.networkCache.status === consts_2.UNKNOWN_NETWORK_STATE) {
1166
+ const networkStatus = (await this.ezsp.ezspNetworkState());
1167
+ this.networkCache.status = networkStatus;
1168
+ }
1169
+ return this.networkCache.status;
1170
+ }
1171
+ /**
1172
+ * Return the EUI 64 of the local node
1173
+ * This call caches the results on the host to prevent frequent EZSP transactions.
1174
+ * Check against BLANK_EUI64 for validity.
1175
+ */
1176
+ async emberGetEui64() {
1177
+ if (this.networkCache.eui64 === consts_2.BLANK_EUI64) {
1178
+ this.networkCache.eui64 = (await this.ezsp.ezspGetEui64());
1179
+ }
1180
+ return this.networkCache.eui64;
1181
+ }
1182
+ /**
1183
+ * Return the PAN ID of the local node.
1184
+ * This call caches the results on the host to prevent frequent EZSP transactions.
1185
+ * Check against INVALID_PAN_ID for validity.
1186
+ */
1187
+ async emberGetPanId() {
1188
+ if (this.networkCache.parameters.panId === consts_2.INVALID_PAN_ID) {
1189
+ const [status, , parameters] = (await this.ezsp.ezspGetNetworkParameters());
1190
+ if (status === enums_2.EmberStatus.SUCCESS) {
1191
+ this.networkCache.parameters = parameters;
1192
+ }
1193
+ else {
1194
+ console.error(`Failed to get PAN ID (via network parameters) with status=${enums_2.EmberStatus[status]}.`);
1195
+ }
1196
+ }
1197
+ return this.networkCache.parameters.panId;
1198
+ }
1199
+ /**
1200
+ * Return the Extended PAN ID of the local node.
1201
+ * This call caches the results on the host to prevent frequent EZSP transactions.
1202
+ * Check against BLANK_EXTENDED_PAN_ID for validity.
1203
+ */
1204
+ async emberGetExtendedPanId() {
1205
+ if ((0, es6_1.default)(this.networkCache.parameters.extendedPanId, consts_2.BLANK_EXTENDED_PAN_ID)) {
1206
+ const [status, , parameters] = (await this.ezsp.ezspGetNetworkParameters());
1207
+ if (status === enums_2.EmberStatus.SUCCESS) {
1208
+ this.networkCache.parameters = parameters;
1209
+ }
1210
+ else {
1211
+ console.error(`Failed to get Extended PAN ID (via network parameters) with status=${enums_2.EmberStatus[status]}.`);
1212
+ }
1213
+ }
1214
+ return this.networkCache.parameters.extendedPanId;
1215
+ }
1216
+ /**
1217
+ * Return the radio channel (uint8_t) of the current network.
1218
+ * This call caches the results on the host to prevent frequent EZSP transactions.
1219
+ * Check against INVALID_RADIO_CHANNEL for validity.
1220
+ */
1221
+ async emberGetRadioChannel() {
1222
+ if (this.networkCache.parameters.radioChannel === consts_2.INVALID_RADIO_CHANNEL) {
1223
+ const [status, , parameters] = (await this.ezsp.ezspGetNetworkParameters());
1224
+ if (status === enums_2.EmberStatus.SUCCESS) {
1225
+ this.networkCache.parameters = parameters;
1226
+ }
1227
+ else {
1228
+ console.error(`Failed to get radio channel (via network parameters) with status=${enums_2.EmberStatus[status]}.`);
1229
+ }
1230
+ }
1231
+ return this.networkCache.parameters.radioChannel;
1232
+ }
1233
+ // queued
1234
+ async emberStartEnergyScan() {
1235
+ return new Promise((resolve, reject) => {
1236
+ this.requestQueue.enqueue(async () => {
1237
+ const status = (await this.ezsp.ezspStartScan(enums_2.EzspNetworkScanType.ENERGY_SCAN, consts_2.EMBER_ALL_802_15_4_CHANNELS_MASK, ENERGY_SCAN_DURATION));
1238
+ if (status !== enums_2.SLStatus.OK) {
1239
+ console.error(`Failed energy scan request with status=${enums_2.SLStatus[status]}.`);
1240
+ return enums_2.EmberStatus.ERR_FATAL;
1241
+ }
1242
+ // TODO: result in logs only atm, since UI doesn't support it
1243
+ resolve();
1244
+ return enums_2.EmberStatus.SUCCESS;
1245
+ }, reject);
1246
+ });
1247
+ }
1248
+ //---- END Cache-enabled EZSP wrappers
1249
+ //---- START EZSP wrappers
1250
+ /**
1251
+ * Ensure the Host & NCP are aligned on protocols using version.
1252
+ * Cache the retrieved information.
1253
+ *
1254
+ * NOTE: currently throws on mismatch until support for lower versions is implemented (not planned atm)
1255
+ *
1256
+ * Does nothing if ncpNeedsResetAndInit == true.
1257
+ */
1258
+ async emberVersion() {
1259
+ // Note that NCP == Network Co-Processor
1260
+ // the EZSP protocol version that the Host is running, we are the host so we set this value
1261
+ const hostEzspProtocolVer = consts_1.EZSP_PROTOCOL_VERSION;
1262
+ // send the Host version number to the NCP.
1263
+ // The NCP returns the EZSP version that the NCP is running along with the stackType and stackVersion
1264
+ const [ncpEzspProtocolVer, ncpStackType, ncpStackVer] = (await this.ezsp.ezspVersion(hostEzspProtocolVer));
1265
+ // verify that the stack type is what is expected
1266
+ if (ncpStackType !== consts_1.EZSP_STACK_TYPE_MESH) {
1267
+ throw new Error(`Stack type ${ncpStackType} is not expected!`);
1268
+ }
1269
+ // verify that the NCP EZSP Protocol version is what is expected
1270
+ if (ncpEzspProtocolVer !== consts_1.EZSP_PROTOCOL_VERSION) {
1271
+ throw new Error(`NCP EZSP protocol version of ${ncpEzspProtocolVer} does not match Host version ${hostEzspProtocolVer}`);
1272
+ }
1273
+ debug(`NCP info: EZSPVersion=${ncpEzspProtocolVer} StackType=${ncpStackType} StackVersion=${ncpStackVer}`);
1274
+ const [status, versionStruct] = (await this.ezsp.ezspGetVersionStruct());
1275
+ if (status !== enums_2.EzspStatus.SUCCESS) {
1276
+ // NCP has old style version number
1277
+ debug(`NCP has old-style version number.`);
1278
+ this.version = {
1279
+ ezsp: ncpEzspProtocolVer,
1280
+ revision: `${ncpStackVer}`,
1281
+ major: ncpStackVer,
1282
+ minor: 0,
1283
+ patch: 0,
1284
+ special: 0,
1285
+ build: 0,
1286
+ type: enums_2.EmberVersionType.GA, // default...
1287
+ };
1288
+ }
1289
+ else {
1290
+ // NCP has new style version number
1291
+ this.version = {
1292
+ ezsp: ncpEzspProtocolVer,
1293
+ revision: `${versionStruct.major}.${versionStruct.minor}.${versionStruct.patch} [${enums_2.EmberVersionType[versionStruct.type]}]`,
1294
+ ...versionStruct,
1295
+ };
1296
+ if (versionStruct.type !== enums_2.EmberVersionType.GA) {
1297
+ console.warn(`NCP is running a non-GA version (${enums_2.EmberVersionType[versionStruct.type]}).`);
1298
+ }
1299
+ }
1300
+ debug(`NCP version info: ${JSON.stringify(this.version)}`);
1301
+ }
1302
+ /**
1303
+ * This function sets an EZSP config value.
1304
+ * WARNING: Do not call for values that cannot be set after init without first resetting NCP (like table sizes).
1305
+ * To avoid an extra NCP call, this does not check for it.
1306
+ * @param configId
1307
+ * @param value uint16_t
1308
+ * @returns
1309
+ */
1310
+ async emberSetEzspConfigValue(configId, value) {
1311
+ const status = (await this.ezsp.ezspSetConfigurationValue(configId, value));
1312
+ debug(`[EzspConfigId] SET "${enums_1.EzspConfigId[configId]}" TO "${value}" with status=${enums_2.EzspStatus[status]}.`);
1313
+ if (status === enums_2.EzspStatus.ERROR_INVALID_ID) {
1314
+ // can be ZLL where not all NCPs need or support it.
1315
+ console.warn(`[EzspConfigId] Unsupported configuration ID ${enums_1.EzspConfigId[configId]} by NCP.`);
1316
+ }
1317
+ else if (status !== enums_2.EzspStatus.SUCCESS) {
1318
+ // don't fail in case a set value gets called "out of time"
1319
+ console.error(`[EzspConfigId] Failed to SET "${enums_1.EzspConfigId[configId]}" TO "${value}" with status=${enums_2.EzspStatus[status]}.`);
1320
+ }
1321
+ return status;
1322
+ }
1323
+ /**
1324
+ * This function sets an EZSP value.
1325
+ * @param valueId
1326
+ * @param valueLength uint8_t
1327
+ * @param value uint8_t *
1328
+ * @returns
1329
+ */
1330
+ async emberSetEzspValue(valueId, valueLength, value) {
1331
+ const status = (await this.ezsp.ezspSetValue(valueId, valueLength, value));
1332
+ debug(`[EzspValueId] SET "${enums_1.EzspValueId[valueId]}" TO "${value}" with status=${enums_2.EzspStatus[status]}.`);
1333
+ return status;
1334
+ }
1335
+ /**
1336
+ * This function sets an EZSP policy.
1337
+ * @param policyId
1338
+ * @param decisionId Can be bitop
1339
+ * @returns
1340
+ */
1341
+ async emberSetEzspPolicy(policyId, decisionId) {
1342
+ const status = (await this.ezsp.ezspSetPolicy(policyId, decisionId));
1343
+ debug(`[EzspPolicyId] SET "${enums_1.EzspPolicyId[policyId]}" TO "${decisionId}" with status=${enums_2.EzspStatus[status]}.`);
1344
+ return status;
1345
+ }
1346
+ /**
1347
+ * Here we convert the normal Ember AES hash call to the specialized EZSP call.
1348
+ * This came about because we cannot pass a block of data that is
1349
+ * both input and output into EZSP. The block must be broken up into two
1350
+ * elements. We unify the two pieces here to make it invisible to the users.
1351
+ * @param context EmberAesMmoHashContext *
1352
+ * @param finalize
1353
+ * @param data uint8_t * Expected of valid length (as in, not larger alloc)
1354
+ * @returns status
1355
+ * @returns result context or null
1356
+ */
1357
+ async aesMmoHash(context, finalize, data) {
1358
+ if (data.length > 255) {
1359
+ throw new Error(enums_2.EzspStatus[enums_2.EzspStatus.ERROR_INVALID_CALL]);
1360
+ }
1361
+ const [status, reContext] = (await this.ezsp.ezspAesMmoHash(context, finalize, data));
1362
+ return [status, reContext];
1363
+ }
1364
+ /**
1365
+ * This routine processes the passed chunk of data and updates
1366
+ * the hash calculation based on it. The data passed in MUST
1367
+ * have a length that is a multiple of 16.
1368
+ *
1369
+ * @param context EmberAesMmoHashContext* A pointer to the location of the hash context to update.
1370
+ * @param data const uint8_t* A pointer to the location of the data to hash.
1371
+ *
1372
+ * @returns An ::EmberStatus value indicating EMBER_SUCCESS if the hash was
1373
+ * calculated successfully. EMBER_INVALID_CALL if the block size is not a
1374
+ * multiple of 16 bytes, and EMBER_INDEX_OUT_OF_RANGE is returned when the
1375
+ * data exceeds the maximum limits of the hash function.
1376
+ * @returns result context or null
1377
+ */
1378
+ async emberAesMmoHashUpdate(context, data) {
1379
+ return this.aesMmoHash(context, false /*finalize?*/, data);
1380
+ }
1381
+ /**
1382
+ * This routine processes the passed chunk of data (if non-NULL)
1383
+ * and update the hash context that is passed in. In then performs
1384
+ * the final calculations on the hash and returns the final answer
1385
+ * in the result parameter of the ::EmberAesMmoHashContext structure.
1386
+ * The length of the data passed in may be any value, it does not have
1387
+ * to be a multiple of 16.
1388
+ *
1389
+ * @param context EmberAesMmoHashContext * A pointer to the location of the hash context to finalize.
1390
+ * @param data uint8_t * A pointer to the location of data to hash. May be NULL.
1391
+ *
1392
+ * @returns An ::EmberStatus value indicating EMBER_SUCCESS if the hash was
1393
+ * calculated successfully. EMBER_INVALID_CALL if the block size is not a
1394
+ * multiple of 16 bytes, and EMBER_INDEX_OUT_OF_RANGE is returned when the
1395
+ * data exceeds the maximum limits of the hash function.
1396
+ * @returns result context or null
1397
+ */
1398
+ async emberAesMmoHashFinal(context, data) {
1399
+ return this.aesMmoHash(context, true /*finalize?*/, data);
1400
+ }
1401
+ /**
1402
+ * This is a convenience method when the hash data is less than 255
1403
+ * bytes. It inits, updates, and finalizes the hash in one function call.
1404
+ *
1405
+ * @param data const uint8_t* The data to hash. Expected of valid length (as in, not larger alloc)
1406
+ *
1407
+ * @returns An ::EmberStatus value indicating EMBER_SUCCESS if the hash was
1408
+ * calculated successfully. EMBER_INVALID_CALL if the block size is not a
1409
+ * multiple of 16 bytes, and EMBER_INDEX_OUT_OF_RANGE is returned when the
1410
+ * data exceeds the maximum limits of the hash function.
1411
+ * @returns result uint8_t* The location where the result of the hash will be written.
1412
+ */
1413
+ async emberAesHashSimple(data) {
1414
+ const context = (0, initters_1.aesMmoHashInit)();
1415
+ const [status, reContext] = (await this.emberAesMmoHashFinal(context, data));
1416
+ return [status, reContext?.result];
1417
+ }
1418
+ /**
1419
+ * Enable local permit join and optionally broadcast the ZDO Mgmt_Permit_Join_req message.
1420
+ * This API can be called from any device type and still return EMBER_SUCCESS.
1421
+ * If the API is called from an end device, the permit association bit will just be left off.
1422
+ *
1423
+ * @param duration uint8_t The duration that the permit join bit will remain on
1424
+ * and other devices will be able to join the current network.
1425
+ * @param broadcastMgmtPermitJoin whether or not to broadcast the ZDO Mgmt_Permit_Join_req message.
1426
+ *
1427
+ * @returns status of whether or not permit join was enabled.
1428
+ * @returns apsFrame Will be null if not broadcasting.
1429
+ * @returns messageTag The tag passed to ezspSend${x} function.
1430
+ */
1431
+ async emberPermitJoining(duration, broadcastMgmtPermitJoin) {
1432
+ let status = (await this.ezsp.ezspPermitJoining(duration));
1433
+ let apsFrame = null;
1434
+ let messageTag = null;
1435
+ debug(`Permit joining for ${duration} sec. status=${[status]}`);
1436
+ if (broadcastMgmtPermitJoin) {
1437
+ // `authentication`: TC significance always 1 (zb specs)
1438
+ [status, apsFrame, messageTag] = (await this.emberPermitJoiningRequest(consts_2.EMBER_BROADCAST_ADDRESS, duration, 1, this.defaultApsOptions));
1439
+ }
1440
+ return [status, apsFrame, messageTag];
1441
+ }
1442
+ /**
1443
+ * Set the trust center policy bitmask using decision.
1444
+ * @param decision
1445
+ * @returns
1446
+ */
1447
+ async emberSetJoinPolicy(decision) {
1448
+ let policy = enums_1.EzspDecisionBitmask.DEFAULT_CONFIGURATION;
1449
+ if (decision == enums_2.EmberJoinDecision.USE_PRECONFIGURED_KEY) {
1450
+ policy = (enums_1.EzspDecisionBitmask.ALLOW_JOINS | enums_1.EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS);
1451
+ }
1452
+ else if (decision == enums_2.EmberJoinDecision.SEND_KEY_IN_THE_CLEAR) {
1453
+ policy = (enums_1.EzspDecisionBitmask.ALLOW_JOINS | enums_1.EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS | enums_1.EzspDecisionBitmask.SEND_KEY_IN_CLEAR);
1454
+ }
1455
+ else if (decision == enums_2.EmberJoinDecision.ALLOW_REJOINS_ONLY) {
1456
+ policy = enums_1.EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS;
1457
+ }
1458
+ return this.emberSetEzspPolicy(enums_1.EzspPolicyId.TRUST_CENTER_POLICY, policy);
1459
+ }
1460
+ /**
1461
+ * Get Source Route Overhead
1462
+ *
1463
+ * Returns the number of bytes needed in a packet for source routing.
1464
+ * Since each hop consumes 2 bytes in the packet, this routine calculates the
1465
+ * total number of bytes needed based on number of hops to reach the destination.
1466
+ *
1467
+ * This function is called by the framework to determine the overhead required
1468
+ * in the network frame for source routing to a particular destination.
1469
+ *
1470
+ * @param destination The node id of the destination Ver.: always
1471
+ * @returns int8u The number of bytes needed for source routing in a packet.
1472
+ */
1473
+ async emberGetSourceRouteOverhead(destination) {
1474
+ const [status, value] = (await this.ezsp.ezspGetSourceRouteOverhead(destination));
1475
+ if (status === enums_2.EzspStatus.SUCCESS) {
1476
+ return value;
1477
+ }
1478
+ else {
1479
+ debug(`Failed to get source route overhead (via extended value), status=${enums_2.EzspStatus[status]}.`);
1480
+ }
1481
+ return 0;
1482
+ }
1483
+ /**
1484
+ * Return the maximum size of the payload that the Application Support sub-layer will accept for
1485
+ * the given message type, destination, and APS frame.
1486
+ *
1487
+ * The size depends on multiple factors, including the security level in use and additional information
1488
+ * added to the message to support the various options.
1489
+ *
1490
+ * @param type The outgoing message type.
1491
+ * @param indexOrDestination uint16_t Depending on the message type, this is either the
1492
+ * EmberNodeId of the destination, an index into the address table, an index
1493
+ * into the binding table, the multicast identifier, or a broadcast address.
1494
+ * @param apsFrame EmberApsFrame *The APS frame for the message.
1495
+ * @return uint8_t The maximum APS payload length for the given message.
1496
+ */
1497
+ async maximumApsPayloadLength(type, indexOrDestination, apsFrame) {
1498
+ let destination = consts_2.EMBER_UNKNOWN_NODE_ID;
1499
+ let max = consts_2.MAXIMUM_APS_PAYLOAD_LENGTH; // uint8_t
1500
+ if ((apsFrame.options & enums_2.EmberApsOption.ENCRYPTION) !== 0) {
1501
+ max -= consts_2.APS_ENCRYPTION_OVERHEAD;
1502
+ }
1503
+ if ((apsFrame.options & enums_2.EmberApsOption.SOURCE_EUI64) !== 0) {
1504
+ max -= consts_1.EUI64_SIZE;
1505
+ }
1506
+ if ((apsFrame.options & enums_2.EmberApsOption.DESTINATION_EUI64) !== 0) {
1507
+ max -= consts_1.EUI64_SIZE;
1508
+ }
1509
+ if ((apsFrame.options & enums_2.EmberApsOption.FRAGMENT) !== 0) {
1510
+ max -= consts_2.APS_FRAGMENTATION_OVERHEAD;
1511
+ }
1512
+ switch (type) {
1513
+ case enums_2.EmberOutgoingMessageType.DIRECT:
1514
+ destination = indexOrDestination;
1515
+ break;
1516
+ case enums_2.EmberOutgoingMessageType.VIA_ADDRESS_TABLE:
1517
+ destination = (await this.ezsp.ezspGetAddressTableRemoteNodeId(indexOrDestination));
1518
+ break;
1519
+ case enums_2.EmberOutgoingMessageType.VIA_BINDING:
1520
+ destination = (await this.ezsp.ezspGetBindingRemoteNodeId(indexOrDestination));
1521
+ break;
1522
+ case enums_2.EmberOutgoingMessageType.MULTICAST:
1523
+ // APS multicast messages include the two-byte group id and exclude the one-byte destination endpoint,
1524
+ // for a net loss of an extra byte.
1525
+ max--;
1526
+ break;
1527
+ case enums_2.EmberOutgoingMessageType.BROADCAST:
1528
+ break;
1529
+ default:
1530
+ break;
1531
+ }
1532
+ max -= (await this.emberGetSourceRouteOverhead(destination));
1533
+ return max;
1534
+ }
1535
+ //---- END EZSP wrappers
1536
+ //---- START Ember ZDO
1537
+ /**
1538
+ * ZDO
1539
+ * Change the default radius for broadcast ZDO requests
1540
+ *
1541
+ * @param radius uint8_t The radius to be used for future ZDO request broadcasts.
1542
+ */
1543
+ setZDORequestRadius(radius) {
1544
+ this.zdoRequestRadius = radius;
1545
+ }
1546
+ /**
1547
+ * ZDO
1548
+ * Retrieve the default radius for broadcast ZDO requests
1549
+ *
1550
+ * @return uint8_t The radius to be used for future ZDO request broadcasts.
1551
+ */
1552
+ getZDORequestRadius() {
1553
+ return this.zdoRequestRadius;
1554
+ }
1555
+ /**
1556
+ * ZDO
1557
+ * Get the next device request sequence number.
1558
+ *
1559
+ * Requests have sequence numbers so that they can be matched up with the
1560
+ * responses. To avoid complexities, the library uses numbers with the high
1561
+ * bit clear and the stack uses numbers with the high bit set.
1562
+ *
1563
+ * @return uint8_t The next device request sequence number
1564
+ */
1565
+ nextZDORequestSequence() {
1566
+ return (this.zdoRequestSequence = ((++this.zdoRequestSequence) & APPLICATION_ZDO_SEQUENCE_MASK));
1567
+ }
1568
+ /**
1569
+ * ZDO
1570
+ *
1571
+ * @param destination
1572
+ * @param clusterId uint16_t
1573
+ * @param options
1574
+ * @param length uint8_t
1575
+ * @returns status Indicates success or failure (with reason) of send
1576
+ * @returns apsFrame The APS Frame resulting of the request being built and sent (`sequence` set from stack-given value).
1577
+ * @returns messageTag The tag passed to ezspSend${x} function.
1578
+ */
1579
+ async sendZDORequestBuffer(destination, clusterId, options) {
1580
+ if (this.zdoRequestBuffalo.getPosition() > consts_1.EZSP_MAX_FRAME_LENGTH) {
1581
+ return [enums_2.EmberStatus.MESSAGE_TOO_LONG, null, null];
1582
+ }
1583
+ const messageTag = this.nextZDORequestSequence();
1584
+ this.zdoRequestBuffalo.setCommandByte(0, messageTag);
1585
+ const apsFrame = {
1586
+ profileId: zdo_1.ZDO_PROFILE_ID,
1587
+ clusterId: clusterId,
1588
+ sourceEndpoint: zdo_1.ZDO_ENDPOINT,
1589
+ destinationEndpoint: zdo_1.ZDO_ENDPOINT,
1590
+ options: options,
1591
+ groupId: 0,
1592
+ sequence: 0, // set by stack
1593
+ };
1594
+ const messageContents = this.zdoRequestBuffalo.getWritten();
1595
+ if (destination === consts_2.EMBER_BROADCAST_ADDRESS || destination === consts_2.EMBER_RX_ON_WHEN_IDLE_BROADCAST_ADDRESS
1596
+ || destination === consts_2.EMBER_SLEEPY_BROADCAST_ADDRESS) {
1597
+ debug(`~~~> [ZDO BROADCAST apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag}]`);
1598
+ const [status, apsSequence] = (await this.ezsp.ezspSendBroadcast(destination, apsFrame, this.getZDORequestRadius(), messageTag, messageContents));
1599
+ apsFrame.sequence = apsSequence;
1600
+ debug(`~~~> [SENT ZDO type=BROADCAST apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag} status=${enums_2.EmberStatus[status]}]`);
1601
+ return [status, apsFrame, messageTag];
1602
+ }
1603
+ else {
1604
+ debug(`~~~> [ZDO UNICAST apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag}]`);
1605
+ const [status, apsSequence] = (await this.ezsp.ezspSendUnicast(enums_2.EmberOutgoingMessageType.DIRECT, destination, apsFrame, messageTag, messageContents));
1606
+ apsFrame.sequence = apsSequence;
1607
+ debug(`~~~> [SENT ZDO type=DIRECT apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag} status=${enums_2.EmberStatus[status]}]`);
1608
+ return [status, apsFrame, messageTag];
1609
+ }
1610
+ }
1611
+ /**
1612
+ * ZDO
1613
+ * Service Discovery Functions
1614
+ * Request the specified node to send a list of its endpoints that
1615
+ * match the specified application profile and, optionally, lists of input
1616
+ * and/or output clusters.
1617
+ * @param target The node whose matching endpoints are desired. The request can
1618
+ * be sent unicast or broadcast ONLY to the "RX-on-when-idle-address" (0xFFFD)
1619
+ * If sent as a broadcast, any node that has matching endpoints will send a
1620
+ * response.
1621
+ * @param profile uint16_t The application profile to match.
1622
+ * @param inCount uint8_t The number of input clusters. To not match any input
1623
+ * clusters, set this value to 0.
1624
+ * @param outCount uint8_t The number of output clusters. To not match any output
1625
+ * clusters, set this value to 0.
1626
+ * @param inClusters uint16_t * The list of input clusters.
1627
+ * @param outClusters uint16_t * The list of output clusters.
1628
+ * @param options The options to use when sending the unicast request. See
1629
+ * emberSendUnicast() for a description. This parameter is ignored if the target
1630
+ * is a broadcast address.
1631
+ * @returns An EmberStatus value. EMBER_SUCCESS, MESSAGE_TOO_LONG,
1632
+ * EMBER_NETWORK_DOWN or EMBER_NETWORK_BUSY.
1633
+ */
1634
+ async emberMatchDescriptorsRequest(target, profile, inClusters, outClusters, options) {
1635
+ // 2 bytes for NWK Address + 2 bytes for Profile Id + 1 byte for in Cluster Count
1636
+ // + in times 2 for 2 byte Clusters + out Cluster Count + out times 2 for 2 byte Clusters
1637
+ const length = (zdo_1.ZDO_MESSAGE_OVERHEAD + 2 + 2 + 1 + (inClusters.length * 2) + 1 + (outClusters.length * 2));
1638
+ // sanity check
1639
+ if (length > consts_1.EZSP_MAX_FRAME_LENGTH) {
1640
+ return [enums_2.EmberStatus.MESSAGE_TOO_LONG, null, null];
1641
+ }
1642
+ this.zdoRequestBuffalo.setPosition(zdo_1.ZDO_MESSAGE_OVERHEAD);
1643
+ this.zdoRequestBuffalo.writeUInt16(target);
1644
+ this.zdoRequestBuffalo.writeUInt16(profile);
1645
+ this.zdoRequestBuffalo.writeUInt8(inClusters.length);
1646
+ this.zdoRequestBuffalo.writeListUInt16(inClusters);
1647
+ this.zdoRequestBuffalo.writeUInt8(outClusters.length);
1648
+ this.zdoRequestBuffalo.writeListUInt16(outClusters);
1649
+ debug(`~~~> [ZDO MATCH DESCRIPTOR target=${target} profile=${profile} inClusters=${inClusters} outClusters=${outClusters}]`);
1650
+ return this.sendZDORequestBuffer(target, zdo_1.MATCH_DESCRIPTORS_REQUEST, options);
1651
+ }
1652
+ /**
1653
+ * ZDO
1654
+ * Device Discovery Functions
1655
+ * Request the 16 bit network address of a node whose EUI64 is known.
1656
+ *
1657
+ * @param target The EUI64 of the node.
1658
+ * @param reportKids true to request that the target list their children
1659
+ * in the response.
1660
+ * @param childStartIndex uint8_t The index of the first child to list in the response.
1661
+ * Ignored if @c reportKids is false.
1662
+ *
1663
+ * @return An ::EmberStatus value.
1664
+ * - ::EMBER_SUCCESS - The request was transmitted successfully.
1665
+ * - ::EMBER_NO_BUFFERS - Insufficient message buffers were available to construct the request.
1666
+ * - ::EMBER_NETWORK_DOWN - The node is not part of a network.
1667
+ * - ::EMBER_NETWORK_BUSY - Transmission of the request failed.
1668
+ */
1669
+ async emberNetworkAddressRequest(target, reportKids, childStartIndex) {
1670
+ this.zdoRequestBuffalo.setPosition(zdo_1.ZDO_MESSAGE_OVERHEAD);
1671
+ this.zdoRequestBuffalo.writeIeeeAddr(target);
1672
+ this.zdoRequestBuffalo.writeUInt8(reportKids ? 1 : 0);
1673
+ this.zdoRequestBuffalo.writeUInt8(childStartIndex);
1674
+ debug(`~~~> [ZDO NETWORK ADDRESS target=${target} reportKids=${reportKids} childStartIndex=${childStartIndex}]`);
1675
+ return this.sendZDORequestBuffer(consts_2.EMBER_RX_ON_WHEN_IDLE_BROADCAST_ADDRESS, zdo_1.NETWORK_ADDRESS_REQUEST, enums_2.EmberApsOption.SOURCE_EUI64);
1676
+ }
1677
+ /**
1678
+ * ZDO
1679
+ * Device Discovery Functions
1680
+ * @brief Request the EUI64 of a node whose 16 bit network address is known.
1681
+ *
1682
+ * @param target uint16_t The network address of the node.
1683
+ * @param reportKids uint8_t true to request that the target list their children
1684
+ * in the response.
1685
+ * @param childStartIndex uint8_t The index of the first child to list in the response.
1686
+ * Ignored if reportKids is false.
1687
+ * @param options The options to use when sending the request. See ::emberSendUnicast() for a description.
1688
+ *
1689
+ * @return An ::EmberStatus value.
1690
+ * - ::EMBER_SUCCESS
1691
+ * - ::EMBER_NO_BUFFERS
1692
+ * - ::EMBER_NETWORK_DOWN
1693
+ * - ::EMBER_NETWORK_BUSY
1694
+ */
1695
+ async emberIeeeAddressRequest(target, reportKids, childStartIndex, options) {
1696
+ this.zdoRequestBuffalo.setPosition(zdo_1.ZDO_MESSAGE_OVERHEAD);
1697
+ this.zdoRequestBuffalo.writeUInt16(target);
1698
+ this.zdoRequestBuffalo.writeUInt8(reportKids ? 1 : 0);
1699
+ this.zdoRequestBuffalo.writeUInt8(childStartIndex);
1700
+ debug(`~~~> [ZDO IEEE ADDRESS target=${target} reportKids=${reportKids} childStartIndex=${childStartIndex}]`);
1701
+ return this.sendZDORequestBuffer(target, zdo_1.IEEE_ADDRESS_REQUEST, options);
1702
+ }
1703
+ /**
1704
+ * ZDO
1705
+ * @param discoveryNodeId uint16_t
1706
+ * @param reportKids uint8_t
1707
+ * @param childStartIndex uint8_t
1708
+ * @param options
1709
+ * @param targetNodeIdOfRequest
1710
+ */
1711
+ async emberIeeeAddressRequestToTarget(discoveryNodeId, reportKids, childStartIndex, options, targetNodeIdOfRequest) {
1712
+ this.zdoRequestBuffalo.setPosition(zdo_1.ZDO_MESSAGE_OVERHEAD);
1713
+ this.zdoRequestBuffalo.writeUInt16(discoveryNodeId);
1714
+ this.zdoRequestBuffalo.writeUInt8(reportKids ? 1 : 0);
1715
+ this.zdoRequestBuffalo.writeUInt8(childStartIndex);
1716
+ debug(`~~~> [ZDO IEEE ADDRESS targetNodeIdOfRequest=${targetNodeIdOfRequest} discoveryNodeId=${discoveryNodeId} `
1717
+ + `reportKids=${reportKids} childStartIndex=${childStartIndex}]`);
1718
+ return this.sendZDORequestBuffer(targetNodeIdOfRequest, zdo_1.IEEE_ADDRESS_REQUEST, options);
1719
+ }
1720
+ /**
1721
+ * ZDO
1722
+ *
1723
+ * @param target uint16_t
1724
+ * @param clusterId uint16_t
1725
+ * @param options
1726
+ * @returns
1727
+ */
1728
+ async emberSendZigDevRequestTarget(target, clusterId, options) {
1729
+ this.zdoRequestBuffalo.setPosition(zdo_1.ZDO_MESSAGE_OVERHEAD);
1730
+ this.zdoRequestBuffalo.writeUInt16(target);
1731
+ return this.sendZDORequestBuffer(target, clusterId, options);
1732
+ }
1733
+ /**
1734
+ * ZDO
1735
+ * @brief Request the specified node to send the simple descriptor for
1736
+ * the specified endpoint.
1737
+ * The simple descriptor contains information specific
1738
+ * to a single endpoint. It describes the application profile identifier,
1739
+ * application device identifier, application device version, application flags,
1740
+ * application input clusters and application output clusters. It is defined in
1741
+ * the ZigBee Application Framework Specification.
1742
+ *
1743
+ * @param target uint16_t The node of interest.
1744
+ * @param targetEndpoint uint8_t The endpoint on the target node whose simple
1745
+ * descriptor is desired.
1746
+ * @param options The options to use when sending the request. See
1747
+ * emberSendUnicast() for a description.
1748
+ *
1749
+ * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS,
1750
+ * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY.
1751
+ */
1752
+ async emberSimpleDescriptorRequest(target, targetEndpoint, options) {
1753
+ this.zdoRequestBuffalo.setPosition(zdo_1.ZDO_MESSAGE_OVERHEAD);
1754
+ this.zdoRequestBuffalo.writeUInt16(target);
1755
+ this.zdoRequestBuffalo.writeUInt8(targetEndpoint);
1756
+ debug(`~~~> [ZDO SIMPLE DESCRIPTOR target=${target} targetEndpoint=${targetEndpoint}]`);
1757
+ return this.sendZDORequestBuffer(target, zdo_1.SIMPLE_DESCRIPTOR_REQUEST, options);
1758
+ }
1759
+ /**
1760
+ * ZDO
1761
+ * Common logic used by `emberBindRequest` & `emberUnbindRequest`.
1762
+ *
1763
+ * @param target
1764
+ * @param bindClusterId
1765
+ * @param source
1766
+ * @param sourceEndpoint
1767
+ * @param clusterId
1768
+ * @param type
1769
+ * @param destination
1770
+ * @param groupAddress
1771
+ * @param destinationEndpoint
1772
+ * @param options
1773
+ *
1774
+ * @returns An ::EmberStatus value.
1775
+ * - ::EMBER_SUCCESS
1776
+ * - ::EMBER_NO_BUFFERS
1777
+ * - ::EMBER_NETWORK_DOWN
1778
+ * - ::EMBER_NETWORK_BUSY
1779
+ * @returns APS frame created for the request
1780
+ * @returns The tag used on the message.
1781
+ */
1782
+ async emberSendZigDevBindRequest(target, bindClusterId, source, sourceEndpoint, clusterId, type, destination, groupAddress, destinationEndpoint, options) {
1783
+ this.zdoRequestBuffalo.setPosition(zdo_1.ZDO_MESSAGE_OVERHEAD);
1784
+ this.zdoRequestBuffalo.writeIeeeAddr(source);
1785
+ this.zdoRequestBuffalo.writeUInt8(sourceEndpoint);
1786
+ this.zdoRequestBuffalo.writeUInt16(clusterId);
1787
+ this.zdoRequestBuffalo.writeUInt8(type);
1788
+ switch (type) {
1789
+ case zdo_1.UNICAST_BINDING:
1790
+ this.zdoRequestBuffalo.writeIeeeAddr(destination);
1791
+ this.zdoRequestBuffalo.writeUInt8(destinationEndpoint);
1792
+ break;
1793
+ case zdo_1.MULTICAST_BINDING:
1794
+ this.zdoRequestBuffalo.writeUInt16(groupAddress);
1795
+ break;
1796
+ default:
1797
+ return [enums_2.EmberStatus.ERR_FATAL, null, null];
1798
+ }
1799
+ return this.sendZDORequestBuffer(target, bindClusterId, options);
1800
+ }
1801
+ /**
1802
+ * ZDO
1803
+ * Send a request to create a binding entry with the specified
1804
+ * contents on the specified node.
1805
+ *
1806
+ * @param target The node on which the binding will be created.
1807
+ * @param source The source EUI64 in the binding entry.
1808
+ * @param sourceEndpoint The source endpoint in the binding entry.
1809
+ * @param clusterId The cluster ID in the binding entry.
1810
+ * @param type The type of binding, either ::UNICAST_BINDING,
1811
+ * ::MULTICAST_BINDING, or ::UNICAST_MANY_TO_ONE_BINDING.
1812
+ * ::UNICAST_MANY_TO_ONE_BINDING is an Ember-specific extension
1813
+ * and should be used only when the target is an Ember device.
1814
+ * @param destination The destination EUI64 in the binding entry for
1815
+ * ::UNICAST_BINDING or ::UNICAST_MANY_TO_ONE_BINDING.
1816
+ * @param groupAddress The group address for the ::MULTICAST_BINDING.
1817
+ * @param destinationEndpoint The destination endpoint in the binding entry for
1818
+ * the ::UNICAST_BINDING or ::UNICAST_MANY_TO_ONE_BINDING.
1819
+ * @param options The options to use when sending the request. See
1820
+ * emberSendUnicast() for a description.
1821
+ *
1822
+ * @returns An ::EmberStatus value.
1823
+ * - ::EMBER_SUCCESS
1824
+ * - ::EMBER_NO_BUFFERS
1825
+ * - ::EMBER_NETWORK_DOWN
1826
+ * - ::EMBER_NETWORK_BUSY
1827
+ * @returns APS frame created for the request
1828
+ * @returns The tag used on the message.
1829
+ */
1830
+ async emberBindRequest(target, source, sourceEndpoint, clusterId, type, destination, groupAddress, destinationEndpoint, options) {
1831
+ debug(`~~~> [ZDO BIND target=${target} source=${source} sourceEndpoint=${sourceEndpoint} clusterId=${clusterId} type=${type} `
1832
+ + `destination=${destination} groupAddress=${groupAddress} destinationEndpoint=${destinationEndpoint}]`);
1833
+ return this.emberSendZigDevBindRequest(target, zdo_1.BIND_REQUEST, source, sourceEndpoint, clusterId, type, destination, groupAddress, destinationEndpoint, options);
1834
+ }
1835
+ /**
1836
+ * ZDO
1837
+ * Send a request to remove a binding entry with the specified
1838
+ * contents from the specified node.
1839
+ *
1840
+ * @param target The node on which the binding will be removed.
1841
+ * @param source The source EUI64 in the binding entry.
1842
+ * @param sourceEndpoint uint8_t The source endpoint in the binding entry.
1843
+ * @param clusterId uint16_t The cluster ID in the binding entry.
1844
+ * @param type uint8_t The type of binding, either ::UNICAST_BINDING,
1845
+ * ::MULTICAST_BINDING, or ::UNICAST_MANY_TO_ONE_BINDING.
1846
+ * ::UNICAST_MANY_TO_ONE_BINDING is an Ember-specific extension
1847
+ * and should be used only when the target is an Ember device.
1848
+ * @param destination The destination EUI64 in the binding entry for the
1849
+ * ::UNICAST_BINDING or ::UNICAST_MANY_TO_ONE_BINDING.
1850
+ * @param groupAddress The group address for the ::MULTICAST_BINDING.
1851
+ * @param destinationEndpoint uint8_t The destination endpoint in the binding entry for
1852
+ * the ::UNICAST_BINDING or ::UNICAST_MANY_TO_ONE_BINDING.
1853
+ * @param options The options to use when sending the request. See
1854
+ * emberSendUnicast() for a description.
1855
+ *
1856
+ * @returns An ::EmberStatus value.
1857
+ * - ::EMBER_SUCCESS
1858
+ * - ::EMBER_NO_BUFFERS
1859
+ * - ::EMBER_NETWORK_DOWN
1860
+ * - ::EMBER_NETWORK_BUSY
1861
+ * @returns APS frame created for the request
1862
+ * @returns The tag used on the message.
1863
+ */
1864
+ async emberUnbindRequest(target, source, sourceEndpoint, clusterId, type, destination, groupAddress, destinationEndpoint, options) {
1865
+ debug(`~~~> [ZDO UNBIND target=${target} source=${source} sourceEndpoint=${sourceEndpoint} clusterId=${clusterId} type=${type} `
1866
+ + `destination=${destination} groupAddress=${groupAddress} destinationEndpoint=${destinationEndpoint}]`);
1867
+ return this.emberSendZigDevBindRequest(target, zdo_1.UNBIND_REQUEST, source, sourceEndpoint, clusterId, type, destination, groupAddress, destinationEndpoint, options);
1868
+ }
1869
+ /**
1870
+ * ZDO
1871
+ * Request the specified node to send a list of its active
1872
+ * endpoints. An active endpoint is one for which a simple descriptor is
1873
+ * available.
1874
+ *
1875
+ * @param target The node whose active endpoints are desired.
1876
+ * @param options The options to use when sending the request. See
1877
+ * emberSendUnicast() for a description.
1878
+ *
1879
+ * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS,
1880
+ * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY.
1881
+ */
1882
+ async emberActiveEndpointsRequest(target, options) {
1883
+ debug(`~~~> [ZDO ACTIVE ENDPOINTS target=${target}]`);
1884
+ return this.emberSendZigDevRequestTarget(target, zdo_1.ACTIVE_ENDPOINTS_REQUEST, options);
1885
+ }
1886
+ /**
1887
+ * ZDO
1888
+ * Request the specified node to send its power descriptor.
1889
+ * The power descriptor gives a dynamic indication of the power
1890
+ * status of the node. It describes current power mode,
1891
+ * available power sources, current power source and
1892
+ * current power source level. It is defined in the ZigBee
1893
+ * Application Framework Specification.
1894
+ *
1895
+ * @param target The node whose power descriptor is desired.
1896
+ * @param options The options to use when sending the request. See
1897
+ * emberSendUnicast() for a description.
1898
+ *
1899
+ * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS,
1900
+ * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY.
1901
+ */
1902
+ async emberPowerDescriptorRequest(target, options) {
1903
+ debug(`~~~> [ZDO POWER DESCRIPTOR target=${target}]`);
1904
+ return this.emberSendZigDevRequestTarget(target, zdo_1.POWER_DESCRIPTOR_REQUEST, options);
1905
+ }
1906
+ /**
1907
+ * ZDO
1908
+ * Request the specified node to send its node descriptor.
1909
+ * The node descriptor contains information about the capabilities of the ZigBee
1910
+ * node. It describes logical type, APS flags, frequency band, MAC capabilities
1911
+ * flags, manufacturer code and maximum buffer size. It is defined in the ZigBee
1912
+ * Application Framework Specification.
1913
+ *
1914
+ * @param target The node whose node descriptor is desired.
1915
+ * @param options The options to use when sending the request. See
1916
+ * emberSendUnicast() for a description.
1917
+ *
1918
+ * @return An ::EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS,
1919
+ * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY.
1920
+ */
1921
+ async emberNodeDescriptorRequest(target, options) {
1922
+ debug(`~~~> [ZDO NODE DESCRIPTOR target=${target}]`);
1923
+ return this.emberSendZigDevRequestTarget(target, zdo_1.NODE_DESCRIPTOR_REQUEST, options);
1924
+ }
1925
+ /**
1926
+ * ZDO
1927
+ * Request the specified node to send its LQI (neighbor) table.
1928
+ * The response gives PAN ID, EUI64, node ID and cost for each neighbor. The
1929
+ * EUI64 is only available if security is enabled. The other fields in the
1930
+ * response are set to zero. The response format is defined in the ZigBee Device
1931
+ * Profile Specification.
1932
+ *
1933
+ * @param target The node whose LQI table is desired.
1934
+ * @param startIndex uint8_t The index of the first neighbor to include in the
1935
+ * response.
1936
+ * @param options The options to use when sending the request. See
1937
+ * emberSendUnicast() for a description.
1938
+ *
1939
+ * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS,
1940
+ * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY.
1941
+ */
1942
+ async emberLqiTableRequest(target, startIndex, options) {
1943
+ debug(`~~~> [ZDO LQI TABLE target=${target} startIndex=${startIndex}]`);
1944
+ return this.emberTableRequest(zdo_1.LQI_TABLE_REQUEST, target, startIndex, options);
1945
+ }
1946
+ /**
1947
+ * ZDO
1948
+ * Request the specified node to send its routing table.
1949
+ * The response gives destination node ID, status and many-to-one flags,
1950
+ * and the next hop node ID.
1951
+ * The response format is defined in the ZigBee Device
1952
+ * Profile Specification.
1953
+ *
1954
+ * @param target The node whose routing table is desired.
1955
+ * @param startIndex uint8_t The index of the first route entry to include in the
1956
+ * response.
1957
+ * @param options The options to use when sending the request. See
1958
+ * emberSendUnicast() for a description.
1959
+ *
1960
+ * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS,
1961
+ * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY.
1962
+ */
1963
+ async emberRoutingTableRequest(target, startIndex, options) {
1964
+ debug(`~~~> [ZDO ROUTING TABLE target=${target} startIndex=${startIndex}]`);
1965
+ return this.emberTableRequest(zdo_1.ROUTING_TABLE_REQUEST, target, startIndex, options);
1966
+ }
1967
+ /**
1968
+ * ZDO
1969
+ * Request the specified node to send its nonvolatile bindings.
1970
+ * The response gives source address, source endpoint, cluster ID, destination
1971
+ * address and destination endpoint for each binding entry. The response format
1972
+ * is defined in the ZigBee Device Profile Specification.
1973
+ * Note that bindings that have the Ember-specific ::UNICAST_MANY_TO_ONE_BINDING
1974
+ * type are reported as having the standard ::UNICAST_BINDING type.
1975
+ *
1976
+ * @param target The node whose binding table is desired.
1977
+ * @param startIndex uint8_t The index of the first binding entry to include in the
1978
+ * response.
1979
+ * @param options The options to use when sending the request. See
1980
+ * emberSendUnicast() for a description.
1981
+ *
1982
+ * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS,
1983
+ * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY.
1984
+ */
1985
+ async emberBindingTableRequest(target, startIndex, options) {
1986
+ debug(`~~~> [ZDO BINDING TABLE target=${target} startIndex=${startIndex}]`);
1987
+ return this.emberTableRequest(zdo_1.BINDING_TABLE_REQUEST, target, startIndex, options);
1988
+ }
1989
+ /**
1990
+ * ZDO
1991
+ *
1992
+ * @param clusterId uint16_t
1993
+ * @param target
1994
+ * @param startIndex uint8_t
1995
+ * @param options
1996
+ * @returns
1997
+ */
1998
+ async emberTableRequest(clusterId, target, startIndex, options) {
1999
+ this.zdoRequestBuffalo.setPosition(zdo_1.ZDO_MESSAGE_OVERHEAD);
2000
+ this.zdoRequestBuffalo.writeUInt8(startIndex);
2001
+ return this.sendZDORequestBuffer(target, clusterId, options);
2002
+ }
2003
+ /**
2004
+ * ZDO
2005
+ * Request the specified node to remove the specified device from
2006
+ * the network. The device to be removed must be the node to which the request
2007
+ * is sent or one of its children.
2008
+ *
2009
+ * @param target The node which will remove the device.
2010
+ * @param deviceAddress All zeros if the target is to remove itself from
2011
+ * the network or the EUI64 of a child of the target device to remove
2012
+ * that child.
2013
+ * @param leaveRequestFlags uint8_t A bitmask of leave options.
2014
+ * Include ::AND_REJOIN if the target is to rejoin the network immediately after leaving.
2015
+ * @param options The options to use when sending the request. See
2016
+ * emberSendUnicast() for a description.
2017
+ *
2018
+ * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS,
2019
+ * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY.
2020
+ */
2021
+ async emberLeaveRequest(target, deviceAddress, leaveRequestFlags, options) {
2022
+ this.zdoRequestBuffalo.setPosition(zdo_1.ZDO_MESSAGE_OVERHEAD);
2023
+ this.zdoRequestBuffalo.writeIeeeAddr(deviceAddress);
2024
+ this.zdoRequestBuffalo.writeUInt8(leaveRequestFlags);
2025
+ debug(`~~~> [ZDO LEAVE target=${target} deviceAddress=${deviceAddress} leaveRequestFlags=${leaveRequestFlags}]`);
2026
+ return this.sendZDORequestBuffer(target, zdo_1.LEAVE_REQUEST, options);
2027
+ }
2028
+ /**
2029
+ * ZDO
2030
+ * Request the specified node to allow or disallow association.
2031
+ *
2032
+ * @param target The node which will allow or disallow association. The request
2033
+ * can be broadcast by using a broadcast address (0xFFFC/0xFFFD/0xFFFF). No
2034
+ * response is sent if the request is broadcast.
2035
+ * @param duration uint8_t A value of 0x00 disables joining. A value of 0xFF enables
2036
+ * joining. Any other value enables joining for that number of seconds.
2037
+ * @param authentication uint8_t Controls Trust Center authentication behavior.
2038
+ * @param options The options to use when sending the request. See
2039
+ * emberSendUnicast() for a description. This parameter is ignored if the target
2040
+ * is a broadcast address.
2041
+ *
2042
+ * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS,
2043
+ * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY.
2044
+ */
2045
+ async emberPermitJoiningRequest(target, duration, authentication, options) {
2046
+ this.zdoRequestBuffalo.setPosition(zdo_1.ZDO_MESSAGE_OVERHEAD);
2047
+ this.zdoRequestBuffalo.writeUInt8(duration);
2048
+ this.zdoRequestBuffalo.writeUInt8(authentication);
2049
+ debug(`~~~> [ZDO PERMIT JOINING target=${target} duration=${duration} authentication=${authentication}]`);
2050
+ return this.sendZDORequestBuffer(target, zdo_1.PERMIT_JOINING_REQUEST, options);
2051
+ }
2052
+ //---- END Ember ZDO
2053
+ //-- START Adapter implementation
2054
+ static async isValidPath(path) {
2055
+ // For TCP paths we cannot get device information, therefore we cannot validate it.
2056
+ if (socketPortUtils_1.default.isTcpPath(path)) {
2057
+ return false;
2058
+ }
2059
+ try {
2060
+ return serialPortUtils_1.default.is((0, utils_1.RealpathSync)(path), autoDetectDefinitions);
2061
+ }
2062
+ catch (error) {
2063
+ debug(`Failed to determine if path is valid: '${error}'`);
2064
+ return false;
2065
+ }
2066
+ }
2067
+ static async autoDetectPath() {
2068
+ const paths = await serialPortUtils_1.default.find(autoDetectDefinitions);
2069
+ paths.sort((a, b) => (a < b) ? -1 : 1);
2070
+ return paths.length > 0 ? paths[0] : null;
2071
+ }
2072
+ async start() {
2073
+ console.log(`======== Ember Adapter Starting ========`);
2074
+ this.initVariables();
2075
+ debug(`Starting EZSP with stack configuration: "${this.stackConfig}".`);
2076
+ const result = await this.initEzsp();
2077
+ return result;
2078
+ }
2079
+ async stop() {
2080
+ await this.ezsp.stop();
2081
+ this.initVariables();
2082
+ console.log(`======== Ember Adapter Stopped ========`);
2083
+ }
2084
+ // queued, non-InterPAN
2085
+ async getCoordinator() {
2086
+ return new Promise((resolve, reject) => {
2087
+ this.requestQueue.enqueue(async () => {
2088
+ this.checkInterpanLock();
2089
+ // in all likelihood this will be retrieved from cache
2090
+ const ieeeAddr = (await this.emberGetEui64());
2091
+ resolve({
2092
+ ieeeAddr,
2093
+ networkAddress: consts_2.ZIGBEE_COORDINATOR_ADDRESS,
2094
+ manufacturerID: consts_2.MANUFACTURER_CODE,
2095
+ endpoints: endpoints_1.FIXED_ENDPOINTS.map((ep) => {
2096
+ return {
2097
+ profileID: ep.profileId,
2098
+ ID: ep.endpoint,
2099
+ deviceID: ep.deviceId,
2100
+ inputClusters: ep.inClusterList,
2101
+ outputClusters: ep.outClusterList,
2102
+ };
2103
+ }),
2104
+ });
2105
+ return enums_2.EmberStatus.SUCCESS;
2106
+ }, reject);
2107
+ });
2108
+ }
2109
+ async getCoordinatorVersion() {
2110
+ return { type: `EZSP v${this.version.ezsp}`, meta: this.version };
2111
+ }
2112
+ // queued
2113
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2114
+ async reset(type) {
2115
+ return Promise.reject(new Error("Not supported"));
2116
+ // NOTE: although this function is legacy atm, a couple of new untested EZSP functions that could also prove useful:
2117
+ // this.ezsp.ezspTokenFactoryReset(true/*excludeOutgoingFC*/, true/*excludeBootCounter*/);
2118
+ // this.ezsp.ezspResetNode()
2119
+ }
2120
+ async supportsBackup() {
2121
+ return true;
2122
+ }
2123
+ // queued
2124
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2125
+ async backup(ieeeAddressesInDatabase) {
2126
+ return new Promise((resolve, reject) => {
2127
+ this.requestQueue.enqueue(async () => {
2128
+ // grab fresh version here, bypass cache
2129
+ const [netStatus, , netParams] = (await this.ezsp.ezspGetNetworkParameters());
2130
+ if (netStatus !== enums_2.EmberStatus.SUCCESS) {
2131
+ console.error(`[BACKUP] Failed to get network parameters.`);
2132
+ return netStatus;
2133
+ }
2134
+ // update cache
2135
+ this.networkCache.parameters = netParams;
2136
+ this.networkCache.eui64 = (await this.ezsp.ezspGetEui64());
2137
+ const [netKeyStatus, netKeyInfo] = (await this.ezsp.ezspGetNetworkKeyInfo());
2138
+ if (netKeyStatus !== enums_2.SLStatus.OK) {
2139
+ console.error(`[BACKUP] Failed to get network keys info.`);
2140
+ return ((netKeyStatus === enums_2.SLStatus.BUSY) || (netKeyStatus === enums_2.SLStatus.NOT_READY))
2141
+ ? enums_2.EmberStatus.NETWORK_BUSY : enums_2.EmberStatus.ERR_FATAL; // allow retry on statuses that should be temporary
2142
+ }
2143
+ if (!netKeyInfo.networkKeySet) {
2144
+ throw new Error(`[BACKUP] No network key set.`);
2145
+ }
2146
+ let keyList = [];
2147
+ if (STACK_CONFIGS[this.stackConfig].KEY_TABLE_SIZE) {
2148
+ keyList = (await this.exportLinkKeys());
2149
+ }
2150
+ // XXX: this only makes sense on stop (if that), not hourly/on start, plus network needs to be at near-standstill @see AN1387
2151
+ // const tokensBuf = (await EmberTokensManager.saveTokens(
2152
+ // this.ezsp,
2153
+ // Buffer.from(this.networkCache.eui64.substring(2/*0x*/), 'hex').reverse()
2154
+ // ));
2155
+ // console.log(tokensBuf.toString('hex'));
2156
+ let context = (0, initters_1.initSecurityManagerContext)();
2157
+ context.coreKeyType = enums_2.SecManKeyType.TC_LINK;
2158
+ const [tcLinkKey, tclkStatus] = (await this.ezsp.ezspExportKey(context));
2159
+ if (tclkStatus !== enums_2.SLStatus.OK) {
2160
+ throw new Error(`[BACKUP] Failed to export TC Link Key with status=${enums_2.SLStatus[tclkStatus]}.`);
2161
+ }
2162
+ context = (0, initters_1.initSecurityManagerContext)(); // make sure it's back to zeroes
2163
+ context.coreKeyType = enums_2.SecManKeyType.NETWORK;
2164
+ context.keyIndex = 0;
2165
+ const [networkKey, nkStatus] = (await this.ezsp.ezspExportKey(context));
2166
+ if (nkStatus !== enums_2.SLStatus.OK) {
2167
+ throw new Error(`[BACKUP] Failed to export Network Key with status=${enums_2.SLStatus[nkStatus]}.`);
2168
+ }
2169
+ const zbChannels = Array.from(Array(consts_2.EMBER_NUM_802_15_4_CHANNELS), (e, i) => i + consts_2.EMBER_MIN_802_15_4_CHANNEL_NUMBER);
2170
+ resolve({
2171
+ networkOptions: {
2172
+ panId: netParams.panId, // uint16_t
2173
+ extendedPanId: Buffer.from(netParams.extendedPanId),
2174
+ channelList: zbChannels.map((c) => ((2 ** c) & netParams.channels) ? c : null).filter((x) => x),
2175
+ networkKey: networkKey.contents,
2176
+ networkKeyDistribute: false,
2177
+ },
2178
+ logicalChannel: netParams.radioChannel,
2179
+ networkKeyInfo: {
2180
+ sequenceNumber: netKeyInfo.networkKeySequenceNumber,
2181
+ frameCounter: netKeyInfo.networkKeyFrameCounter,
2182
+ },
2183
+ securityLevel: STACK_CONFIGS[this.stackConfig].SECURITY_LEVEL,
2184
+ networkUpdateId: netParams.nwkUpdateId,
2185
+ coordinatorIeeeAddress: Buffer.from(this.networkCache.eui64.substring(2) /*take out 0x*/, 'hex').reverse(),
2186
+ devices: keyList.map((key) => ({
2187
+ networkAddress: null, // not used for restore, no reason to make NCP calls for nothing
2188
+ ieeeAddress: Buffer.from(key.deviceEui64.substring(2) /*take out 0x*/, 'hex').reverse(),
2189
+ isDirectChild: false, // not used
2190
+ linkKey: {
2191
+ key: key.key.contents,
2192
+ rxCounter: key.incomingFrameCounter,
2193
+ txCounter: key.outgoingFrameCounter,
2194
+ },
2195
+ })),
2196
+ ezsp: {
2197
+ version: this.version.ezsp,
2198
+ hashed_tclk: tcLinkKey.contents,
2199
+ // tokens: tokensBuf.toString('hex'),
2200
+ // altNetworkKey: altNetworkKey.contents,
2201
+ }
2202
+ });
2203
+ return enums_2.EmberStatus.SUCCESS;
2204
+ }, reject, true);
2205
+ });
2206
+ }
2207
+ // queued, non-InterPAN
2208
+ async getNetworkParameters() {
2209
+ return new Promise((resolve, reject) => {
2210
+ this.requestQueue.enqueue(async () => {
2211
+ this.checkInterpanLock();
2212
+ // first call will cache for the others, but in all likelihood, it will all be from freshly cached after init
2213
+ // since Controller caches this also.
2214
+ const panID = (await this.emberGetPanId());
2215
+ const extendedPanID = (await this.emberGetExtendedPanId());
2216
+ const channel = (await this.emberGetRadioChannel());
2217
+ resolve({
2218
+ panID: panID,
2219
+ extendedPanID: parseInt(Buffer.from(extendedPanID).toString('hex'), 16),
2220
+ channel: channel,
2221
+ });
2222
+ return enums_2.EmberStatus.SUCCESS;
2223
+ }, reject);
2224
+ });
2225
+ }
2226
+ // queued
2227
+ async setTransmitPower(value) {
2228
+ if (typeof value !== 'number') {
2229
+ console.error(`Tried to set transmit power to non-number. Value ${value} of type ${typeof value}.`);
2230
+ return;
2231
+ }
2232
+ return new Promise((resolve, reject) => {
2233
+ this.requestQueue.enqueue(async () => {
2234
+ const status = await this.ezsp.ezspSetRadioPower(value);
2235
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2236
+ console.error(`Failed to set transmit power to ${value} status=${enums_2.EmberStatus[status]}.`);
2237
+ return status;
2238
+ }
2239
+ resolve();
2240
+ return enums_2.EmberStatus.SUCCESS;
2241
+ }, reject);
2242
+ });
2243
+ }
2244
+ // queued
2245
+ async addInstallCode(ieeeAddress, key) {
2246
+ if (!key) {
2247
+ throw new Error(`[ADD INSTALL CODE] Failed for "${ieeeAddress}"; no code given.`);
2248
+ }
2249
+ if (consts_2.EMBER_INSTALL_CODE_SIZES.indexOf(key.length) === -1) {
2250
+ throw new Error(`[ADD INSTALL CODE] Failed for "${ieeeAddress}"; invalid code size.`);
2251
+ }
2252
+ // Reverse the bits in a byte (uint8_t)
2253
+ const reverse = (b) => {
2254
+ return (((b * 0x0802 & 0x22110) | (b * 0x8020 & 0x88440)) * 0x10101 >> 16) & 0xFF;
2255
+ };
2256
+ let crc = 0xFFFF; // uint16_t
2257
+ // Compute the CRC and verify that it matches.
2258
+ // The bit reversals, byte swap, and ones' complement are due to differences between halCommonCrc16 and the Smart Energy version.
2259
+ for (let index = 0; index < (key.length - consts_2.EMBER_INSTALL_CODE_CRC_SIZE); index++) {
2260
+ crc = (0, math_1.halCommonCrc16)(reverse(key[index]), crc);
2261
+ }
2262
+ crc = (~(0, math_1.highLowToInt)(reverse((0, math_1.lowByte)(crc)), reverse((0, math_1.highByte)(crc)))) & 0xFFFF;
2263
+ if (key[key.length - consts_2.EMBER_INSTALL_CODE_CRC_SIZE] !== (0, math_1.lowByte)(crc) || key[key.length - consts_2.EMBER_INSTALL_CODE_CRC_SIZE + 1] !== (0, math_1.highByte)(crc)) {
2264
+ throw new Error(`[ADD INSTALL CODE] Failed for "${ieeeAddress}"; invalid code CRC.`);
2265
+ }
2266
+ return new Promise((resolve, reject) => {
2267
+ this.requestQueue.enqueue(async () => {
2268
+ // Compute the key from the install code and CRC.
2269
+ const [aesStatus, keyContents] = (await this.emberAesHashSimple(key));
2270
+ if (aesStatus !== enums_2.EmberStatus.SUCCESS) {
2271
+ console.error(`[ADD INSTALL CODE] Failed AES hash for "${ieeeAddress}" with status=${enums_2.EmberStatus[aesStatus]}.`);
2272
+ return aesStatus;
2273
+ }
2274
+ // Add the key to the transient key table.
2275
+ // This will be used while the DUT joins.
2276
+ const impStatus = (await this.ezsp.ezspImportTransientKey(ieeeAddress, { contents: keyContents }, enums_2.SecManFlag.NONE));
2277
+ if (impStatus == enums_2.SLStatus.OK) {
2278
+ debug(`[ADD INSTALL CODE] Success for "${ieeeAddress}".`);
2279
+ }
2280
+ else {
2281
+ console.error(`[ADD INSTALL CODE] Failed for "${ieeeAddress}" with status=${enums_2.SLStatus[impStatus]}.`);
2282
+ return enums_2.EmberStatus.ERR_FATAL;
2283
+ }
2284
+ resolve();
2285
+ return enums_2.EmberStatus.SUCCESS;
2286
+ }, reject);
2287
+ });
2288
+ }
2289
+ /** WARNING: Adapter impl. Starts timer immediately upon returning */
2290
+ waitFor(networkAddress, endpoint, frameType, direction, transactionSequenceNumber, clusterID, commandIdentifier, timeout) {
2291
+ const sourceEndpointInfo = endpoints_1.FIXED_ENDPOINTS[0];
2292
+ const waiter = this.oneWaitress.waitFor({
2293
+ target: networkAddress,
2294
+ apsFrame: {
2295
+ clusterId: clusterID,
2296
+ profileId: sourceEndpointInfo.profileId, // XXX: only used by OTA upstream
2297
+ sequence: 0, // set by stack
2298
+ sourceEndpoint: sourceEndpointInfo.endpoint,
2299
+ destinationEndpoint: endpoint,
2300
+ groupId: 0,
2301
+ options: enums_2.EmberApsOption.NONE,
2302
+ },
2303
+ zclSequence: transactionSequenceNumber,
2304
+ commandIdentifier,
2305
+ }, timeout || DEFAULT_ZCL_REQUEST_TIMEOUT * 3); // XXX: since this is used by OTA...
2306
+ return {
2307
+ cancel: () => this.oneWaitress.remove(waiter.id),
2308
+ promise: waiter.start().promise,
2309
+ };
2310
+ }
2311
+ //---- ZDO
2312
+ // queued, non-InterPAN
2313
+ async permitJoin(seconds, networkAddress) {
2314
+ const preJoining = async () => {
2315
+ if (seconds) {
2316
+ const plaintextKey = { contents: Buffer.from(consts_2.ZIGBEE_PROFILE_INTEROPERABILITY_LINK_KEY) };
2317
+ const impKeyStatus = (await this.ezsp.ezspImportTransientKey(consts_2.BLANK_EUI64, plaintextKey, enums_2.SecManFlag.NONE));
2318
+ debug(`[ZDO] Pre joining import transient key status=${enums_2.SLStatus[impKeyStatus]}.`);
2319
+ return impKeyStatus === enums_2.SLStatus.OK ? enums_2.EmberStatus.SUCCESS : enums_2.EmberStatus.ERR_FATAL;
2320
+ }
2321
+ else {
2322
+ await this.ezsp.ezspClearTransientLinkKeys();
2323
+ const setJPstatus = (await this.emberSetJoinPolicy(enums_2.EmberJoinDecision.ALLOW_REJOINS_ONLY));
2324
+ if (setJPstatus !== enums_2.EzspStatus.SUCCESS) {
2325
+ console.error(`[ZDO] Failed set join policy for with status=${enums_2.EzspStatus[setJPstatus]}.`);
2326
+ return enums_2.EmberStatus.ERR_FATAL;
2327
+ }
2328
+ return enums_2.EmberStatus.SUCCESS;
2329
+ }
2330
+ };
2331
+ // NOTE: can't ZDO PJ on coordinator, so if network address is null or zero (coordinator), using local permit join
2332
+ if (networkAddress) {
2333
+ return new Promise((resolve, reject) => {
2334
+ this.requestQueue.enqueue(async () => {
2335
+ this.checkInterpanLock();
2336
+ const pjStatus = (await preJoining());
2337
+ if (pjStatus !== enums_2.EmberStatus.SUCCESS) {
2338
+ console.error(`[ZDO] Failed pre joining request for "${networkAddress}" with status=${enums_2.EmberStatus[pjStatus]}.`);
2339
+ return pjStatus;
2340
+ }
2341
+ // `authentication`: TC significance always 1 (zb specs)
2342
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2343
+ const [status, apsFrame, messageTag] = (await this.emberPermitJoiningRequest(networkAddress, seconds, 1, 0));
2344
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2345
+ console.error(`[ZDO] Failed permit joining request for "${networkAddress}" with status=${enums_2.EmberStatus[status]}.`);
2346
+ return status;
2347
+ }
2348
+ (await this.oneWaitress.startWaitingFor({
2349
+ target: networkAddress,
2350
+ apsFrame,
2351
+ responseClusterId: zdo_1.PERMIT_JOINING_RESPONSE,
2352
+ }, DEFAULT_ZDO_REQUEST_TIMEOUT));
2353
+ resolve();
2354
+ return enums_2.EmberStatus.SUCCESS;
2355
+ }, reject);
2356
+ });
2357
+ }
2358
+ else {
2359
+ // no device specified to open, open coordinator + broadcast
2360
+ return new Promise((resolve, reject) => {
2361
+ this.requestQueue.enqueue(async () => {
2362
+ this.checkInterpanLock();
2363
+ const pjStatus = (await preJoining());
2364
+ if (pjStatus !== enums_2.EmberStatus.SUCCESS) {
2365
+ console.error(`[ZDO] Failed pre joining request for "${networkAddress}" with status=${enums_2.EmberStatus[pjStatus]}.`);
2366
+ return pjStatus;
2367
+ }
2368
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2369
+ const [status, apsFrame, messageTag] = (await this.emberPermitJoining(seconds, true /*broadcast*/));
2370
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2371
+ console.error(`[ZDO] Failed permit joining request with status=${enums_2.EmberStatus[status]}.`);
2372
+ return status;
2373
+ }
2374
+ // NOTE: because Z2M is refreshing the permit join duration early to prevent it from closing
2375
+ // (every 200sec, even if only opened for 254sec), we can't wait for the stack opened status,
2376
+ // as it won't trigger again if already opened... so instead we assume it worked
2377
+ // NOTE2: with EZSP, 255=forever, and 254=max, but since upstream logic uses fixed 254 with interval refresh,
2378
+ // we can't simply bypass upstream calls if called for "forever" to prevent useless NCP calls (3-4 each time),
2379
+ // until called with 0 (disable), since we don't know if it was requested for forever or not...
2380
+ // TLDR: upstream logic change required to allow this
2381
+ // if (seconds) {
2382
+ // await this.oneWaitress.startWaitingForEvent(
2383
+ // {eventName: OneWaitressEvents.STACK_STATUS_NETWORK_OPENED},
2384
+ // DEFAULT_ZCL_REQUEST_TIMEOUT,
2385
+ // '[ZDO] Permit Joining',
2386
+ // );
2387
+ // } else {
2388
+ // // NOTE: CLOSED stack status is not triggered if the network was not OPENED in the first place, so don't wait for it
2389
+ // // same kind of problem as described above (upstream always tries to close after start, but EZSP already is)
2390
+ // }
2391
+ resolve();
2392
+ return enums_2.EmberStatus.SUCCESS;
2393
+ }, reject);
2394
+ });
2395
+ }
2396
+ }
2397
+ // queued, non-InterPAN
2398
+ async lqi(networkAddress) {
2399
+ const neighbors = [];
2400
+ const request = async (startIndex) => {
2401
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2402
+ const [reqStatus, apsFrame, messageTag] = (await this.emberLqiTableRequest(networkAddress, startIndex, this.defaultApsOptions));
2403
+ if (reqStatus !== enums_2.EmberStatus.SUCCESS) {
2404
+ console.error(`[ZDO] Failed LQI request for "${networkAddress}" (index "${startIndex}") with status=${enums_2.EmberStatus[reqStatus]}.`);
2405
+ return [reqStatus, null, null];
2406
+ }
2407
+ const result = (await this.oneWaitress.startWaitingFor({
2408
+ target: networkAddress,
2409
+ apsFrame,
2410
+ responseClusterId: zdo_1.LQI_TABLE_RESPONSE,
2411
+ }, DEFAULT_ZDO_REQUEST_TIMEOUT));
2412
+ for (const entry of result.entryList) {
2413
+ neighbors.push({
2414
+ ieeeAddr: entry.eui64,
2415
+ networkAddress: entry.nodeId,
2416
+ linkquality: entry.lqi,
2417
+ relationship: entry.relationship,
2418
+ depth: entry.depth,
2419
+ });
2420
+ }
2421
+ return [enums_2.EmberStatus.SUCCESS, result.neighborTableEntries, result.entryList.length];
2422
+ };
2423
+ return new Promise((resolve, reject) => {
2424
+ this.requestQueue.enqueue(async () => {
2425
+ this.checkInterpanLock();
2426
+ let [status, tableEntries, entryCount] = (await request(0));
2427
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2428
+ return status;
2429
+ }
2430
+ const size = tableEntries;
2431
+ let nextStartIndex = entryCount;
2432
+ while (neighbors.length < size) {
2433
+ [status, tableEntries, entryCount] = (await request(nextStartIndex));
2434
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2435
+ return status;
2436
+ }
2437
+ nextStartIndex += entryCount;
2438
+ }
2439
+ resolve({ neighbors });
2440
+ return status;
2441
+ }, reject);
2442
+ });
2443
+ }
2444
+ // queued, non-InterPAN
2445
+ async routingTable(networkAddress) {
2446
+ const table = [];
2447
+ const request = async (startIndex) => {
2448
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2449
+ const [reqStatus, apsFrame, messageTag] = (await this.emberRoutingTableRequest(networkAddress, startIndex, this.defaultApsOptions));
2450
+ if (reqStatus !== enums_2.EmberStatus.SUCCESS) {
2451
+ console.error(`[ZDO] Failed routing table request for "${networkAddress}" (index "${startIndex}") with status=${enums_2.EmberStatus[reqStatus]}.`);
2452
+ return [reqStatus, null, null];
2453
+ }
2454
+ const result = (await this.oneWaitress.startWaitingFor({
2455
+ target: networkAddress,
2456
+ apsFrame,
2457
+ responseClusterId: zdo_1.ROUTING_TABLE_RESPONSE,
2458
+ }, DEFAULT_ZDO_REQUEST_TIMEOUT));
2459
+ for (const entry of result.entryList) {
2460
+ table.push({
2461
+ destinationAddress: entry.destinationAddress,
2462
+ status: RoutingTableStatus[entry.status], // get str value from enum to satisfy upstream's needs
2463
+ nextHop: entry.nextHopAddress,
2464
+ });
2465
+ }
2466
+ return [enums_2.EmberStatus.SUCCESS, result.routingTableEntries, result.entryList.length];
2467
+ };
2468
+ return new Promise((resolve, reject) => {
2469
+ this.requestQueue.enqueue(async () => {
2470
+ this.checkInterpanLock();
2471
+ let [status, tableEntries, entryCount] = (await request(0));
2472
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2473
+ return status;
2474
+ }
2475
+ const size = tableEntries;
2476
+ let nextStartIndex = entryCount;
2477
+ while (table.length < size) {
2478
+ [status, tableEntries, entryCount] = (await request(nextStartIndex));
2479
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2480
+ return status;
2481
+ }
2482
+ nextStartIndex += entryCount;
2483
+ }
2484
+ resolve({ table });
2485
+ return enums_2.EmberStatus.SUCCESS;
2486
+ }, reject);
2487
+ });
2488
+ }
2489
+ // queued, non-InterPAN
2490
+ async nodeDescriptor(networkAddress) {
2491
+ return new Promise((resolve, reject) => {
2492
+ this.requestQueue.enqueue(async () => {
2493
+ this.checkInterpanLock();
2494
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2495
+ const [status, apsFrame, messageTag] = (await this.emberNodeDescriptorRequest(networkAddress, this.defaultApsOptions));
2496
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2497
+ console.error(`[ZDO] Failed node descriptor for "${networkAddress}" with status=${enums_2.EmberStatus[status]}.`);
2498
+ return status;
2499
+ }
2500
+ const result = (await this.oneWaitress.startWaitingFor({
2501
+ target: networkAddress,
2502
+ apsFrame,
2503
+ responseClusterId: zdo_1.NODE_DESCRIPTOR_RESPONSE,
2504
+ }, DEFAULT_ZDO_REQUEST_TIMEOUT));
2505
+ let type = 'Unknown';
2506
+ switch (result.logicalType) {
2507
+ case 0x0:
2508
+ type = 'Coordinator';
2509
+ break;
2510
+ case 0x1:
2511
+ type = 'Router';
2512
+ break;
2513
+ case 0x2:
2514
+ type = 'EndDevice';
2515
+ break;
2516
+ }
2517
+ // always 0 before rev. 21 where field was added
2518
+ if (result.stackRevision < CURRENT_ZIGBEE_SPEC_REVISION) {
2519
+ console.warn(`[ZDO] Node descriptor for "${networkAddress}" reports device is only compliant to revision `
2520
+ + `"${(result.stackRevision < 21) ? 'pre-21' : result.stackRevision}" of the ZigBee specification `
2521
+ + `(current revision: ${CURRENT_ZIGBEE_SPEC_REVISION}).`);
2522
+ }
2523
+ resolve({ type, manufacturerCode: result.manufacturerCode });
2524
+ return enums_2.EmberStatus.SUCCESS;
2525
+ }, reject);
2526
+ });
2527
+ }
2528
+ // queued, non-InterPAN
2529
+ async activeEndpoints(networkAddress) {
2530
+ return new Promise((resolve, reject) => {
2531
+ this.requestQueue.enqueue(async () => {
2532
+ this.checkInterpanLock();
2533
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2534
+ const [status, apsFrame, messageTag] = (await this.emberActiveEndpointsRequest(networkAddress, this.defaultApsOptions));
2535
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2536
+ console.error(`[ZDO] Failed active endpoints request for "${networkAddress}" with status=${enums_2.EmberStatus[status]}.`);
2537
+ return status;
2538
+ }
2539
+ const result = (await this.oneWaitress.startWaitingFor({
2540
+ target: networkAddress,
2541
+ apsFrame,
2542
+ responseClusterId: zdo_1.ACTIVE_ENDPOINTS_RESPONSE,
2543
+ }, DEFAULT_ZDO_REQUEST_TIMEOUT));
2544
+ resolve({ endpoints: result.endpointList });
2545
+ return enums_2.EmberStatus.SUCCESS;
2546
+ }, reject);
2547
+ });
2548
+ }
2549
+ // queued, non-InterPAN
2550
+ async simpleDescriptor(networkAddress, endpointID) {
2551
+ return new Promise((resolve, reject) => {
2552
+ this.requestQueue.enqueue(async () => {
2553
+ this.checkInterpanLock();
2554
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2555
+ const [status, apsFrame, messageTag] = (await this.emberSimpleDescriptorRequest(networkAddress, endpointID, this.defaultApsOptions));
2556
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2557
+ console.error(`[ZDO] Failed simple descriptor request for "${networkAddress}" endpoint "${endpointID}" `
2558
+ + `with status=${enums_2.EmberStatus[status]}.`);
2559
+ return status;
2560
+ }
2561
+ const result = (await this.oneWaitress.startWaitingFor({
2562
+ target: networkAddress,
2563
+ apsFrame,
2564
+ responseClusterId: zdo_1.SIMPLE_DESCRIPTOR_RESPONSE,
2565
+ }, DEFAULT_ZDO_REQUEST_TIMEOUT));
2566
+ resolve({
2567
+ profileID: result.profileId,
2568
+ endpointID: result.endpoint,
2569
+ deviceID: result.deviceId,
2570
+ inputClusters: result.inClusterList,
2571
+ outputClusters: result.outClusterList,
2572
+ });
2573
+ return enums_2.EmberStatus.SUCCESS;
2574
+ }, reject);
2575
+ });
2576
+ }
2577
+ // queued, non-InterPAN
2578
+ async bind(destinationNetworkAddress, sourceIeeeAddress, sourceEndpoint, clusterID, destinationAddressOrGroup, type, destinationEndpoint) {
2579
+ if (typeof destinationAddressOrGroup === 'string' && type === 'endpoint') {
2580
+ // dest address is EUI64 (str), so type should always be endpoint (unicast)
2581
+ return new Promise((resolve, reject) => {
2582
+ this.requestQueue.enqueue(async () => {
2583
+ this.checkInterpanLock();
2584
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2585
+ const [status, apsFrame, messageTag] = (await this.emberBindRequest(destinationNetworkAddress, sourceIeeeAddress, sourceEndpoint, clusterID, zdo_1.UNICAST_BINDING, destinationAddressOrGroup, null, // doesn't matter
2586
+ destinationEndpoint, this.defaultApsOptions));
2587
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2588
+ console.error(`[ZDO] Failed bind request for "${destinationNetworkAddress}" destination "${destinationAddressOrGroup}" `
2589
+ + `endpoint "${destinationEndpoint}" with status=${enums_2.EmberStatus[status]}.`);
2590
+ return status;
2591
+ }
2592
+ await this.oneWaitress.startWaitingFor({
2593
+ target: destinationNetworkAddress,
2594
+ apsFrame,
2595
+ responseClusterId: zdo_1.BIND_RESPONSE,
2596
+ }, DEFAULT_ZDO_REQUEST_TIMEOUT);
2597
+ resolve();
2598
+ return enums_2.EmberStatus.SUCCESS;
2599
+ }, reject);
2600
+ });
2601
+ }
2602
+ else if (typeof destinationAddressOrGroup === 'number' && type === 'group') {
2603
+ // dest is group num, so type should always be group (multicast)
2604
+ return new Promise((resolve, reject) => {
2605
+ this.requestQueue.enqueue(async () => {
2606
+ this.checkInterpanLock();
2607
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2608
+ const [status, apsFrame, messageTag] = (await this.emberBindRequest(destinationNetworkAddress, sourceIeeeAddress, sourceEndpoint, clusterID, zdo_1.MULTICAST_BINDING, null, // doesn't matter
2609
+ destinationAddressOrGroup, destinationEndpoint, // doesn't matter
2610
+ this.defaultApsOptions));
2611
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2612
+ console.error(`[ZDO] Failed bind request for "${destinationNetworkAddress}" group "${destinationAddressOrGroup}" `
2613
+ + `with status=${enums_2.EmberStatus[status]}.`);
2614
+ return status;
2615
+ }
2616
+ await this.oneWaitress.startWaitingFor({
2617
+ target: destinationNetworkAddress,
2618
+ apsFrame,
2619
+ responseClusterId: zdo_1.BIND_RESPONSE,
2620
+ }, DEFAULT_ZDO_REQUEST_TIMEOUT);
2621
+ resolve();
2622
+ return enums_2.EmberStatus.SUCCESS;
2623
+ }, reject);
2624
+ });
2625
+ }
2626
+ }
2627
+ // queued, non-InterPAN
2628
+ async unbind(destinationNetworkAddress, sourceIeeeAddress, sourceEndpoint, clusterID, destinationAddressOrGroup, type, destinationEndpoint) {
2629
+ if (typeof destinationAddressOrGroup === 'string' && type === 'endpoint') {
2630
+ // dest address is EUI64 (str), so type should always be endpoint (unicast)
2631
+ return new Promise((resolve, reject) => {
2632
+ this.requestQueue.enqueue(async () => {
2633
+ this.checkInterpanLock();
2634
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2635
+ const [status, apsFrame, messageTag] = (await this.emberUnbindRequest(destinationNetworkAddress, sourceIeeeAddress, sourceEndpoint, clusterID, zdo_1.UNICAST_BINDING, destinationAddressOrGroup, null, // doesn't matter
2636
+ destinationEndpoint, this.defaultApsOptions));
2637
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2638
+ console.error(`[ZDO] Failed unbind request for "${destinationNetworkAddress}" destination "${destinationAddressOrGroup}" `
2639
+ + `endpoint "${destinationEndpoint}" with status=${enums_2.EmberStatus[status]}.`);
2640
+ return status;
2641
+ }
2642
+ await this.oneWaitress.startWaitingFor({
2643
+ target: destinationNetworkAddress,
2644
+ apsFrame,
2645
+ responseClusterId: zdo_1.UNBIND_RESPONSE,
2646
+ }, DEFAULT_ZDO_REQUEST_TIMEOUT);
2647
+ resolve();
2648
+ return enums_2.EmberStatus.SUCCESS;
2649
+ }, reject);
2650
+ });
2651
+ }
2652
+ else if (typeof destinationAddressOrGroup === 'number' && type === 'group') {
2653
+ // dest is group num, so type should always be group (multicast)
2654
+ return new Promise((resolve, reject) => {
2655
+ this.requestQueue.enqueue(async () => {
2656
+ this.checkInterpanLock();
2657
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2658
+ const [status, apsFrame, messageTag] = (await this.emberUnbindRequest(destinationNetworkAddress, sourceIeeeAddress, sourceEndpoint, clusterID, zdo_1.MULTICAST_BINDING, null, // doesn't matter
2659
+ destinationAddressOrGroup, destinationEndpoint, // doesn't matter
2660
+ this.defaultApsOptions));
2661
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2662
+ console.error(`[ZDO] Failed unbind request for "${destinationNetworkAddress}" group "${destinationAddressOrGroup}" `
2663
+ + `with status=${enums_2.EmberStatus[status]}.`);
2664
+ return status;
2665
+ }
2666
+ await this.oneWaitress.startWaitingFor({
2667
+ target: destinationNetworkAddress,
2668
+ apsFrame,
2669
+ responseClusterId: zdo_1.UNBIND_RESPONSE,
2670
+ }, DEFAULT_ZDO_REQUEST_TIMEOUT);
2671
+ resolve();
2672
+ return enums_2.EmberStatus.SUCCESS;
2673
+ }, reject);
2674
+ });
2675
+ }
2676
+ }
2677
+ // queued, non-InterPAN
2678
+ async removeDevice(networkAddress, ieeeAddr) {
2679
+ return new Promise((resolve, reject) => {
2680
+ this.requestQueue.enqueue(async () => {
2681
+ this.checkInterpanLock();
2682
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2683
+ const [status, apsFrame, messageTag] = (await this.emberLeaveRequest(networkAddress, ieeeAddr, enums_2.EmberLeaveRequestFlags.WITHOUT_REJOIN, this.defaultApsOptions));
2684
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2685
+ console.error(`[ZDO] Failed remove device request for "${networkAddress}" target "${ieeeAddr}" `
2686
+ + `with status=${enums_2.EmberStatus[status]}.`);
2687
+ return status;
2688
+ }
2689
+ await this.oneWaitress.startWaitingFor({
2690
+ target: networkAddress,
2691
+ apsFrame,
2692
+ responseClusterId: zdo_1.LEAVE_RESPONSE,
2693
+ }, DEFAULT_ZDO_REQUEST_TIMEOUT);
2694
+ resolve();
2695
+ return enums_2.EmberStatus.SUCCESS;
2696
+ }, reject);
2697
+ });
2698
+ }
2699
+ //---- ZCL
2700
+ // queued, non-InterPAN
2701
+ async sendZclFrameToEndpoint(ieeeAddr, networkAddress, endpoint, zclFrame, timeout, disableResponse, disableRecovery, sourceEndpoint) {
2702
+ const sourceEndpointInfo = typeof sourceEndpoint === 'number' ?
2703
+ endpoints_1.FIXED_ENDPOINTS.find((epi) => (epi.endpoint === sourceEndpoint)) : endpoints_1.FIXED_ENDPOINTS[0];
2704
+ const command = zclFrame.getCommand();
2705
+ let commandResponseId = null;
2706
+ if (command.hasOwnProperty('response') && disableResponse === false) {
2707
+ commandResponseId = command.response;
2708
+ }
2709
+ else if (!zclFrame.Header.frameControl.disableDefaultResponse) {
2710
+ commandResponseId = zcl_1.Foundation.defaultRsp.ID;
2711
+ }
2712
+ const apsFrame = {
2713
+ profileId: sourceEndpointInfo.profileId,
2714
+ clusterId: zclFrame.Cluster.ID,
2715
+ sourceEndpoint: sourceEndpointInfo.endpoint,
2716
+ destinationEndpoint: (typeof endpoint === 'number') ? endpoint : endpoints_1.FIXED_ENDPOINTS[0].endpoint,
2717
+ options: this.defaultApsOptions,
2718
+ groupId: 0,
2719
+ sequence: 0, // set by stack
2720
+ };
2721
+ // don't RETRY if no response expected
2722
+ if (commandResponseId == null) {
2723
+ apsFrame.options &= ~enums_2.EmberApsOption.RETRY;
2724
+ }
2725
+ const data = zclFrame.toBuffer();
2726
+ return new Promise((resolve, reject) => {
2727
+ this.requestQueue.enqueue(async () => {
2728
+ this.checkInterpanLock();
2729
+ if (CHECK_APS_PAYLOAD_LENGTH) {
2730
+ const maxPayloadLength = (await this.maximumApsPayloadLength(enums_2.EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame));
2731
+ if (data.length > maxPayloadLength) {
2732
+ return enums_2.EmberStatus.MESSAGE_TOO_LONG; // queue will reject
2733
+ }
2734
+ }
2735
+ // track group changes in NCP multicast table
2736
+ if (apsFrame.clusterId === cluster_1.default.genGroups.ID) {
2737
+ await this.onGroupChange(command.ID, zclFrame.Payload.groupid);
2738
+ }
2739
+ debug(`~~~> [ZCL to=${networkAddress} apsFrame=${JSON.stringify(apsFrame)} header=${JSON.stringify(zclFrame.Header)}]`);
2740
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2741
+ const [status, messageTag] = (await this.ezsp.send(enums_2.EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, data, 0, // alias
2742
+ 0));
2743
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2744
+ console.error(`~x~> [ZCL to=${networkAddress}] Failed to send request with status=${enums_2.EmberStatus[status]}.`);
2745
+ return status; // let queue handle retry based on status
2746
+ }
2747
+ if (commandResponseId != null) {
2748
+ // NOTE: aps sequence number will have been set by send function
2749
+ const result = (await this.oneWaitress.startWaitingFor({
2750
+ target: networkAddress,
2751
+ apsFrame,
2752
+ zclSequence: zclFrame.Header.transactionSequenceNumber,
2753
+ commandIdentifier: commandResponseId,
2754
+ }, timeout || DEFAULT_ZCL_REQUEST_TIMEOUT));
2755
+ resolve(result);
2756
+ }
2757
+ else {
2758
+ resolve(null); // don't expect a response
2759
+ return enums_2.EmberStatus.SUCCESS;
2760
+ }
2761
+ }, reject);
2762
+ });
2763
+ }
2764
+ // queued, non-InterPAN
2765
+ async sendZclFrameToGroup(groupID, zclFrame, sourceEndpoint) {
2766
+ const sourceEndpointInfo = typeof sourceEndpoint === 'number' ?
2767
+ endpoints_1.FIXED_ENDPOINTS.find((epi) => (epi.endpoint === sourceEndpoint)) : endpoints_1.FIXED_ENDPOINTS[0];
2768
+ const apsFrame = {
2769
+ profileId: sourceEndpointInfo.profileId,
2770
+ clusterId: zclFrame.Cluster.ID,
2771
+ sourceEndpoint: sourceEndpointInfo.endpoint,
2772
+ destinationEndpoint: endpoints_1.FIXED_ENDPOINTS[0].endpoint,
2773
+ options: this.defaultApsOptions,
2774
+ groupId: groupID,
2775
+ sequence: 0, // set by stack
2776
+ };
2777
+ const data = zclFrame.toBuffer();
2778
+ return new Promise((resolve, reject) => {
2779
+ this.requestQueue.enqueue(async () => {
2780
+ this.checkInterpanLock();
2781
+ if (CHECK_APS_PAYLOAD_LENGTH) {
2782
+ const maxPayloadLength = (await this.maximumApsPayloadLength(enums_2.EmberOutgoingMessageType.MULTICAST, groupID, apsFrame));
2783
+ if (data.length > maxPayloadLength) {
2784
+ return enums_2.EmberStatus.MESSAGE_TOO_LONG; // queue will reject
2785
+ }
2786
+ }
2787
+ debug(`~~~> [ZCL GROUP apsFrame=${JSON.stringify(apsFrame)} header=${JSON.stringify(zclFrame.Header)}]`);
2788
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2789
+ const [status, messageTag] = (await this.ezsp.send(enums_2.EmberOutgoingMessageType.MULTICAST, apsFrame.groupId, // not used for MC
2790
+ apsFrame, data, 0, // alias
2791
+ 0));
2792
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2793
+ console.error(`~x~> [ZCL GROUP] Failed to send with status=${enums_2.EmberStatus[status]}.`);
2794
+ return status; // let queue handle retry based on status
2795
+ }
2796
+ // NOTE: since ezspMessageSentHandler could take a while here, we don't block, it'll just be logged if the delivery failed
2797
+ resolve();
2798
+ return enums_2.EmberStatus.SUCCESS;
2799
+ }, reject);
2800
+ });
2801
+ }
2802
+ // queued, non-InterPAN
2803
+ async sendZclFrameToAll(endpoint, zclFrame, sourceEndpoint) {
2804
+ const sourceEndpointInfo = typeof sourceEndpoint === 'number' ?
2805
+ endpoints_1.FIXED_ENDPOINTS.find((epi) => (epi.endpoint === sourceEndpoint)) : endpoints_1.FIXED_ENDPOINTS[0];
2806
+ const apsFrame = {
2807
+ profileId: sourceEndpointInfo.profileId,
2808
+ clusterId: zclFrame.Cluster.ID,
2809
+ sourceEndpoint: sourceEndpointInfo.endpoint,
2810
+ destinationEndpoint: (typeof endpoint === 'number') ? endpoint : endpoints_1.FIXED_ENDPOINTS[0].endpoint,
2811
+ options: this.defaultApsOptions,
2812
+ groupId: consts_2.EMBER_RX_ON_WHEN_IDLE_BROADCAST_ADDRESS,
2813
+ sequence: 0, // set by stack
2814
+ };
2815
+ const data = zclFrame.toBuffer();
2816
+ return new Promise((resolve, reject) => {
2817
+ this.requestQueue.enqueue(async () => {
2818
+ this.checkInterpanLock();
2819
+ if (CHECK_APS_PAYLOAD_LENGTH) {
2820
+ const maxPayloadLength = (await this.maximumApsPayloadLength(enums_2.EmberOutgoingMessageType.BROADCAST, consts_2.EMBER_RX_ON_WHEN_IDLE_BROADCAST_ADDRESS, apsFrame));
2821
+ if (data.length > maxPayloadLength) {
2822
+ return enums_2.EmberStatus.MESSAGE_TOO_LONG; // queue will reject
2823
+ }
2824
+ }
2825
+ debug(`~~~> [ZCL BROADCAST apsFrame=${JSON.stringify(apsFrame)} header=${JSON.stringify(zclFrame.Header)}]`);
2826
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2827
+ const [status, messageTag] = (await this.ezsp.send(enums_2.EmberOutgoingMessageType.BROADCAST, consts_2.EMBER_RX_ON_WHEN_IDLE_BROADCAST_ADDRESS, apsFrame, data, 0, // alias
2828
+ 0));
2829
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2830
+ console.error(`~x~> [ZCL BROADCAST] Failed to send with status=${enums_2.EmberStatus[status]}.`);
2831
+ return status; // let queue handle retry based on status
2832
+ }
2833
+ // NOTE: since ezspMessageSentHandler could take a while here, we don't block, it'll just be logged if the delivery failed
2834
+ resolve();
2835
+ return enums_2.EmberStatus.SUCCESS;
2836
+ }, reject);
2837
+ });
2838
+ }
2839
+ //---- InterPAN for Touchlink
2840
+ // XXX: There might be a better way to handle touchlink with ZLL ezsp functions, but I don't have any device to test so, didn't look into it...
2841
+ // TODO: check all this touchlink/interpan stuff
2842
+ // queued
2843
+ async setChannelInterPAN(channel) {
2844
+ if (typeof channel !== 'number') {
2845
+ console.error(`Tried to set channel InterPAN to non-number. Channel ${channel} of type ${typeof channel}.`);
2846
+ return;
2847
+ }
2848
+ return new Promise((resolve, reject) => {
2849
+ this.requestQueue.enqueue(async () => {
2850
+ this.interpanLock = true;
2851
+ const status = (await this.ezsp.ezspSetLogicalAndRadioChannel(channel));
2852
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2853
+ this.interpanLock = false; // XXX: ok?
2854
+ console.error(`Failed to set InterPAN channel to ${channel} with status=${enums_2.EmberStatus[status]}.`);
2855
+ return status;
2856
+ }
2857
+ resolve();
2858
+ return status;
2859
+ }, reject);
2860
+ });
2861
+ }
2862
+ // queued
2863
+ async sendZclFrameInterPANToIeeeAddr(zclFrame, ieeeAddress) {
2864
+ return new Promise((resolve, reject) => {
2865
+ this.requestQueue.enqueue(async () => {
2866
+ const msgBuffalo = new buffalo_1.EzspBuffalo(Buffer.alloc(consts_2.MAXIMUM_INTERPAN_LENGTH));
2867
+ // cache-enabled getters
2868
+ const sourcePanId = (await this.emberGetPanId());
2869
+ const sourceEui64 = (await this.emberGetEui64());
2870
+ msgBuffalo.writeUInt16((consts_2.LONG_DEST_FRAME_CONTROL | consts_2.MAC_ACK_REQUIRED)); // macFrameControl
2871
+ msgBuffalo.writeUInt8(0); // sequence Skip Sequence number, stack sets the sequence number.
2872
+ msgBuffalo.writeUInt16(consts_2.INVALID_PAN_ID); // destPanId
2873
+ msgBuffalo.writeIeeeAddr(ieeeAddress); // destAddress (longAddress)
2874
+ msgBuffalo.writeUInt16(sourcePanId); // sourcePanId
2875
+ msgBuffalo.writeIeeeAddr(sourceEui64); // sourceAddress
2876
+ msgBuffalo.writeUInt16(consts_2.STUB_NWK_FRAME_CONTROL); // nwkFrameControl
2877
+ msgBuffalo.writeUInt8((enums_2.EmberInterpanMessageType.UNICAST | consts_2.INTERPAN_APS_FRAME_TYPE)); // apsFrameControl
2878
+ msgBuffalo.writeUInt16(zclFrame.Cluster.ID);
2879
+ msgBuffalo.writeUInt16(consts_2.TOUCHLINK_PROFILE_ID);
2880
+ debug(`~~~> [ZCL TOUCHLINK to=${ieeeAddress} header=${JSON.stringify(zclFrame.Header)}]`);
2881
+ const status = (await this.ezsp.ezspSendRawMessage(Buffer.concat([msgBuffalo.getWritten(), zclFrame.toBuffer()])));
2882
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2883
+ console.error(`~x~> [ZCL TOUCHLINK to=${ieeeAddress}] Failed to send with status=${enums_2.EmberStatus[status]}.`);
2884
+ return status;
2885
+ }
2886
+ // NOTE: can use ezspRawTransmitCompleteHandler if needed here
2887
+ resolve();
2888
+ return status;
2889
+ }, reject);
2890
+ });
2891
+ }
2892
+ // queued
2893
+ async sendZclFrameInterPANBroadcast(zclFrame, timeout) {
2894
+ const command = zclFrame.getCommand();
2895
+ if (!command.hasOwnProperty('response')) {
2896
+ throw new Error(`Command '${command.name}' has no response, cannot wait for response.`);
2897
+ }
2898
+ // just for waitress
2899
+ const apsFrame = {
2900
+ profileId: consts_2.TOUCHLINK_PROFILE_ID,
2901
+ clusterId: zclFrame.Cluster.ID,
2902
+ sourceEndpoint: 0,
2903
+ destinationEndpoint: 0,
2904
+ options: enums_2.EmberApsOption.NONE,
2905
+ groupId: consts_2.EMBER_SLEEPY_BROADCAST_ADDRESS,
2906
+ sequence: 0, // set by stack
2907
+ };
2908
+ return new Promise((resolve, reject) => {
2909
+ this.requestQueue.enqueue(async () => {
2910
+ const msgBuffalo = new buffalo_1.EzspBuffalo(Buffer.alloc(consts_2.MAXIMUM_INTERPAN_LENGTH));
2911
+ // cache-enabled getters
2912
+ const sourcePanId = (await this.emberGetPanId());
2913
+ const sourceEui64 = (await this.emberGetEui64());
2914
+ msgBuffalo.writeUInt16(consts_2.SHORT_DEST_FRAME_CONTROL); // macFrameControl
2915
+ msgBuffalo.writeUInt8(0); // sequence Skip Sequence number, stack sets the sequence number.
2916
+ msgBuffalo.writeUInt16(consts_2.INVALID_PAN_ID); // destPanId
2917
+ msgBuffalo.writeUInt16(apsFrame.groupId); // destAddress (longAddress)
2918
+ msgBuffalo.writeUInt16(sourcePanId); // sourcePanId
2919
+ msgBuffalo.writeIeeeAddr(sourceEui64); // sourceAddress
2920
+ msgBuffalo.writeUInt16(consts_2.STUB_NWK_FRAME_CONTROL); // nwkFrameControl
2921
+ msgBuffalo.writeUInt8((enums_2.EmberInterpanMessageType.BROADCAST | consts_2.INTERPAN_APS_FRAME_TYPE)); // apsFrameControl
2922
+ msgBuffalo.writeUInt16(apsFrame.clusterId);
2923
+ msgBuffalo.writeUInt16(apsFrame.profileId);
2924
+ const data = Buffer.concat([msgBuffalo.getWritten(), zclFrame.toBuffer()]);
2925
+ debug(`~~~> [ZCL TOUCHLINK BROADCAST header=${JSON.stringify(zclFrame.Header)}]`);
2926
+ const status = (await this.ezsp.ezspSendRawMessage(data));
2927
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2928
+ console.error(`~x~> [ZCL TOUCHLINK BROADCAST] Failed to send with status=${enums_2.EmberStatus[status]}.`);
2929
+ return status;
2930
+ }
2931
+ // NOTE: can use ezspRawTransmitCompleteHandler if needed here
2932
+ const result = (await this.oneWaitress.startWaitingFor({
2933
+ target: null,
2934
+ apsFrame: apsFrame,
2935
+ zclSequence: zclFrame.Header.transactionSequenceNumber,
2936
+ commandIdentifier: command.response,
2937
+ }, timeout || DEFAULT_ZCL_REQUEST_TIMEOUT * 2)); // XXX: touchlink timeout?
2938
+ resolve(result);
2939
+ return enums_2.EmberStatus.SUCCESS;
2940
+ }, reject);
2941
+ });
2942
+ }
2943
+ // queued
2944
+ async restoreChannelInterPAN() {
2945
+ return new Promise((resolve, reject) => {
2946
+ this.requestQueue.enqueue(async () => {
2947
+ const status = (await this.ezsp.ezspSetLogicalAndRadioChannel(this.networkOptions.channelList[0]));
2948
+ if (status !== enums_2.EmberStatus.SUCCESS) {
2949
+ console.error(`Failed to restore InterPAN channel to ${this.networkOptions.channelList[0]} with status=${enums_2.EmberStatus[status]}.`);
2950
+ return status;
2951
+ }
2952
+ // let adapter settle down
2953
+ await (0, utils_1.Wait)(3000);
2954
+ this.interpanLock = false;
2955
+ resolve();
2956
+ return status;
2957
+ }, reject);
2958
+ });
2959
+ }
2960
+ //-- END Adapter implementation
2961
+ checkInterpanLock() {
2962
+ if (this.interpanLock) {
2963
+ console.error(`[INTERPAN MODE] Cannot execute non-InterPAN commands.`);
2964
+ // will be caught by request queue and rejected internally.
2965
+ throw new Error(enums_2.EzspStatus[enums_2.EzspStatus.ERROR_INVALID_CALL]);
2966
+ }
2967
+ }
2968
+ }
2969
+ exports.EmberAdapter = EmberAdapter;
2970
+ //# sourceMappingURL=emberAdapter.js.map