@willieee802/zigbee-herdsman 0.18.2 → 0.19.2

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 (329) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +67 -0
  3. package/dist/adapter/adapter.d.ts +61 -61
  4. package/dist/adapter/adapter.d.ts.map +1 -1
  5. package/dist/adapter/adapter.js +146 -146
  6. package/dist/adapter/deconz/adapter/deconzAdapter.d.ts +68 -68
  7. package/dist/adapter/deconz/adapter/deconzAdapter.js +1060 -1060
  8. package/dist/adapter/deconz/adapter/index.d.ts +2 -2
  9. package/dist/adapter/deconz/adapter/index.js +10 -10
  10. package/dist/adapter/deconz/driver/constants.d.ts +104 -104
  11. package/dist/adapter/deconz/driver/constants.js +55 -55
  12. package/dist/adapter/deconz/driver/driver.d.ts +81 -81
  13. package/dist/adapter/deconz/driver/driver.js +732 -732
  14. package/dist/adapter/deconz/driver/frame.d.ts +6 -6
  15. package/dist/adapter/deconz/driver/frame.js +13 -13
  16. package/dist/adapter/deconz/driver/frameParser.d.ts +2 -2
  17. package/dist/adapter/deconz/driver/frameParser.js +443 -443
  18. package/dist/adapter/deconz/driver/parser.d.ts +12 -12
  19. package/dist/adapter/deconz/driver/parser.js +61 -61
  20. package/dist/adapter/deconz/driver/writer.d.ts +8 -8
  21. package/dist/adapter/deconz/driver/writer.js +44 -44
  22. package/dist/adapter/events.d.ts +47 -47
  23. package/dist/adapter/events.js +14 -14
  24. package/dist/adapter/ezsp/adapter/backup.d.ts +9 -9
  25. package/dist/adapter/ezsp/adapter/backup.js +53 -53
  26. package/dist/adapter/ezsp/adapter/ezspAdapter.d.ts +59 -59
  27. package/dist/adapter/ezsp/adapter/ezspAdapter.d.ts.map +1 -1
  28. package/dist/adapter/ezsp/adapter/ezspAdapter.js +603 -585
  29. package/dist/adapter/ezsp/adapter/ezspAdapter.js.map +1 -1
  30. package/dist/adapter/ezsp/adapter/index.d.ts +2 -2
  31. package/dist/adapter/ezsp/adapter/index.js +10 -10
  32. package/dist/adapter/ezsp/driver/commands.d.ts +36 -36
  33. package/dist/adapter/ezsp/driver/commands.js +2359 -2359
  34. package/dist/adapter/ezsp/driver/consts.d.ts +10 -10
  35. package/dist/adapter/ezsp/driver/consts.js +13 -13
  36. package/dist/adapter/ezsp/driver/driver.d.ts +103 -103
  37. package/dist/adapter/ezsp/driver/driver.js +639 -639
  38. package/dist/adapter/ezsp/driver/ezsp.d.ts +96 -96
  39. package/dist/adapter/ezsp/driver/ezsp.js +586 -586
  40. package/dist/adapter/ezsp/driver/index.d.ts +3 -3
  41. package/dist/adapter/ezsp/driver/index.js +8 -8
  42. package/dist/adapter/ezsp/driver/multicast.d.ts +12 -12
  43. package/dist/adapter/ezsp/driver/multicast.js +74 -74
  44. package/dist/adapter/ezsp/driver/parser.d.ts +12 -12
  45. package/dist/adapter/ezsp/driver/parser.js +111 -111
  46. package/dist/adapter/ezsp/driver/types/basic.d.ts +62 -62
  47. package/dist/adapter/ezsp/driver/types/basic.js +208 -208
  48. package/dist/adapter/ezsp/driver/types/basic.js.map +1 -1
  49. package/dist/adapter/ezsp/driver/types/index.d.ts +9 -9
  50. package/dist/adapter/ezsp/driver/types/index.js +133 -133
  51. package/dist/adapter/ezsp/driver/types/named.d.ts +697 -697
  52. package/dist/adapter/ezsp/driver/types/named.js +1726 -1726
  53. package/dist/adapter/ezsp/driver/types/named.js.map +1 -1
  54. package/dist/adapter/ezsp/driver/types/struct.d.ts +251 -251
  55. package/dist/adapter/ezsp/driver/types/struct.js +708 -708
  56. package/dist/adapter/ezsp/driver/types/struct.js.map +1 -1
  57. package/dist/adapter/ezsp/driver/uart.d.ts +44 -44
  58. package/dist/adapter/ezsp/driver/uart.js +368 -368
  59. package/dist/adapter/ezsp/driver/utils/crc16ccitt.d.ts +2 -2
  60. package/dist/adapter/ezsp/driver/utils/crc16ccitt.js +55 -55
  61. package/dist/adapter/ezsp/driver/utils/index.d.ts +18 -18
  62. package/dist/adapter/ezsp/driver/utils/index.js +67 -67
  63. package/dist/adapter/ezsp/driver/writer.d.ts +13 -13
  64. package/dist/adapter/ezsp/driver/writer.js +88 -88
  65. package/dist/adapter/index.d.ts +4 -4
  66. package/dist/adapter/index.js +35 -35
  67. package/dist/adapter/serialPort.d.ts +8 -8
  68. package/dist/adapter/serialPort.js +22 -22
  69. package/dist/adapter/serialPort.js.map +1 -1
  70. package/dist/adapter/serialPortUtils.d.ts +12 -12
  71. package/dist/adapter/serialPortUtils.js +18 -18
  72. package/dist/adapter/socketPortUtils.d.ts +10 -10
  73. package/dist/adapter/socketPortUtils.js +16 -16
  74. package/dist/adapter/tstype.d.ts +85 -85
  75. package/dist/adapter/tstype.js +2 -2
  76. package/dist/adapter/z-stack/adapter/adapter-backup.d.ts +62 -62
  77. package/dist/adapter/z-stack/adapter/adapter-backup.d.ts.map +1 -1
  78. package/dist/adapter/z-stack/adapter/adapter-backup.js +460 -441
  79. package/dist/adapter/z-stack/adapter/adapter-backup.js.map +1 -1
  80. package/dist/adapter/z-stack/adapter/adapter-nv-memory.d.ts +150 -150
  81. package/dist/adapter/z-stack/adapter/adapter-nv-memory.js +258 -258
  82. package/dist/adapter/z-stack/adapter/endpoints.d.ts +11 -11
  83. package/dist/adapter/z-stack/adapter/endpoints.js +73 -73
  84. package/dist/adapter/z-stack/adapter/index.d.ts +2 -2
  85. package/dist/adapter/z-stack/adapter/index.js +8 -8
  86. package/dist/adapter/z-stack/adapter/manager.d.ts +86 -86
  87. package/dist/adapter/z-stack/adapter/manager.js +476 -476
  88. package/dist/adapter/z-stack/adapter/tstype.d.ts +6 -6
  89. package/dist/adapter/z-stack/adapter/tstype.js +10 -10
  90. package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts +81 -81
  91. package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts.map +1 -1
  92. package/dist/adapter/z-stack/adapter/zStackAdapter.js +868 -868
  93. package/dist/adapter/z-stack/adapter/zStackAdapter.js.map +1 -1
  94. package/dist/adapter/z-stack/constants/af.d.ts +23 -23
  95. package/dist/adapter/z-stack/constants/af.js +27 -27
  96. package/dist/adapter/z-stack/constants/common.d.ts +278 -278
  97. package/dist/adapter/z-stack/constants/common.js +289 -289
  98. package/dist/adapter/z-stack/constants/dbg.d.ts +22 -22
  99. package/dist/adapter/z-stack/constants/dbg.js +24 -24
  100. package/dist/adapter/z-stack/constants/index.d.ts +10 -10
  101. package/dist/adapter/z-stack/constants/index.js +47 -47
  102. package/dist/adapter/z-stack/constants/mac.d.ts +127 -127
  103. package/dist/adapter/z-stack/constants/mac.js +129 -129
  104. package/dist/adapter/z-stack/constants/sapi.d.ts +24 -24
  105. package/dist/adapter/z-stack/constants/sapi.js +26 -26
  106. package/dist/adapter/z-stack/constants/sys.d.ts +71 -71
  107. package/dist/adapter/z-stack/constants/sys.js +73 -73
  108. package/dist/adapter/z-stack/constants/util.d.ts +81 -81
  109. package/dist/adapter/z-stack/constants/util.js +83 -83
  110. package/dist/adapter/z-stack/constants/utils.d.ts +4 -4
  111. package/dist/adapter/z-stack/constants/utils.js +14 -14
  112. package/dist/adapter/z-stack/constants/zdo.d.ts +102 -102
  113. package/dist/adapter/z-stack/constants/zdo.js +104 -104
  114. package/dist/adapter/z-stack/models/index.d.ts +1 -1
  115. package/dist/adapter/z-stack/models/index.js +17 -17
  116. package/dist/adapter/z-stack/models/startup-options.d.ts +12 -12
  117. package/dist/adapter/z-stack/models/startup-options.js +2 -2
  118. package/dist/adapter/z-stack/structs/entries/address-manager-entry.d.ts +23 -23
  119. package/dist/adapter/z-stack/structs/entries/address-manager-entry.js +45 -45
  120. package/dist/adapter/z-stack/structs/entries/address-manager-table.d.ts +10 -10
  121. package/dist/adapter/z-stack/structs/entries/address-manager-table.js +22 -22
  122. package/dist/adapter/z-stack/structs/entries/aps-link-key-data-entry.d.ts +10 -10
  123. package/dist/adapter/z-stack/structs/entries/aps-link-key-data-entry.js +21 -21
  124. package/dist/adapter/z-stack/structs/entries/aps-link-key-data-table.d.ts +10 -10
  125. package/dist/adapter/z-stack/structs/entries/aps-link-key-data-table.js +23 -23
  126. package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-entry.d.ts +10 -10
  127. package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-entry.js +24 -24
  128. package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-table.d.ts +10 -10
  129. package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-table.js +23 -23
  130. package/dist/adapter/z-stack/structs/entries/channel-list.d.ts +8 -8
  131. package/dist/adapter/z-stack/structs/entries/channel-list.js +15 -15
  132. package/dist/adapter/z-stack/structs/entries/has-configured.d.ts +8 -8
  133. package/dist/adapter/z-stack/structs/entries/has-configured.js +16 -16
  134. package/dist/adapter/z-stack/structs/entries/index.d.ts +16 -16
  135. package/dist/adapter/z-stack/structs/entries/index.js +32 -32
  136. package/dist/adapter/z-stack/structs/entries/nib.d.ts +10 -10
  137. package/dist/adapter/z-stack/structs/entries/nib.js +68 -68
  138. package/dist/adapter/z-stack/structs/entries/nwk-key-descriptor.d.ts +10 -10
  139. package/dist/adapter/z-stack/structs/entries/nwk-key-descriptor.js +18 -18
  140. package/dist/adapter/z-stack/structs/entries/nwk-key.d.ts +8 -8
  141. package/dist/adapter/z-stack/structs/entries/nwk-key.js +15 -15
  142. package/dist/adapter/z-stack/structs/entries/nwk-pan-id.d.ts +8 -8
  143. package/dist/adapter/z-stack/structs/entries/nwk-pan-id.js +15 -15
  144. package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-entry.d.ts +13 -13
  145. package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-entry.js +23 -23
  146. package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-table.d.ts +10 -10
  147. package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-table.js +22 -22
  148. package/dist/adapter/z-stack/structs/entries/security-manager-entry.d.ts +20 -20
  149. package/dist/adapter/z-stack/structs/entries/security-manager-entry.js +36 -36
  150. package/dist/adapter/z-stack/structs/entries/security-manager-table.d.ts +10 -10
  151. package/dist/adapter/z-stack/structs/entries/security-manager-table.js +24 -24
  152. package/dist/adapter/z-stack/structs/index.d.ts +4 -4
  153. package/dist/adapter/z-stack/structs/index.js +20 -20
  154. package/dist/adapter/z-stack/structs/serializable-memory-object.d.ts +13 -13
  155. package/dist/adapter/z-stack/structs/serializable-memory-object.js +2 -2
  156. package/dist/adapter/z-stack/structs/struct.d.ts +99 -99
  157. package/dist/adapter/z-stack/structs/struct.js +295 -295
  158. package/dist/adapter/z-stack/structs/table.d.ts +94 -94
  159. package/dist/adapter/z-stack/structs/table.js +161 -161
  160. package/dist/adapter/z-stack/unpi/constants.d.ts +28 -28
  161. package/dist/adapter/z-stack/unpi/constants.js +41 -41
  162. package/dist/adapter/z-stack/unpi/frame.d.ts +16 -16
  163. package/dist/adapter/z-stack/unpi/frame.js +48 -48
  164. package/dist/adapter/z-stack/unpi/index.d.ts +5 -5
  165. package/dist/adapter/z-stack/unpi/index.js +37 -37
  166. package/dist/adapter/z-stack/unpi/parser.d.ts +10 -10
  167. package/dist/adapter/z-stack/unpi/parser.js +74 -74
  168. package/dist/adapter/z-stack/unpi/writer.d.ts +10 -10
  169. package/dist/adapter/z-stack/unpi/writer.js +44 -44
  170. package/dist/adapter/z-stack/utils/channel-list.d.ts +20 -20
  171. package/dist/adapter/z-stack/utils/channel-list.js +40 -40
  172. package/dist/adapter/z-stack/utils/index.d.ts +2 -2
  173. package/dist/adapter/z-stack/utils/index.js +18 -18
  174. package/dist/adapter/z-stack/utils/network-options.d.ts +8 -8
  175. package/dist/adapter/z-stack/utils/network-options.js +22 -22
  176. package/dist/adapter/z-stack/znp/buffaloZnp.d.ts +11 -11
  177. package/dist/adapter/z-stack/znp/buffaloZnp.js +113 -113
  178. package/dist/adapter/z-stack/znp/definition.d.ts +5 -5
  179. package/dist/adapter/z-stack/znp/definition.js +3050 -3050
  180. package/dist/adapter/z-stack/znp/index.d.ts +3 -3
  181. package/dist/adapter/z-stack/znp/index.js +10 -10
  182. package/dist/adapter/z-stack/znp/parameterType.d.ts +22 -22
  183. package/dist/adapter/z-stack/znp/parameterType.js +25 -25
  184. package/dist/adapter/z-stack/znp/tstype.d.ts +21 -21
  185. package/dist/adapter/z-stack/znp/tstype.js +2 -2
  186. package/dist/adapter/z-stack/znp/znp.d.ts +43 -43
  187. package/dist/adapter/z-stack/znp/znp.js +325 -325
  188. package/dist/adapter/z-stack/znp/zpiObject.d.ts +19 -19
  189. package/dist/adapter/z-stack/znp/zpiObject.js +96 -96
  190. package/dist/adapter/zigate/adapter/index.d.ts +2 -2
  191. package/dist/adapter/zigate/adapter/index.js +10 -10
  192. package/dist/adapter/zigate/adapter/zigateAdapter.d.ts +69 -69
  193. package/dist/adapter/zigate/adapter/zigateAdapter.js +678 -678
  194. package/dist/adapter/zigate/debug.d.ts +7 -7
  195. package/dist/adapter/zigate/debug.js +22 -22
  196. package/dist/adapter/zigate/driver/buffaloZiGate.d.ts +18 -18
  197. package/dist/adapter/zigate/driver/buffaloZiGate.js +139 -139
  198. package/dist/adapter/zigate/driver/commandType.d.ts +41 -41
  199. package/dist/adapter/zigate/driver/commandType.js +385 -385
  200. package/dist/adapter/zigate/driver/constants.d.ts +276 -276
  201. package/dist/adapter/zigate/driver/constants.js +371 -371
  202. package/dist/adapter/zigate/driver/frame.d.ts +26 -26
  203. package/dist/adapter/zigate/driver/frame.js +172 -172
  204. package/dist/adapter/zigate/driver/frame.js.map +1 -1
  205. package/dist/adapter/zigate/driver/messageType.d.ts +11 -11
  206. package/dist/adapter/zigate/driver/messageType.js +278 -278
  207. package/dist/adapter/zigate/driver/parameterType.d.ts +20 -20
  208. package/dist/adapter/zigate/driver/parameterType.js +23 -23
  209. package/dist/adapter/zigate/driver/ziGateObject.d.ts +23 -23
  210. package/dist/adapter/zigate/driver/ziGateObject.js +106 -106
  211. package/dist/adapter/zigate/driver/zigate.d.ts +49 -49
  212. package/dist/adapter/zigate/driver/zigate.js +303 -303
  213. package/dist/buffalo/buffalo.d.ts +50 -50
  214. package/dist/buffalo/buffalo.js +322 -322
  215. package/dist/buffalo/index.d.ts +3 -3
  216. package/dist/buffalo/index.js +33 -33
  217. package/dist/buffalo/tstype.d.ts +8 -8
  218. package/dist/buffalo/tstype.js +2 -2
  219. package/dist/controller/controller.d.ts +113 -110
  220. package/dist/controller/controller.d.ts.map +1 -1
  221. package/dist/controller/controller.js +619 -607
  222. package/dist/controller/controller.js.map +1 -1
  223. package/dist/controller/database.d.ts +18 -18
  224. package/dist/controller/database.js +93 -93
  225. package/dist/controller/events.d.ts +58 -55
  226. package/dist/controller/events.d.ts.map +1 -1
  227. package/dist/controller/events.js +101 -101
  228. package/dist/controller/events.js.map +1 -1
  229. package/dist/controller/greenPower.d.ts +12 -12
  230. package/dist/controller/greenPower.js +220 -220
  231. package/dist/controller/helpers/index.d.ts +2 -2
  232. package/dist/controller/helpers/index.js +28 -28
  233. package/dist/controller/helpers/request.d.ts +22 -22
  234. package/dist/controller/helpers/request.js +71 -71
  235. package/dist/controller/helpers/zclFrameConverter.d.ts +7 -7
  236. package/dist/controller/helpers/zclFrameConverter.js +31 -31
  237. package/dist/controller/helpers/zclTransactionSequenceNumber.d.ts +5 -5
  238. package/dist/controller/helpers/zclTransactionSequenceNumber.js +13 -13
  239. package/dist/controller/index.d.ts +5 -5
  240. package/dist/controller/index.js +8 -8
  241. package/dist/controller/logger-stub.d.ts +6 -6
  242. package/dist/controller/logger-stub.js +2 -2
  243. package/dist/controller/model/device.d.ts +133 -132
  244. package/dist/controller/model/device.d.ts.map +1 -1
  245. package/dist/controller/model/device.js +717 -708
  246. package/dist/controller/model/device.js.map +1 -1
  247. package/dist/controller/model/endpoint.d.ts +131 -131
  248. package/dist/controller/model/endpoint.d.ts.map +1 -1
  249. package/dist/controller/model/endpoint.js +816 -817
  250. package/dist/controller/model/endpoint.js.map +1 -1
  251. package/dist/controller/model/entity.d.ts +14 -14
  252. package/dist/controller/model/entity.js +26 -26
  253. package/dist/controller/model/group.d.ts +38 -38
  254. package/dist/controller/model/group.js +221 -221
  255. package/dist/controller/model/index.d.ts +5 -5
  256. package/dist/controller/model/index.js +14 -14
  257. package/dist/controller/touchlink.d.ts +19 -19
  258. package/dist/controller/touchlink.js +157 -157
  259. package/dist/controller/tstype.d.ts +21 -21
  260. package/dist/controller/tstype.js +9 -9
  261. package/dist/index.d.ts +3 -3
  262. package/dist/index.js +33 -33
  263. package/dist/models/backup-storage-legacy.d.ts +26 -26
  264. package/dist/models/backup-storage-legacy.js +2 -2
  265. package/dist/models/backup-storage-unified.d.ts +49 -49
  266. package/dist/models/backup-storage-unified.js +2 -2
  267. package/dist/models/backup.d.ts +37 -37
  268. package/dist/models/backup.js +2 -2
  269. package/dist/models/index.d.ts +4 -4
  270. package/dist/models/index.js +20 -20
  271. package/dist/models/network-options.d.ts +12 -12
  272. package/dist/models/network-options.js +2 -2
  273. package/dist/utils/assertString.d.ts +2 -2
  274. package/dist/utils/assertString.js +8 -8
  275. package/dist/utils/backup.d.ts +20 -20
  276. package/dist/utils/backup.js +187 -187
  277. package/dist/utils/equalsPartial.d.ts +2 -2
  278. package/dist/utils/equalsPartial.js +11 -11
  279. package/dist/utils/index.d.ts +9 -9
  280. package/dist/utils/index.js +45 -45
  281. package/dist/utils/isNumberArray.d.ts +2 -2
  282. package/dist/utils/isNumberArray.js +6 -6
  283. package/dist/utils/queue.d.ts +11 -11
  284. package/dist/utils/queue.js +50 -50
  285. package/dist/utils/realpathSync.d.ts +2 -2
  286. package/dist/utils/realpathSync.js +12 -12
  287. package/dist/utils/wait.d.ts +2 -2
  288. package/dist/utils/wait.js +8 -8
  289. package/dist/utils/waitress.d.ts +21 -21
  290. package/dist/utils/waitress.js +61 -61
  291. package/dist/zcl/buffaloZcl.d.ts +41 -41
  292. package/dist/zcl/buffaloZcl.js +591 -591
  293. package/dist/zcl/definition/buffaloZclDataType.d.ts +17 -17
  294. package/dist/zcl/definition/buffaloZclDataType.js +20 -20
  295. package/dist/zcl/definition/cluster.d.ts +29 -29
  296. package/dist/zcl/definition/cluster.js +5335 -5335
  297. package/dist/zcl/definition/dataType.d.ts +59 -59
  298. package/dist/zcl/definition/dataType.js +64 -64
  299. package/dist/zcl/definition/direction.d.ts +5 -5
  300. package/dist/zcl/definition/direction.js +8 -8
  301. package/dist/zcl/definition/endpointDeviceType.d.ts +4 -4
  302. package/dist/zcl/definition/endpointDeviceType.js +15 -15
  303. package/dist/zcl/definition/foundation.d.ts +11 -11
  304. package/dist/zcl/definition/foundation.js +167 -167
  305. package/dist/zcl/definition/frameControl.d.ts +10 -10
  306. package/dist/zcl/definition/frameControl.js +2 -2
  307. package/dist/zcl/definition/frameType.d.ts +5 -5
  308. package/dist/zcl/definition/frameType.js +8 -8
  309. package/dist/zcl/definition/index.d.ts +13 -13
  310. package/dist/zcl/definition/index.js +51 -51
  311. package/dist/zcl/definition/manufacturerCode.d.ts +1074 -1074
  312. package/dist/zcl/definition/manufacturerCode.js +1079 -1079
  313. package/dist/zcl/definition/powerSource.d.ts +4 -4
  314. package/dist/zcl/definition/powerSource.js +12 -12
  315. package/dist/zcl/definition/status.d.ts +38 -38
  316. package/dist/zcl/definition/status.js +41 -41
  317. package/dist/zcl/definition/tstype.d.ts +16 -16
  318. package/dist/zcl/definition/tstype.js +2 -2
  319. package/dist/zcl/index.d.ts +15 -15
  320. package/dist/zcl/index.js +55 -55
  321. package/dist/zcl/tstype.d.ts +56 -56
  322. package/dist/zcl/tstype.js +10 -10
  323. package/dist/zcl/utils.d.ts +6 -6
  324. package/dist/zcl/utils.js +165 -165
  325. package/dist/zcl/zclFrame.d.ts +45 -45
  326. package/dist/zcl/zclFrame.js +347 -347
  327. package/dist/zcl/zclStatusError.d.ts +5 -5
  328. package/dist/zcl/zclStatusError.js +13 -13
  329. package/package.json +6 -6
@@ -1,709 +1,718 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
- Object.defineProperty(exports, "__esModule", { value: true });
29
- const zclTransactionSequenceNumber_1 = __importDefault(require("../helpers/zclTransactionSequenceNumber"));
30
- const endpoint_1 = __importDefault(require("./endpoint"));
31
- const entity_1 = __importDefault(require("./entity"));
32
- const utils_1 = require("../../utils");
33
- const debug_1 = __importDefault(require("debug"));
34
- const Zcl = __importStar(require("../../zcl"));
35
- const assert_1 = __importDefault(require("assert"));
36
- const helpers_1 = require("../helpers");
37
- /**
38
- * @ignore
39
- */
40
- const OneJanuary2000 = new Date('January 01, 2000 00:00:00 UTC+00:00').getTime();
41
- const debug = {
42
- error: (0, debug_1.default)('zigbee-herdsman:controller:device:error'),
43
- log: (0, debug_1.default)('zigbee-herdsman:controller:device:log'),
44
- };
45
- class Device extends entity_1.default {
46
- // Getters/setters
47
- get ieeeAddr() { return this._ieeeAddr; }
48
- set ieeeAddr(ieeeAddr) { this._ieeeAddr = ieeeAddr; }
49
- get applicationVersion() { return this._applicationVersion; }
50
- set applicationVersion(applicationVersion) { this._applicationVersion = applicationVersion; }
51
- get endpoints() { return this._endpoints; }
52
- get interviewCompleted() { return this._interviewCompleted; }
53
- get interviewing() { return this._interviewing; }
54
- get lastSeen() { return this._lastSeen; }
55
- get manufacturerID() { return this._manufacturerID; }
56
- get isDeleted() { return this._deleted; }
57
- set type(type) { this._type = type; }
58
- get type() { return this._type; }
59
- get dateCode() { return this._dateCode; }
60
- set dateCode(dateCode) { this._dateCode = dateCode; }
61
- set hardwareVersion(hardwareVersion) { this._hardwareVersion = hardwareVersion; }
62
- get hardwareVersion() { return this._hardwareVersion; }
63
- get manufacturerName() { return this._manufacturerName; }
64
- set manufacturerName(manufacturerName) { this._manufacturerName = manufacturerName; }
65
- set modelID(modelID) { this._modelID = modelID; }
66
- get modelID() { return this._modelID; }
67
- get networkAddress() { return this._networkAddress; }
68
- set networkAddress(networkAddress) {
69
- this._networkAddress = networkAddress;
70
- for (const endpoint of this._endpoints) {
71
- endpoint.deviceNetworkAddress = networkAddress;
72
- }
73
- }
74
- get powerSource() { return this._powerSource; }
75
- set powerSource(powerSource) {
76
- this._powerSource = typeof powerSource === 'number' ? Zcl.PowerSource[powerSource & ~(1 << 7)] : powerSource;
77
- }
78
- get softwareBuildID() { return this._softwareBuildID; }
79
- set softwareBuildID(softwareBuildID) { this._softwareBuildID = softwareBuildID; }
80
- get stackVersion() { return this._stackVersion; }
81
- set stackVersion(stackVersion) { this._stackVersion = stackVersion; }
82
- get zclVersion() { return this._zclVersion; }
83
- set zclVersion(zclVersion) { this._zclVersion = zclVersion; }
84
- get linkquality() { return this._linkquality; }
85
- set linkquality(linkquality) { this._linkquality = linkquality; }
86
- get skipDefaultResponse() { return this._skipDefaultResponse; }
87
- set skipDefaultResponse(skipDefaultResponse) { this._skipDefaultResponse = skipDefaultResponse; }
88
- get skipTimeResponse() { return this._skipTimeResponse; }
89
- set skipTimeResponse(skipTimeResponse) { this._skipTimeResponse = skipTimeResponse; }
90
- get defaultSendRequestWhen() { return this._defaultSendRequestWhen; }
91
- set defaultSendRequestWhen(defaultSendRequestWhen) {
92
- this._defaultSendRequestWhen = defaultSendRequestWhen;
93
- }
94
- get checkinInterval() { return this._checkinInterval; }
95
- get pendingRequestTimeout() { return this._pendingRequestTimeout; }
96
- set pendingRequestTimeout(pendingRequestTimeout) { this._pendingRequestTimeout = pendingRequestTimeout; }
97
- constructor(databaseID, ID, type, ieeeAddr, networkAddress, manufacturerID, endpoints, manufacturerName, powerSource, modelID, applicationVersion, stackVersion, zclVersion, hardwareVersion, dateCode, softwareBuildID, interviewCompleted, meta, lastSeen, defaultSendRequestWhen, checkinInterval, pendingRequestTimeout) {
98
- super();
99
- this.databaseID = databaseID;
100
- this.ID = ID;
101
- this._type = type;
102
- this.ieeeAddr = ieeeAddr;
103
- this._networkAddress = networkAddress;
104
- this._manufacturerID = manufacturerID;
105
- this._endpoints = endpoints;
106
- this._manufacturerName = manufacturerName;
107
- this._powerSource = powerSource;
108
- this._modelID = modelID;
109
- this._applicationVersion = applicationVersion;
110
- this._stackVersion = stackVersion;
111
- this._zclVersion = zclVersion;
112
- this.hardwareVersion = hardwareVersion;
113
- this._dateCode = dateCode;
114
- this._softwareBuildID = softwareBuildID;
115
- this._interviewCompleted = interviewCompleted;
116
- this._interviewing = false;
117
- this._skipDefaultResponse = false;
118
- this._skipTimeResponse = false;
119
- this.meta = meta;
120
- this._lastSeen = lastSeen;
121
- this._defaultSendRequestWhen = defaultSendRequestWhen;
122
- this._checkinInterval = checkinInterval;
123
- this._pendingRequestTimeout = pendingRequestTimeout;
124
- }
125
- createEndpoint(ID) {
126
- if (this.getEndpoint(ID)) {
127
- throw new Error(`Device '${this.ieeeAddr}' already has an endpoint '${ID}'`);
128
- }
129
- const endpoint = endpoint_1.default.create(this.databaseID, ID, undefined, undefined, [], [], this.networkAddress, this.ieeeAddr);
130
- this.endpoints.push(endpoint);
131
- this.save();
132
- return endpoint;
133
- }
134
- changeIeeeAddress(ieeeAddr) {
135
- delete Device.devices.get(this.databaseID)[this.ieeeAddr];
136
- this.ieeeAddr = ieeeAddr;
137
- Device.devices.get(this.databaseID)[this.ieeeAddr] = this;
138
- this.endpoints.forEach((e) => e.deviceIeeeAddress = ieeeAddr);
139
- this.save();
140
- }
141
- getEndpoint(ID) {
142
- return this.endpoints.find((e) => e.ID === ID);
143
- }
144
- // There might be multiple endpoints with same DeviceId but it is not supported and first endpoint is returned
145
- getEndpointByDeviceType(deviceType) {
146
- const deviceID = Zcl.EndpointDeviceType[deviceType];
147
- return this.endpoints.find((d) => d.deviceID === deviceID);
148
- }
149
- implicitCheckin() {
150
- this.endpoints.forEach(async (e) => e.sendPendingRequests(false));
151
- }
152
- updateLastSeen() {
153
- this._lastSeen = Date.now();
154
- }
155
- hasPendingRequests() {
156
- return this.endpoints.find(e => e.hasPendingRequests()) !== undefined;
157
- }
158
- async onZclData(dataPayload, endpoint) {
159
- var _a;
160
- const frame = dataPayload.frame;
161
- // Update reportable properties
162
- if (frame.isCluster('genBasic') && (frame.isCommand('readRsp') || frame.isCommand('report'))) {
163
- for (const [key, value] of Object.entries(helpers_1.ZclFrameConverter.attributeKeyValue(frame))) {
164
- (_a = Device.ReportablePropertiesMapping[key]) === null || _a === void 0 ? void 0 : _a.set(value, this);
165
- }
166
- }
167
- // Respond to enroll requests
168
- if (frame.isSpecific() && frame.isCluster('ssIasZone') && frame.isCommand('enrollReq')) {
169
- debug.log(`IAS - '${this.ieeeAddr}' responding to enroll response`);
170
- const payload = { enrollrspcode: 0, zoneid: 23 };
171
- await endpoint.command('ssIasZone', 'enrollRsp', payload, { disableDefaultResponse: true });
172
- }
173
- // Reponse to read requests
174
- if (frame.isGlobal() && frame.isCommand('read')) {
175
- const time = Math.round(((new Date()).getTime() - OneJanuary2000) / 1000);
176
- const attributes = {
177
- ...endpoint.clusters,
178
- genTime: { attributes: {
179
- timeStatus: 3,
180
- time: time,
181
- timeZone: ((new Date()).getTimezoneOffset() * -1) * 60,
182
- localTime: time - (new Date()).getTimezoneOffset() * 60,
183
- lastSetTime: time,
184
- validUntilTime: time + (24 * 60 * 60), // valid for 24 hours
185
- } },
186
- };
187
- if (frame.Cluster.name in attributes && (frame.Cluster.name !== 'genTime' || !this._skipTimeResponse)) {
188
- const response = {};
189
- for (const entry of frame.Payload) {
190
- if (frame.Cluster.hasAttribute(entry.attrId)) {
191
- const name = frame.Cluster.getAttribute(entry.attrId).name;
192
- if (name in attributes[frame.Cluster.name].attributes) {
193
- response[name] = attributes[frame.Cluster.name].attributes[name];
194
- }
195
- }
196
- }
197
- try {
198
- await endpoint.readResponse(frame.Cluster.ID, frame.Header.transactionSequenceNumber, response, { srcEndpoint: dataPayload.destinationEndpoint });
199
- }
200
- catch (error) {
201
- debug.error(`Read response to ${this.ieeeAddr} failed`);
202
- }
203
- }
204
- }
205
- // Handle check-in from sleeping end devices
206
- if (frame.isSpecific() && frame.isCluster("genPollCtrl") && frame.isCommand("checkin")) {
207
- try {
208
- if (this.hasPendingRequests() || (this._checkinInterval === undefined)) {
209
- const payload = {
210
- startFastPolling: true,
211
- fastPollTimeout: 0,
212
- };
213
- debug.log(`check-in from ${this.ieeeAddr}: accepting fast-poll`);
214
- await endpoint.command(frame.Cluster.ID, 'checkinRsp', payload, { sendWhen: 'immediate' });
215
- // This is a good time to read the checkin interval if we haven't stored it previously
216
- if (this._checkinInterval === undefined) {
217
- const pollPeriod = await endpoint.read('genPollCtrl', ['checkinInterval'], { sendWhen: 'immediate' });
218
- this._checkinInterval = pollPeriod.checkinInterval / 4; // convert to seconds
219
- this.pendingRequestTimeout = this._checkinInterval * 1000; // milliseconds
220
- debug.log(`Request Queue (${this.ieeeAddr}): default expiration timeout set to ${this.pendingRequestTimeout}`);
221
- }
222
- await Promise.all(this.endpoints.map(async (e) => e.sendPendingRequests(true)));
223
- // We *must* end fast-poll when we're done sending things. Otherwise
224
- // we cause undue power-drain.
225
- debug.log(`check-in from ${this.ieeeAddr}: stopping fast-poll`);
226
- await endpoint.command(frame.Cluster.ID, 'fastPollStop', {}, { sendWhen: 'immediate' });
227
- }
228
- else {
229
- const payload = {
230
- startFastPolling: false,
231
- fastPollTimeout: 0,
232
- };
233
- debug.log(`check-in from ${this.ieeeAddr}: declining fast-poll`);
234
- await endpoint.command(frame.Cluster.ID, 'checkinRsp', payload, { sendWhen: 'immediate' });
235
- }
236
- }
237
- catch (error) {
238
- /* istanbul ignore next */
239
- debug.error(`Handling of poll check-in form ${this.ieeeAddr} failed`);
240
- }
241
- }
242
- // Send a default response if necessary.
243
- const isDefaultResponse = frame.isGlobal() && frame.getCommand().name === 'defaultRsp';
244
- const commandHasResponse = frame.getCommand().hasOwnProperty('response');
245
- const disableDefaultResponse = frame.Header.frameControl.disableDefaultResponse;
246
- // Sometimes messages are received twice, prevent responding twice
247
- const alreadyResponded = this._lastDefaultResponseSequenceNumber === frame.Header.transactionSequenceNumber;
248
- if (this.type !== 'GreenPower' && !dataPayload.wasBroadcast && !disableDefaultResponse && !isDefaultResponse &&
249
- !commandHasResponse && !this._skipDefaultResponse && !alreadyResponded) {
250
- try {
251
- this._lastDefaultResponseSequenceNumber = frame.Header.transactionSequenceNumber;
252
- await endpoint.defaultResponse(frame.getCommand().ID, 0, frame.Cluster.ID, frame.Header.transactionSequenceNumber);
253
- }
254
- catch (error) {
255
- debug.error(`Default response to ${this.ieeeAddr} failed`);
256
- }
257
- }
258
- }
259
- /*
260
- * CRUD
261
- */
262
- static fromDatabaseEntry(entry, databaseID) {
263
- const networkAddress = entry.nwkAddr;
264
- const ieeeAddr = entry.ieeeAddr;
265
- const endpoints = Object.values(entry.endpoints).map((e) => {
266
- return endpoint_1.default.fromDatabaseRecord(e, networkAddress, ieeeAddr, databaseID);
267
- });
268
- const meta = entry.meta ? entry.meta : {};
269
- if (entry.type === 'Group') {
270
- throw new Error('Cannot load device from group');
271
- }
272
- let defaultSendRequestWhen = entry.defaultSendRequestWhen;
273
- /* istanbul ignore next */
274
- if (defaultSendRequestWhen == null) {
275
- // Guess defaultSendRequestWhen based on old useImplicitCheckin/defaultSendWhenActive
276
- if (entry.hasOwnProperty('useImplicitCheckin') && !entry.useImplicitCheckin) {
277
- defaultSendRequestWhen = 'fastpoll';
278
- }
279
- else if (entry.hasOwnProperty('defaultSendWhenActive') && entry.defaultSendWhenActive) {
280
- defaultSendRequestWhen = 'active';
281
- }
282
- else {
283
- defaultSendRequestWhen = 'immediate';
284
- }
285
- }
286
- // default: no timeout (messages expire immediately after first send attempt)
287
- let pendingRequestTimeout = 0;
288
- if ((endpoints.filter((e) => e.supportsInputCluster('genPollCtrl'))).length > 0) {
289
- // default for devices that support genPollCtrl cluster (RX off when idle): 1 day
290
- pendingRequestTimeout = 86400000;
291
- /* istanbul ignore else */
292
- if (entry.hasOwnProperty('checkinInterval')) {
293
- // if the checkin interval is known, messages expire by default after one checkin interval
294
- pendingRequestTimeout = entry.checkinInterval * 1000; // milliseconds
295
- }
296
- }
297
- debug.log(`Request Queue (${ieeeAddr}): default expiration timeout set to ${pendingRequestTimeout}`);
298
- return new Device(databaseID, entry.id, entry.type, ieeeAddr, networkAddress, entry.manufId, endpoints, entry.manufName, entry.powerSource, entry.modelId, entry.appVersion, entry.stackVersion, entry.zclVersion, entry.hwVersion, entry.dateCode, entry.swBuildId, entry.interviewCompleted, meta, entry.lastSeen || null, defaultSendRequestWhen, entry.checkinInterval, pendingRequestTimeout);
299
- }
300
- toDatabaseEntry() {
301
- const epList = this.endpoints.map((e) => e.ID);
302
- const endpoints = {};
303
- for (const endpoint of this.endpoints) {
304
- endpoints[endpoint.ID] = endpoint.toDatabaseRecord();
305
- }
306
- return {
307
- id: this.ID, type: this.type, ieeeAddr: this.ieeeAddr, nwkAddr: this.networkAddress,
308
- manufId: this.manufacturerID, manufName: this.manufacturerName, powerSource: this.powerSource,
309
- modelId: this.modelID, epList, endpoints, appVersion: this.applicationVersion,
310
- stackVersion: this.stackVersion, hwVersion: this.hardwareVersion, dateCode: this.dateCode,
311
- swBuildId: this.softwareBuildID, zclVersion: this.zclVersion, interviewCompleted: this.interviewCompleted,
312
- meta: this.meta, lastSeen: this.lastSeen, defaultSendRequestWhen: this.defaultSendRequestWhen,
313
- checkinInterval: this.checkinInterval
314
- };
315
- }
316
- save(writeDatabase = true) {
317
- entity_1.default.getDatabaseByID(this.databaseID).update(this.toDatabaseEntry(), writeDatabase);
318
- }
319
- static loadFromDatabaseIfNecessary() {
320
- entity_1.default.databases.forEach(database => {
321
- if (!Device.devices.get(database.id)) {
322
- Device.devices.set(database.id, {});
323
- const entries = database.getEntries(['Coordinator', 'EndDevice', 'Router', 'GreenPower', 'Unknown']);
324
- for (const entry of entries) {
325
- const device = Device.fromDatabaseEntry(entry, database.id);
326
- Device.devices.get(database.id)[device.ieeeAddr] = device;
327
- }
328
- }
329
- });
330
- }
331
- static byIeeeAddr(databaseID, ieeeAddr, includeDeleted = false) {
332
- Device.loadFromDatabaseIfNecessary();
333
- const device = Device.devices.get(databaseID)[ieeeAddr];
334
- return (device === null || device === void 0 ? void 0 : device._deleted) && !includeDeleted ? undefined : device;
335
- }
336
- static byNetworkAddress(networkAddress, databaseID) {
337
- return Device.allByDatabaseID(databaseID).find(d => d.networkAddress === networkAddress);
338
- }
339
- static byType(type, databaseID) {
340
- return Device.allByDatabaseID(databaseID).filter(d => d.type === type);
341
- }
342
- // public static all(): Device[] {
343
- // Device.loadFromDatabaseIfNecessary();
344
- // const devices: Device[] = [];
345
- // Device.devices.forEach(deviceDB => {
346
- // devices.push(...Object.values(deviceDB).filter(d => !d._deleted));
347
- // });
348
- // return devices;
349
- // }
350
- static allByDatabaseID(databaseID) {
351
- Device.loadFromDatabaseIfNecessary();
352
- return Object.values(Device.devices.get(databaseID)).filter(d => !d._deleted);
353
- }
354
- undelete(interviewCompleted) {
355
- (0, assert_1.default)(this._deleted, `Device '${this.ieeeAddr}' is not deleted`);
356
- this._deleted = false;
357
- this._interviewCompleted = interviewCompleted !== null && interviewCompleted !== void 0 ? interviewCompleted : this._interviewCompleted;
358
- entity_1.default.getDatabaseByID(this.databaseID).insert(this.toDatabaseEntry());
359
- }
360
- static create(type, ieeeAddr, networkAddress, manufacturerID, manufacturerName, powerSource, modelID, interviewCompleted, endpoints, databaseID) {
361
- Device.loadFromDatabaseIfNecessary();
362
- const database = entity_1.default.getDatabaseByID(databaseID);
363
- if (Device.devices.get(databaseID)[ieeeAddr] && !Device.devices.get(databaseID)[ieeeAddr]._deleted) {
364
- throw new Error(`Device with ieeeAddr '${ieeeAddr}' already exists`);
365
- }
366
- const endpointsMapped = endpoints.map((e) => {
367
- return endpoint_1.default.create(databaseID, e.ID, e.profileID, e.deviceID, e.inputClusters, e.outputClusters, networkAddress, ieeeAddr);
368
- });
369
- const ID = database.newID();
370
- const device = new Device(databaseID, ID, type, ieeeAddr, networkAddress, manufacturerID, endpointsMapped, manufacturerName, powerSource, modelID, undefined, undefined, undefined, undefined, undefined, undefined, interviewCompleted, {}, null, 'immediate', undefined, 0);
371
- database.insert(device.toDatabaseEntry());
372
- Device.devices.get(databaseID)[device.ieeeAddr] = device;
373
- return device;
374
- }
375
- /*
376
- * Zigbee functions
377
- */
378
- async interview() {
379
- if (this.interviewing) {
380
- const message = `Interview - interview already in progress for '${this.ieeeAddr}'`;
381
- debug.log(message);
382
- throw new Error(message);
383
- }
384
- let error;
385
- this._interviewing = true;
386
- debug.log(`Interview - start device '${this.ieeeAddr}'`);
387
- try {
388
- await this.interviewInternal();
389
- debug.log(`Interview - completed for device '${this.ieeeAddr}'`);
390
- this._interviewCompleted = true;
391
- }
392
- catch (e) {
393
- if (this.interviewQuirks()) {
394
- debug.log(`Interview - completed for device '${this.ieeeAddr}' because of quirks ('${e}')`);
395
- }
396
- else {
397
- debug.log(`Interview - failed for device '${this.ieeeAddr}' with error '${e.stack}'`);
398
- error = e;
399
- }
400
- }
401
- finally {
402
- this._interviewing = false;
403
- this.save();
404
- }
405
- if (error) {
406
- throw error;
407
- }
408
- }
409
- interviewQuirks() {
410
- var _a, _b, _c;
411
- debug.log(`Interview - quirks check for '${this.modelID}'-'${this.manufacturerName}'-'${this.type}'`);
412
- // TuYa devices are typically hard to interview. They also don't require a full interview to work correctly
413
- // e.g. no ias enrolling is required for the devices to work.
414
- // Assume that in case we got both the manufacturerName and modelID the device works correctly.
415
- // https://github.com/Koenkk/zigbee2mqtt/issues/7564:
416
- // Fails during ias enroll due to UNSUPPORTED_ATTRIBUTE
417
- // https://github.com/Koenkk/zigbee2mqtt/issues/4655
418
- // Device does not change zoneState after enroll (event with original gateway)
419
- // modelID is mostly in the form of e.g. TS0202 and manufacturerName like e.g. _TYZB01_xph99wvr
420
- if (((_a = this.modelID) === null || _a === void 0 ? void 0 : _a.match('^TS\\d*$')) &&
421
- (((_b = this.manufacturerName) === null || _b === void 0 ? void 0 : _b.match('^_TZ.*_.*$')) || ((_c = this.manufacturerName) === null || _c === void 0 ? void 0 : _c.match('^_TYZB01_.*$')))) {
422
- this._powerSource = this._powerSource || 'Battery';
423
- this._interviewing = false;
424
- this._interviewCompleted = true;
425
- debug.log(`Interview - quirks matched for TuYa end device`);
426
- return true;
427
- }
428
- // Some devices, e.g. Xiaomi end devices have a different interview procedure, after pairing they
429
- // report it's modelID trough a readResponse. The readResponse is received by the controller and set
430
- // on the device.
431
- const lookup = {
432
- '^3R.*?Z': {
433
- type: 'EndDevice', powerSource: 'Battery'
434
- },
435
- 'lumi\..*': {
436
- type: 'EndDevice', manufacturerID: 4151, manufacturerName: 'LUMI', powerSource: 'Battery'
437
- },
438
- 'TERNCY-PP01': {
439
- type: 'EndDevice', manufacturerID: 4648, manufacturerName: 'TERNCY', powerSource: 'Battery'
440
- },
441
- '3RWS18BZ': {},
442
- 'MULTI-MECI--EA01': {},
443
- 'MOT003': {}, // https://github.com/Koenkk/zigbee2mqtt/issues/12471
444
- };
445
- const match = Object.keys(lookup).find((key) => this.modelID && this.modelID.match(key));
446
- if (match) {
447
- const info = lookup[match];
448
- debug.log(`Interview procedure failed but got modelID matching '${match}', assuming interview succeeded`);
449
- this._type = this._type === 'Unknown' ? info.type : this._type;
450
- this._manufacturerID = this._manufacturerID || info.manufacturerID;
451
- this._manufacturerName = this._manufacturerName || info.manufacturerName;
452
- this._powerSource = this._powerSource || info.powerSource;
453
- this._interviewing = false;
454
- this._interviewCompleted = true;
455
- debug.log(`Interview - quirks matched on '${match}'`);
456
- return true;
457
- }
458
- else {
459
- debug.log('Interview - quirks did not match');
460
- return false;
461
- }
462
- }
463
- async interviewInternal() {
464
- const nodeDescriptorQuery = async () => {
465
- const nodeDescriptor = await entity_1.default.getAdapterByID(this.databaseID).nodeDescriptor(this.networkAddress);
466
- this._manufacturerID = nodeDescriptor.manufacturerCode;
467
- this._type = nodeDescriptor.type;
468
- debug.log(`Interview - got node descriptor for device '${this.ieeeAddr}'`);
469
- };
470
- const hasNodeDescriptor = () => this._manufacturerID != null && this._type != null;
471
- if (!hasNodeDescriptor()) {
472
- for (let attempt = 0; attempt < 6; attempt++) {
473
- try {
474
- await nodeDescriptorQuery();
475
- break;
476
- }
477
- catch (error) {
478
- if (this.interviewQuirks()) {
479
- debug.log(`Interview - completed for device '${this.ieeeAddr}' because of quirks ('${error}')`);
480
- return;
481
- }
482
- else {
483
- // Most of the times the first node descriptor query fails and the seconds one succeeds.
484
- debug.log(`Interview - node descriptor request failed for '${this.ieeeAddr}', attempt ${attempt + 1}`);
485
- }
486
- }
487
- }
488
- }
489
- else {
490
- debug.log(`Interview - skip node descriptor request for '${this.ieeeAddr}', already got it`);
491
- }
492
- if (!hasNodeDescriptor()) {
493
- throw new Error(`Interview failed because can not get node descriptor ('${this.ieeeAddr}')`);
494
- }
495
- if (this.manufacturerID === 4619 && this._type === 'EndDevice') {
496
- // Give TuYa end device some time to pair. Otherwise they leave immediately.
497
- // https://github.com/Koenkk/zigbee2mqtt/issues/5814
498
- debug.log("Interview - Detected TuYa end device, waiting 10 seconds...");
499
- await (0, utils_1.Wait)(10000);
500
- }
501
- else if ([0, 4098].includes(this.manufacturerID)) {
502
- // Potentially a TuYa device, some sleep fast so make sure to read the modelId and manufacturerName quickly.
503
- // In case the device responds, the endoint and modelID/manufacturerName are set
504
- // in controller.onZclOrRawData()
505
- // https://github.com/Koenkk/zigbee2mqtt/issues/7553
506
- debug.log("Interview - Detected potential TuYa end device, reading modelID and manufacturerName...");
507
- try {
508
- const endpoint = endpoint_1.default.create(this.databaseID, 1, undefined, undefined, [], [], this.networkAddress, this.ieeeAddr);
509
- const result = await endpoint.read('genBasic', ['modelId', 'manufacturerName'], { sendWhen: 'immediate' });
510
- Object.entries(result)
511
- .forEach((entry) => Device.ReportablePropertiesMapping[entry[0]].set(entry[1], this));
512
- }
513
- catch (error) {
514
- /* istanbul ignore next */
515
- debug.log(`Interview - TuYa read modelID and manufacturerName failed (${error})`);
516
- }
517
- }
518
- // e.g. Xiaomi Aqara Opple devices fail to respond to the first active endpoints request, therefore try 2 times
519
- // https://github.com/Koenkk/zigbee-herdsman/pull/103
520
- const adapter = entity_1.default.getAdapterByID(this.databaseID);
521
- let activeEndpoints;
522
- for (let attempt = 0; attempt < 2; attempt++) {
523
- try {
524
- activeEndpoints = await adapter.activeEndpoints(this.networkAddress);
525
- break;
526
- }
527
- catch (error) {
528
- debug.log(`Interview - active endpoints request failed for '${this.ieeeAddr}', attempt ${attempt + 1}`);
529
- }
530
- }
531
- if (!activeEndpoints) {
532
- throw new Error(`Interview failed because can not get active endpoints ('${this.ieeeAddr}')`);
533
- }
534
- // Make sure that the endpoint are sorted.
535
- activeEndpoints.endpoints.sort((a, b) => a - b);
536
- // Some devices, e.g. TERNCY return endpoint 0 in the active endpoints request.
537
- // This is not a valid endpoint number according to the ZCL, requesting a simple descriptor will result
538
- // into an error. Therefore we filter it, more info: https://github.com/Koenkk/zigbee-herdsman/issues/82
539
- activeEndpoints.endpoints.filter((e) => e !== 0 && !this.getEndpoint(e)).forEach((e) => this._endpoints.push(endpoint_1.default.create(this.databaseID, e, undefined, undefined, [], [], this.networkAddress, this.ieeeAddr)));
540
- debug.log(`Interview - got active endpoints for device '${this.ieeeAddr}'`);
541
- for (const endpointID of activeEndpoints.endpoints.filter((e) => e !== 0)) {
542
- const endpoint = this.getEndpoint(endpointID);
543
- const simpleDescriptor = await adapter.simpleDescriptor(this.networkAddress, endpoint.ID);
544
- endpoint.profileID = simpleDescriptor.profileID;
545
- endpoint.deviceID = simpleDescriptor.deviceID;
546
- endpoint.inputClusters = simpleDescriptor.inputClusters;
547
- endpoint.outputClusters = simpleDescriptor.outputClusters;
548
- debug.log(`Interview - got simple descriptor for endpoint '${endpoint.ID}' device '${this.ieeeAddr}'`);
549
- // Read attributes, nice to have but not required for succesfull pairing as most of the attributes
550
- // are not mandatory in ZCL specification.
551
- if (endpoint.supportsInputCluster('genBasic')) {
552
- for (const [key, item] of Object.entries(Device.ReportablePropertiesMapping)) {
553
- if (!this[item.key]) {
554
- try {
555
- let result;
556
- try {
557
- result = await endpoint.read('genBasic', [key], { sendWhen: 'immediate' });
558
- }
559
- catch (error) {
560
- // Reading attributes can fail for many reason, e.g. it could be that device rejoins
561
- // while joining like in:
562
- // https://github.com/Koenkk/zigbee-herdsman-converters/issues/2485.
563
- // The modelID and manufacturerName are crucial for device identification, so retry.
564
- if (item.key === 'modelID' || item.key === 'manufacturerName') {
565
- debug.log(`Interview - first ${item.key} retrieval attempt failed, ` +
566
- `retrying after 10 seconds...`);
567
- await (0, utils_1.Wait)(10000);
568
- result = await endpoint.read('genBasic', [key], { sendWhen: 'immediate' });
569
- }
570
- else {
571
- throw error;
572
- }
573
- }
574
- item.set(result[key], this);
575
- debug.log(`Interview - got '${item.key}' for device '${this.ieeeAddr}'`);
576
- }
577
- catch (error) {
578
- debug.log(`Interview - failed to read attribute '${item.key}' from ` +
579
- `endpoint '${endpoint.ID}' (${error})`);
580
- }
581
- }
582
- }
583
- }
584
- }
585
- const coordinator = Device.byType('Coordinator', this.databaseID)[0];
586
- // Enroll IAS device
587
- for (const endpoint of this.endpoints.filter((e) => e.supportsInputCluster('ssIasZone'))) {
588
- debug.log(`Interview - IAS - enrolling '${this.ieeeAddr}' endpoint '${endpoint.ID}'`);
589
- const stateBefore = await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState'], { sendWhen: 'immediate' });
590
- debug.log(`Interview - IAS - before enrolling state: '${JSON.stringify(stateBefore)}'`);
591
- // Do not enroll when device has already been enrolled
592
- if (stateBefore.zoneState !== 1 || stateBefore.iasCieAddr !== coordinator.ieeeAddr) {
593
- debug.log(`Interview - IAS - not enrolled, enrolling`);
594
- await endpoint.write('ssIasZone', { 'iasCieAddr': coordinator.ieeeAddr }, { sendWhen: 'immediate' });
595
- debug.log(`Interview - IAS - wrote iasCieAddr`);
596
- // There are 2 enrollment procedures:
597
- // - Auto enroll: coordinator has to send enrollResponse without receiving an enroll request
598
- // this case is handled below.
599
- // - Manual enroll: coordinator replies to enroll request with an enroll response.
600
- // this case in hanled in onZclData().
601
- // https://github.com/Koenkk/zigbee2mqtt/issues/4569#issuecomment-706075676
602
- await (0, utils_1.Wait)(500);
603
- debug.log(`IAS - '${this.ieeeAddr}' sending enroll response (auto enroll)`);
604
- const payload = { enrollrspcode: 0, zoneid: 23 };
605
- await endpoint.command('ssIasZone', 'enrollRsp', payload, { disableDefaultResponse: true, sendWhen: 'immediate' });
606
- let enrolled = false;
607
- for (let attempt = 0; attempt < 20; attempt++) {
608
- await (0, utils_1.Wait)(500);
609
- const stateAfter = await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState'], { sendWhen: 'immediate' });
610
- debug.log(`Interview - IAS - after enrolling state (${attempt}): '${JSON.stringify(stateAfter)}'`);
611
- if (stateAfter.zoneState === 1) {
612
- enrolled = true;
613
- break;
614
- }
615
- }
616
- if (enrolled) {
617
- debug.log(`Interview - IAS successfully enrolled '${this.ieeeAddr}' endpoint '${endpoint.ID}'`);
618
- }
619
- else {
620
- throw new Error(`Interview failed because of failed IAS enroll (zoneState didn't change ('${this.ieeeAddr}')`);
621
- }
622
- }
623
- else {
624
- debug.log(`Interview - IAS - already enrolled, skipping enroll`);
625
- }
626
- }
627
- // Bind poll control
628
- try {
629
- for (const endpoint of this.endpoints.filter((e) => e.supportsInputCluster('genPollCtrl'))) {
630
- debug.log(`Interview - Poll control - binding '${this.ieeeAddr}' endpoint '${endpoint.ID}'`);
631
- await endpoint.bind('genPollCtrl', coordinator.endpoints[0]);
632
- const pollPeriod = await endpoint.read('genPollCtrl', ['checkinInterval'], { sendWhen: 'immediate' });
633
- this._checkinInterval = pollPeriod.checkinInterval / 4; // convert to seconds
634
- this.pendingRequestTimeout = this._checkinInterval * 1000; // milliseconds
635
- if (pollPeriod.checkinInterval <= 2400) { // 10 minutes
636
- this.defaultSendRequestWhen = 'fastpoll';
637
- }
638
- else {
639
- this.defaultSendRequestWhen = 'active';
640
- }
641
- }
642
- }
643
- catch (error) {
644
- /* istanbul ignore next */
645
- debug.log(`Interview - failed to bind genPollCtrl (${error})`);
646
- }
647
- }
648
- async removeFromNetwork() {
649
- if (this._type === 'GreenPower') {
650
- const payload = {
651
- options: 0x002550,
652
- srcID: Number(this.ieeeAddr),
653
- };
654
- const frame = Zcl.ZclFrame.create(Zcl.FrameType.SPECIFIC, Zcl.Direction.SERVER_TO_CLIENT, true, null, zclTransactionSequenceNumber_1.default.next(), 'pairing', 33, payload);
655
- await entity_1.default.getAdapterByID(this.databaseID).sendZclFrameToAll(242, frame, 242);
656
- }
657
- else
658
- await entity_1.default.getAdapterByID(this.databaseID).removeDevice(this.networkAddress, this.ieeeAddr);
659
- await this.removeFromDatabase();
660
- }
661
- async removeFromDatabase() {
662
- Device.loadFromDatabaseIfNecessary();
663
- const database = entity_1.default.getDatabaseByID(this.databaseID);
664
- for (const endpoint of this.endpoints) {
665
- endpoint.removeFromAllGroupsDatabase();
666
- }
667
- if (database.has(this.ID)) {
668
- database.remove(this.ID);
669
- }
670
- this._deleted = true;
671
- // Clear all data in case device joins again
672
- this._interviewCompleted = false;
673
- this._interviewing = false;
674
- this.meta = {};
675
- const newEndpoints = [];
676
- for (const endpoint of this.endpoints) {
677
- newEndpoints.push(endpoint_1.default.create(database.id, endpoint.ID, endpoint.profileID, endpoint.deviceID, endpoint.inputClusters, endpoint.outputClusters, this.networkAddress, this.ieeeAddr));
678
- }
679
- this._endpoints = newEndpoints;
680
- }
681
- async lqi() {
682
- return entity_1.default.getAdapterByID(this.databaseID).lqi(this.networkAddress);
683
- }
684
- async routingTable() {
685
- return entity_1.default.getAdapterByID(this.databaseID).routingTable(this.networkAddress);
686
- }
687
- async ping(disableRecovery = true) {
688
- // Zigbee does not have an official pining mechamism. Use a read request
689
- // of a mandatory basic cluster attribute to keep it as lightweight as
690
- // possible.
691
- await this.endpoints[0].read('genBasic', ['zclVersion'], { disableRecovery });
692
- }
693
- }
694
- // This lookup contains all devices that are queried from the database, this is to ensure that always
695
- // the same instance is returned.
696
- Device.devices = new Map();
697
- Device.ReportablePropertiesMapping = {
698
- modelId: { key: 'modelID', set: (v, d) => { d.modelID = v; } },
699
- manufacturerName: { key: 'manufacturerName', set: (v, d) => { d.manufacturerName = v; } },
700
- powerSource: { key: 'powerSource', set: (v, d) => { d.powerSource = v; } },
701
- zclVersion: { key: 'zclVersion', set: (v, d) => { d.zclVersion = v; } },
702
- appVersion: { key: 'applicationVersion', set: (v, d) => { d.applicationVersion = v; } },
703
- stackVersion: { key: 'stackVersion', set: (v, d) => { d.stackVersion = v; } },
704
- hwVersion: { key: 'hardwareVersion', set: (v, d) => { d.hardwareVersion = v; } },
705
- dateCode: { key: 'dateCode', set: (v, d) => { d.dateCode = v; } },
706
- swBuildId: { key: 'softwareBuildID', set: (v, d) => { d.softwareBuildID = v; } },
707
- };
708
- exports.default = Device;
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const zclTransactionSequenceNumber_1 = __importDefault(require("../helpers/zclTransactionSequenceNumber"));
30
+ const endpoint_1 = __importDefault(require("./endpoint"));
31
+ const entity_1 = __importDefault(require("./entity"));
32
+ const utils_1 = require("../../utils");
33
+ const debug_1 = __importDefault(require("debug"));
34
+ const Zcl = __importStar(require("../../zcl"));
35
+ const assert_1 = __importDefault(require("assert"));
36
+ const helpers_1 = require("../helpers");
37
+ /**
38
+ * @ignore
39
+ */
40
+ const OneJanuary2000 = new Date('January 01, 2000 00:00:00 UTC+00:00').getTime();
41
+ const debug = {
42
+ error: (0, debug_1.default)('zigbee-herdsman:controller:device:error'),
43
+ log: (0, debug_1.default)('zigbee-herdsman:controller:device:log'),
44
+ };
45
+ class Device extends entity_1.default {
46
+ // Getters/setters
47
+ get ieeeAddr() { return this._ieeeAddr; }
48
+ set ieeeAddr(ieeeAddr) { this._ieeeAddr = ieeeAddr; }
49
+ get applicationVersion() { return this._applicationVersion; }
50
+ set applicationVersion(applicationVersion) { this._applicationVersion = applicationVersion; }
51
+ get endpoints() { return this._endpoints; }
52
+ get interviewCompleted() { return this._interviewCompleted; }
53
+ get interviewing() { return this._interviewing; }
54
+ get lastSeen() { return this._lastSeen; }
55
+ get manufacturerID() { return this._manufacturerID; }
56
+ get isDeleted() { return this._deleted; }
57
+ set type(type) { this._type = type; }
58
+ get type() { return this._type; }
59
+ get dateCode() { return this._dateCode; }
60
+ set dateCode(dateCode) { this._dateCode = dateCode; }
61
+ set hardwareVersion(hardwareVersion) { this._hardwareVersion = hardwareVersion; }
62
+ get hardwareVersion() { return this._hardwareVersion; }
63
+ get manufacturerName() { return this._manufacturerName; }
64
+ set manufacturerName(manufacturerName) { this._manufacturerName = manufacturerName; }
65
+ set modelID(modelID) { this._modelID = modelID; }
66
+ get modelID() { return this._modelID; }
67
+ get networkAddress() { return this._networkAddress; }
68
+ set networkAddress(networkAddress) {
69
+ this._networkAddress = networkAddress;
70
+ for (const endpoint of this._endpoints) {
71
+ endpoint.deviceNetworkAddress = networkAddress;
72
+ }
73
+ }
74
+ get powerSource() { return this._powerSource; }
75
+ set powerSource(powerSource) {
76
+ this._powerSource = typeof powerSource === 'number' ? Zcl.PowerSource[powerSource & ~(1 << 7)] : powerSource;
77
+ }
78
+ get softwareBuildID() { return this._softwareBuildID; }
79
+ set softwareBuildID(softwareBuildID) { this._softwareBuildID = softwareBuildID; }
80
+ get stackVersion() { return this._stackVersion; }
81
+ set stackVersion(stackVersion) { this._stackVersion = stackVersion; }
82
+ get zclVersion() { return this._zclVersion; }
83
+ set zclVersion(zclVersion) { this._zclVersion = zclVersion; }
84
+ get linkquality() { return this._linkquality; }
85
+ set linkquality(linkquality) { this._linkquality = linkquality; }
86
+ get skipDefaultResponse() { return this._skipDefaultResponse; }
87
+ set skipDefaultResponse(skipDefaultResponse) { this._skipDefaultResponse = skipDefaultResponse; }
88
+ get skipTimeResponse() { return this._skipTimeResponse; }
89
+ set skipTimeResponse(skipTimeResponse) { this._skipTimeResponse = skipTimeResponse; }
90
+ get defaultSendRequestWhen() { return this._defaultSendRequestWhen; }
91
+ set defaultSendRequestWhen(defaultSendRequestWhen) {
92
+ this._defaultSendRequestWhen = defaultSendRequestWhen;
93
+ }
94
+ get checkinInterval() { return this._checkinInterval; }
95
+ get pendingRequestTimeout() { return this._pendingRequestTimeout; }
96
+ set pendingRequestTimeout(pendingRequestTimeout) { this._pendingRequestTimeout = pendingRequestTimeout; }
97
+ constructor(databaseID, ID, type, ieeeAddr, networkAddress, manufacturerID, endpoints, manufacturerName, powerSource, modelID, applicationVersion, stackVersion, zclVersion, hardwareVersion, dateCode, softwareBuildID, interviewCompleted, meta, lastSeen, defaultSendRequestWhen, checkinInterval, pendingRequestTimeout) {
98
+ super();
99
+ this.databaseID = databaseID;
100
+ this.ID = ID;
101
+ this._type = type;
102
+ this.ieeeAddr = ieeeAddr;
103
+ this._networkAddress = networkAddress;
104
+ this._manufacturerID = manufacturerID;
105
+ this._endpoints = endpoints;
106
+ this._manufacturerName = manufacturerName;
107
+ this._powerSource = powerSource;
108
+ this._modelID = modelID;
109
+ this._applicationVersion = applicationVersion;
110
+ this._stackVersion = stackVersion;
111
+ this._zclVersion = zclVersion;
112
+ this.hardwareVersion = hardwareVersion;
113
+ this._dateCode = dateCode;
114
+ this._softwareBuildID = softwareBuildID;
115
+ this._interviewCompleted = interviewCompleted;
116
+ this._interviewing = false;
117
+ this._skipDefaultResponse = false;
118
+ this._skipTimeResponse = false;
119
+ this.meta = meta;
120
+ this._lastSeen = lastSeen;
121
+ this._defaultSendRequestWhen = defaultSendRequestWhen;
122
+ this._checkinInterval = checkinInterval;
123
+ this._pendingRequestTimeout = pendingRequestTimeout;
124
+ }
125
+ createEndpoint(ID) {
126
+ if (this.getEndpoint(ID)) {
127
+ throw new Error(`Device '${this.ieeeAddr}' already has an endpoint '${ID}'`);
128
+ }
129
+ const endpoint = endpoint_1.default.create(this.databaseID, ID, undefined, undefined, [], [], this.networkAddress, this.ieeeAddr);
130
+ this.endpoints.push(endpoint);
131
+ this.save();
132
+ return endpoint;
133
+ }
134
+ changeIeeeAddress(ieeeAddr) {
135
+ delete Device.devices.get(this.databaseID)[this.ieeeAddr];
136
+ this.ieeeAddr = ieeeAddr;
137
+ Device.devices.get(this.databaseID)[this.ieeeAddr] = this;
138
+ this.endpoints.forEach((e) => e.deviceIeeeAddress = ieeeAddr);
139
+ this.save();
140
+ }
141
+ getEndpoint(ID) {
142
+ return this.endpoints.find((e) => e.ID === ID);
143
+ }
144
+ // There might be multiple endpoints with same DeviceId but it is not supported and first endpoint is returned
145
+ getEndpointByDeviceType(deviceType) {
146
+ const deviceID = Zcl.EndpointDeviceType[deviceType];
147
+ return this.endpoints.find((d) => d.deviceID === deviceID);
148
+ }
149
+ implicitCheckin() {
150
+ this.endpoints.forEach(async (e) => e.sendPendingRequests(false));
151
+ }
152
+ updateLastSeen() {
153
+ this._lastSeen = Date.now();
154
+ }
155
+ hasPendingRequests() {
156
+ return this.endpoints.find(e => e.hasPendingRequests()) !== undefined;
157
+ }
158
+ async onZclData(dataPayload, endpoint) {
159
+ var _a;
160
+ const frame = dataPayload.frame;
161
+ // Update reportable properties
162
+ if (frame.isCluster('genBasic') && (frame.isCommand('readRsp') || frame.isCommand('report'))) {
163
+ for (const [key, value] of Object.entries(helpers_1.ZclFrameConverter.attributeKeyValue(frame))) {
164
+ (_a = Device.ReportablePropertiesMapping[key]) === null || _a === void 0 ? void 0 : _a.set(value, this);
165
+ }
166
+ }
167
+ // Respond to enroll requests
168
+ if (frame.isSpecific() && frame.isCluster('ssIasZone') && frame.isCommand('enrollReq')) {
169
+ debug.log(`IAS - '${this.ieeeAddr}' responding to enroll response`);
170
+ const payload = { enrollrspcode: 0, zoneid: 23 };
171
+ await endpoint.command('ssIasZone', 'enrollRsp', payload, { disableDefaultResponse: true });
172
+ }
173
+ // Reponse to read requests
174
+ if (frame.isGlobal() && frame.isCommand('read')) {
175
+ const time = Math.round(((new Date()).getTime() - OneJanuary2000) / 1000);
176
+ const attributes = {
177
+ ...endpoint.clusters,
178
+ genTime: { attributes: {
179
+ timeStatus: 3,
180
+ time: time,
181
+ timeZone: ((new Date()).getTimezoneOffset() * -1) * 60,
182
+ localTime: time - (new Date()).getTimezoneOffset() * 60,
183
+ lastSetTime: time,
184
+ validUntilTime: time + (24 * 60 * 60), // valid for 24 hours
185
+ } },
186
+ };
187
+ if (frame.Cluster.name in attributes && (frame.Cluster.name !== 'genTime' || !this._skipTimeResponse)) {
188
+ const response = {};
189
+ for (const entry of frame.Payload) {
190
+ if (frame.Cluster.hasAttribute(entry.attrId)) {
191
+ const name = frame.Cluster.getAttribute(entry.attrId).name;
192
+ if (name in attributes[frame.Cluster.name].attributes) {
193
+ response[name] = attributes[frame.Cluster.name].attributes[name];
194
+ }
195
+ }
196
+ }
197
+ try {
198
+ await endpoint.readResponse(frame.Cluster.ID, frame.Header.transactionSequenceNumber, response, { srcEndpoint: dataPayload.destinationEndpoint });
199
+ }
200
+ catch (error) {
201
+ debug.error(`Read response to ${this.ieeeAddr} failed`);
202
+ }
203
+ }
204
+ }
205
+ // Handle check-in from sleeping end devices
206
+ if (frame.isSpecific() && frame.isCluster("genPollCtrl") && frame.isCommand("checkin")) {
207
+ try {
208
+ if (this.hasPendingRequests() || (this._checkinInterval === undefined)) {
209
+ const payload = {
210
+ startFastPolling: true,
211
+ fastPollTimeout: 0,
212
+ };
213
+ debug.log(`check-in from ${this.ieeeAddr}: accepting fast-poll`);
214
+ await endpoint.command(frame.Cluster.ID, 'checkinRsp', payload, { sendWhen: 'immediate' });
215
+ // This is a good time to read the checkin interval if we haven't stored it previously
216
+ if (this._checkinInterval === undefined) {
217
+ const pollPeriod = await endpoint.read('genPollCtrl', ['checkinInterval'], { sendWhen: 'immediate' });
218
+ this._checkinInterval = pollPeriod.checkinInterval / 4; // convert to seconds
219
+ this.pendingRequestTimeout = this._checkinInterval * 1000; // milliseconds
220
+ debug.log(`Request Queue (${this.ieeeAddr}): default expiration timeout set to ${this.pendingRequestTimeout}`);
221
+ }
222
+ await Promise.all(this.endpoints.map(async (e) => e.sendPendingRequests(true)));
223
+ // We *must* end fast-poll when we're done sending things. Otherwise
224
+ // we cause undue power-drain.
225
+ debug.log(`check-in from ${this.ieeeAddr}: stopping fast-poll`);
226
+ await endpoint.command(frame.Cluster.ID, 'fastPollStop', {}, { sendWhen: 'immediate' });
227
+ }
228
+ else {
229
+ const payload = {
230
+ startFastPolling: false,
231
+ fastPollTimeout: 0,
232
+ };
233
+ debug.log(`check-in from ${this.ieeeAddr}: declining fast-poll`);
234
+ await endpoint.command(frame.Cluster.ID, 'checkinRsp', payload, { sendWhen: 'immediate' });
235
+ }
236
+ }
237
+ catch (error) {
238
+ /* istanbul ignore next */
239
+ debug.error(`Handling of poll check-in form ${this.ieeeAddr} failed`);
240
+ }
241
+ }
242
+ // Send a default response if necessary.
243
+ const isDefaultResponse = frame.isGlobal() && frame.getCommand().name === 'defaultRsp';
244
+ const commandHasResponse = frame.getCommand().hasOwnProperty('response');
245
+ const disableDefaultResponse = frame.Header.frameControl.disableDefaultResponse;
246
+ // Sometimes messages are received twice, prevent responding twice
247
+ const alreadyResponded = this._lastDefaultResponseSequenceNumber === frame.Header.transactionSequenceNumber;
248
+ if (this.type !== 'GreenPower' && !dataPayload.wasBroadcast && !disableDefaultResponse && !isDefaultResponse &&
249
+ !commandHasResponse && !this._skipDefaultResponse && !alreadyResponded) {
250
+ try {
251
+ this._lastDefaultResponseSequenceNumber = frame.Header.transactionSequenceNumber;
252
+ // In the ZCL it is not documented what the direction of the default response should be
253
+ // In https://github.com/Koenkk/zigbee2mqtt/issues/18096 a commandResponse (SERVER_TO_CLIENT)
254
+ // is send and the device expects a CLIENT_TO_SERVER back.
255
+ // Previously SERVER_TO_CLIENT was always used.
256
+ // Therefore for non-global commands we inverse the direction.
257
+ const direction = frame.isGlobal() ? Zcl.Direction.SERVER_TO_CLIENT : (frame.Header.frameControl.direction === Zcl.Direction.CLIENT_TO_SERVER
258
+ ? Zcl.Direction.SERVER_TO_CLIENT : Zcl.Direction.CLIENT_TO_SERVER);
259
+ await endpoint.defaultResponse(frame.getCommand().ID, 0, frame.Cluster.ID, frame.Header.transactionSequenceNumber, { direction });
260
+ }
261
+ catch (error) {
262
+ debug.error(`Default response to ${this.ieeeAddr} failed`);
263
+ }
264
+ }
265
+ }
266
+ /*
267
+ * CRUD
268
+ */
269
+ static fromDatabaseEntry(entry, databaseID) {
270
+ const networkAddress = entry.nwkAddr;
271
+ const ieeeAddr = entry.ieeeAddr;
272
+ const endpoints = Object.values(entry.endpoints).map((e) => {
273
+ return endpoint_1.default.fromDatabaseRecord(e, networkAddress, ieeeAddr, databaseID);
274
+ });
275
+ const meta = entry.meta ? entry.meta : {};
276
+ if (entry.type === 'Group') {
277
+ throw new Error('Cannot load device from group');
278
+ }
279
+ let defaultSendRequestWhen = entry.defaultSendRequestWhen;
280
+ /* istanbul ignore next */
281
+ if (defaultSendRequestWhen == null) {
282
+ // Guess defaultSendRequestWhen based on old useImplicitCheckin/defaultSendWhenActive
283
+ if (entry.hasOwnProperty('useImplicitCheckin') && !entry.useImplicitCheckin) {
284
+ defaultSendRequestWhen = 'fastpoll';
285
+ }
286
+ else if (entry.hasOwnProperty('defaultSendWhenActive') && entry.defaultSendWhenActive) {
287
+ defaultSendRequestWhen = 'active';
288
+ }
289
+ else {
290
+ defaultSendRequestWhen = 'immediate';
291
+ }
292
+ }
293
+ // default: no timeout (messages expire immediately after first send attempt)
294
+ let pendingRequestTimeout = 0;
295
+ if ((endpoints.filter((e) => e.supportsInputCluster('genPollCtrl'))).length > 0) {
296
+ // default for devices that support genPollCtrl cluster (RX off when idle): 1 day
297
+ pendingRequestTimeout = 86400000;
298
+ /* istanbul ignore else */
299
+ if (entry.hasOwnProperty('checkinInterval')) {
300
+ // if the checkin interval is known, messages expire by default after one checkin interval
301
+ pendingRequestTimeout = entry.checkinInterval * 1000; // milliseconds
302
+ }
303
+ }
304
+ debug.log(`Request Queue (${ieeeAddr}): default expiration timeout set to ${pendingRequestTimeout}`);
305
+ return new Device(databaseID, entry.id, entry.type, ieeeAddr, networkAddress, entry.manufId, endpoints, entry.manufName, entry.powerSource, entry.modelId, entry.appVersion, entry.stackVersion, entry.zclVersion, entry.hwVersion, entry.dateCode, entry.swBuildId, entry.interviewCompleted, meta, entry.lastSeen || null, defaultSendRequestWhen, entry.checkinInterval, pendingRequestTimeout);
306
+ }
307
+ toDatabaseEntry() {
308
+ const epList = this.endpoints.map((e) => e.ID);
309
+ const endpoints = {};
310
+ for (const endpoint of this.endpoints) {
311
+ endpoints[endpoint.ID] = endpoint.toDatabaseRecord();
312
+ }
313
+ return {
314
+ id: this.ID, type: this.type, ieeeAddr: this.ieeeAddr, nwkAddr: this.networkAddress,
315
+ manufId: this.manufacturerID, manufName: this.manufacturerName, powerSource: this.powerSource,
316
+ modelId: this.modelID, epList, endpoints, appVersion: this.applicationVersion,
317
+ stackVersion: this.stackVersion, hwVersion: this.hardwareVersion, dateCode: this.dateCode,
318
+ swBuildId: this.softwareBuildID, zclVersion: this.zclVersion, interviewCompleted: this.interviewCompleted,
319
+ meta: this.meta, lastSeen: this.lastSeen, defaultSendRequestWhen: this.defaultSendRequestWhen,
320
+ checkinInterval: this.checkinInterval
321
+ };
322
+ }
323
+ save(writeDatabase = true) {
324
+ entity_1.default.getDatabaseByID(this.databaseID).update(this.toDatabaseEntry(), writeDatabase);
325
+ }
326
+ static loadFromDatabaseIfNecessary() {
327
+ entity_1.default.databases.forEach(database => {
328
+ if (!Device.devices.get(database.id)) {
329
+ Device.devices.set(database.id, {});
330
+ const entries = database.getEntries(['Coordinator', 'EndDevice', 'Router', 'GreenPower', 'Unknown']);
331
+ for (const entry of entries) {
332
+ const device = Device.fromDatabaseEntry(entry, database.id);
333
+ Device.devices.get(database.id)[device.ieeeAddr] = device;
334
+ }
335
+ }
336
+ });
337
+ }
338
+ static byIeeeAddr(databaseID, ieeeAddr, includeDeleted = false) {
339
+ Device.loadFromDatabaseIfNecessary();
340
+ const device = Device.devices.get(databaseID)[ieeeAddr];
341
+ return (device === null || device === void 0 ? void 0 : device._deleted) && !includeDeleted ? undefined : device;
342
+ }
343
+ static byNetworkAddress(networkAddress, databaseID) {
344
+ return Device.allByDatabaseID(databaseID).find(d => d.networkAddress === networkAddress);
345
+ }
346
+ static byType(type, databaseID) {
347
+ return Device.allByDatabaseID(databaseID).filter(d => d.type === type);
348
+ }
349
+ static all() {
350
+ Device.loadFromDatabaseIfNecessary();
351
+ const devices = [];
352
+ Device.devices.forEach(deviceDB => {
353
+ devices.push(...Object.values(deviceDB).filter(d => !d._deleted));
354
+ });
355
+ return devices;
356
+ }
357
+ static allByDatabaseID(databaseID) {
358
+ Device.loadFromDatabaseIfNecessary();
359
+ return Object.values(Device.devices.get(databaseID)).filter(d => !d._deleted);
360
+ }
361
+ undelete(interviewCompleted) {
362
+ (0, assert_1.default)(this._deleted, `Device '${this.ieeeAddr}' is not deleted`);
363
+ this._deleted = false;
364
+ this._interviewCompleted = interviewCompleted !== null && interviewCompleted !== void 0 ? interviewCompleted : this._interviewCompleted;
365
+ entity_1.default.getDatabaseByID(this.databaseID).insert(this.toDatabaseEntry());
366
+ }
367
+ static create(type, ieeeAddr, networkAddress, manufacturerID, manufacturerName, powerSource, modelID, interviewCompleted, endpoints, databaseID) {
368
+ Device.loadFromDatabaseIfNecessary();
369
+ const database = entity_1.default.getDatabaseByID(databaseID);
370
+ if (Device.devices.get(databaseID)[ieeeAddr] && !Device.devices.get(databaseID)[ieeeAddr]._deleted) {
371
+ throw new Error(`Device with ieeeAddr '${ieeeAddr}' already exists`);
372
+ }
373
+ const endpointsMapped = endpoints.map((e) => {
374
+ return endpoint_1.default.create(databaseID, e.ID, e.profileID, e.deviceID, e.inputClusters, e.outputClusters, networkAddress, ieeeAddr);
375
+ });
376
+ const ID = database.newID();
377
+ const device = new Device(databaseID, ID, type, ieeeAddr, networkAddress, manufacturerID, endpointsMapped, manufacturerName, powerSource, modelID, undefined, undefined, undefined, undefined, undefined, undefined, interviewCompleted, {}, null, 'immediate', undefined, 0);
378
+ database.insert(device.toDatabaseEntry());
379
+ Device.devices.get(databaseID)[device.ieeeAddr] = device;
380
+ return device;
381
+ }
382
+ /*
383
+ * Zigbee functions
384
+ */
385
+ async interview() {
386
+ if (this.interviewing) {
387
+ const message = `Interview - interview already in progress for '${this.ieeeAddr}'`;
388
+ debug.log(message);
389
+ throw new Error(message);
390
+ }
391
+ let error;
392
+ this._interviewing = true;
393
+ debug.log(`Interview - start device '${this.ieeeAddr}'`);
394
+ try {
395
+ await this.interviewInternal();
396
+ debug.log(`Interview - completed for device '${this.ieeeAddr}'`);
397
+ this._interviewCompleted = true;
398
+ }
399
+ catch (e) {
400
+ if (this.interviewQuirks()) {
401
+ debug.log(`Interview - completed for device '${this.ieeeAddr}' because of quirks ('${e}')`);
402
+ }
403
+ else {
404
+ debug.log(`Interview - failed for device '${this.ieeeAddr}' with error '${e.stack}'`);
405
+ error = e;
406
+ }
407
+ }
408
+ finally {
409
+ this._interviewing = false;
410
+ this.save();
411
+ }
412
+ if (error) {
413
+ throw error;
414
+ }
415
+ }
416
+ interviewQuirks() {
417
+ var _a, _b, _c;
418
+ debug.log(`Interview - quirks check for '${this.modelID}'-'${this.manufacturerName}'-'${this.type}'`);
419
+ // TuYa devices are typically hard to interview. They also don't require a full interview to work correctly
420
+ // e.g. no ias enrolling is required for the devices to work.
421
+ // Assume that in case we got both the manufacturerName and modelID the device works correctly.
422
+ // https://github.com/Koenkk/zigbee2mqtt/issues/7564:
423
+ // Fails during ias enroll due to UNSUPPORTED_ATTRIBUTE
424
+ // https://github.com/Koenkk/zigbee2mqtt/issues/4655
425
+ // Device does not change zoneState after enroll (event with original gateway)
426
+ // modelID is mostly in the form of e.g. TS0202 and manufacturerName like e.g. _TYZB01_xph99wvr
427
+ if (((_a = this.modelID) === null || _a === void 0 ? void 0 : _a.match('^TS\\d*$')) &&
428
+ (((_b = this.manufacturerName) === null || _b === void 0 ? void 0 : _b.match('^_TZ.*_.*$')) || ((_c = this.manufacturerName) === null || _c === void 0 ? void 0 : _c.match('^_TYZB01_.*$')))) {
429
+ this._powerSource = this._powerSource || 'Battery';
430
+ this._interviewing = false;
431
+ this._interviewCompleted = true;
432
+ debug.log(`Interview - quirks matched for TuYa end device`);
433
+ return true;
434
+ }
435
+ // Some devices, e.g. Xiaomi end devices have a different interview procedure, after pairing they
436
+ // report it's modelID trough a readResponse. The readResponse is received by the controller and set
437
+ // on the device.
438
+ const lookup = {
439
+ '^3R.*?Z': {
440
+ type: 'EndDevice', powerSource: 'Battery'
441
+ },
442
+ 'lumi\..*': {
443
+ type: 'EndDevice', manufacturerID: 4151, manufacturerName: 'LUMI', powerSource: 'Battery'
444
+ },
445
+ 'TERNCY-PP01': {
446
+ type: 'EndDevice', manufacturerID: 4648, manufacturerName: 'TERNCY', powerSource: 'Battery'
447
+ },
448
+ '3RWS18BZ': {},
449
+ 'MULTI-MECI--EA01': {},
450
+ 'MOT003': {}, // https://github.com/Koenkk/zigbee2mqtt/issues/12471
451
+ };
452
+ const match = Object.keys(lookup).find((key) => this.modelID && this.modelID.match(key));
453
+ if (match) {
454
+ const info = lookup[match];
455
+ debug.log(`Interview procedure failed but got modelID matching '${match}', assuming interview succeeded`);
456
+ this._type = this._type === 'Unknown' ? info.type : this._type;
457
+ this._manufacturerID = this._manufacturerID || info.manufacturerID;
458
+ this._manufacturerName = this._manufacturerName || info.manufacturerName;
459
+ this._powerSource = this._powerSource || info.powerSource;
460
+ this._interviewing = false;
461
+ this._interviewCompleted = true;
462
+ debug.log(`Interview - quirks matched on '${match}'`);
463
+ return true;
464
+ }
465
+ else {
466
+ debug.log('Interview - quirks did not match');
467
+ return false;
468
+ }
469
+ }
470
+ async interviewInternal() {
471
+ const nodeDescriptorQuery = async () => {
472
+ const nodeDescriptor = await entity_1.default.getAdapterByID(this.databaseID).nodeDescriptor(this.networkAddress);
473
+ this._manufacturerID = nodeDescriptor.manufacturerCode;
474
+ this._type = nodeDescriptor.type;
475
+ debug.log(`Interview - got node descriptor for device '${this.ieeeAddr}'`);
476
+ };
477
+ const hasNodeDescriptor = () => this._manufacturerID != null && this._type != null;
478
+ if (!hasNodeDescriptor()) {
479
+ for (let attempt = 0; attempt < 6; attempt++) {
480
+ try {
481
+ await nodeDescriptorQuery();
482
+ break;
483
+ }
484
+ catch (error) {
485
+ if (this.interviewQuirks()) {
486
+ debug.log(`Interview - completed for device '${this.ieeeAddr}' because of quirks ('${error}')`);
487
+ return;
488
+ }
489
+ else {
490
+ // Most of the times the first node descriptor query fails and the seconds one succeeds.
491
+ debug.log(`Interview - node descriptor request failed for '${this.ieeeAddr}', attempt ${attempt + 1}`);
492
+ }
493
+ }
494
+ }
495
+ }
496
+ else {
497
+ debug.log(`Interview - skip node descriptor request for '${this.ieeeAddr}', already got it`);
498
+ }
499
+ if (!hasNodeDescriptor()) {
500
+ throw new Error(`Interview failed because can not get node descriptor ('${this.ieeeAddr}')`);
501
+ }
502
+ if (this.manufacturerID === 4619 && this._type === 'EndDevice') {
503
+ // Give TuYa end device some time to pair. Otherwise they leave immediately.
504
+ // https://github.com/Koenkk/zigbee2mqtt/issues/5814
505
+ debug.log("Interview - Detected TuYa end device, waiting 10 seconds...");
506
+ await (0, utils_1.Wait)(10000);
507
+ }
508
+ else if ([0, 4098].includes(this.manufacturerID)) {
509
+ // Potentially a TuYa device, some sleep fast so make sure to read the modelId and manufacturerName quickly.
510
+ // In case the device responds, the endoint and modelID/manufacturerName are set
511
+ // in controller.onZclOrRawData()
512
+ // https://github.com/Koenkk/zigbee2mqtt/issues/7553
513
+ debug.log("Interview - Detected potential TuYa end device, reading modelID and manufacturerName...");
514
+ try {
515
+ const endpoint = endpoint_1.default.create(this.databaseID, 1, undefined, undefined, [], [], this.networkAddress, this.ieeeAddr);
516
+ const result = await endpoint.read('genBasic', ['modelId', 'manufacturerName'], { sendWhen: 'immediate' });
517
+ Object.entries(result)
518
+ .forEach((entry) => Device.ReportablePropertiesMapping[entry[0]].set(entry[1], this));
519
+ }
520
+ catch (error) {
521
+ /* istanbul ignore next */
522
+ debug.log(`Interview - TuYa read modelID and manufacturerName failed (${error})`);
523
+ }
524
+ }
525
+ // e.g. Xiaomi Aqara Opple devices fail to respond to the first active endpoints request, therefore try 2 times
526
+ // https://github.com/Koenkk/zigbee-herdsman/pull/103
527
+ const adapter = entity_1.default.getAdapterByID(this.databaseID);
528
+ let activeEndpoints;
529
+ for (let attempt = 0; attempt < 2; attempt++) {
530
+ try {
531
+ activeEndpoints = await adapter.activeEndpoints(this.networkAddress);
532
+ break;
533
+ }
534
+ catch (error) {
535
+ debug.log(`Interview - active endpoints request failed for '${this.ieeeAddr}', attempt ${attempt + 1}`);
536
+ }
537
+ }
538
+ if (!activeEndpoints) {
539
+ throw new Error(`Interview failed because can not get active endpoints ('${this.ieeeAddr}')`);
540
+ }
541
+ // Make sure that the endpoint are sorted.
542
+ activeEndpoints.endpoints.sort((a, b) => a - b);
543
+ // Some devices, e.g. TERNCY return endpoint 0 in the active endpoints request.
544
+ // This is not a valid endpoint number according to the ZCL, requesting a simple descriptor will result
545
+ // into an error. Therefore we filter it, more info: https://github.com/Koenkk/zigbee-herdsman/issues/82
546
+ activeEndpoints.endpoints.filter((e) => e !== 0 && !this.getEndpoint(e)).forEach((e) => this._endpoints.push(endpoint_1.default.create(this.databaseID, e, undefined, undefined, [], [], this.networkAddress, this.ieeeAddr)));
547
+ debug.log(`Interview - got active endpoints for device '${this.ieeeAddr}'`);
548
+ for (const endpointID of activeEndpoints.endpoints.filter((e) => e !== 0)) {
549
+ const endpoint = this.getEndpoint(endpointID);
550
+ const simpleDescriptor = await adapter.simpleDescriptor(this.networkAddress, endpoint.ID);
551
+ endpoint.profileID = simpleDescriptor.profileID;
552
+ endpoint.deviceID = simpleDescriptor.deviceID;
553
+ endpoint.inputClusters = simpleDescriptor.inputClusters;
554
+ endpoint.outputClusters = simpleDescriptor.outputClusters;
555
+ debug.log(`Interview - got simple descriptor for endpoint '${endpoint.ID}' device '${this.ieeeAddr}'`);
556
+ // Read attributes, nice to have but not required for succesfull pairing as most of the attributes
557
+ // are not mandatory in ZCL specification.
558
+ if (endpoint.supportsInputCluster('genBasic')) {
559
+ for (const [key, item] of Object.entries(Device.ReportablePropertiesMapping)) {
560
+ if (!this[item.key]) {
561
+ try {
562
+ let result;
563
+ try {
564
+ result = await endpoint.read('genBasic', [key], { sendWhen: 'immediate' });
565
+ }
566
+ catch (error) {
567
+ // Reading attributes can fail for many reason, e.g. it could be that device rejoins
568
+ // while joining like in:
569
+ // https://github.com/Koenkk/zigbee-herdsman-converters/issues/2485.
570
+ // The modelID and manufacturerName are crucial for device identification, so retry.
571
+ if (item.key === 'modelID' || item.key === 'manufacturerName') {
572
+ debug.log(`Interview - first ${item.key} retrieval attempt failed, ` +
573
+ `retrying after 10 seconds...`);
574
+ await (0, utils_1.Wait)(10000);
575
+ result = await endpoint.read('genBasic', [key], { sendWhen: 'immediate' });
576
+ }
577
+ else {
578
+ throw error;
579
+ }
580
+ }
581
+ item.set(result[key], this);
582
+ debug.log(`Interview - got '${item.key}' for device '${this.ieeeAddr}'`);
583
+ }
584
+ catch (error) {
585
+ debug.log(`Interview - failed to read attribute '${item.key}' from ` +
586
+ `endpoint '${endpoint.ID}' (${error})`);
587
+ }
588
+ }
589
+ }
590
+ }
591
+ }
592
+ const coordinator = Device.byType('Coordinator', this.databaseID)[0];
593
+ // Enroll IAS device
594
+ for (const endpoint of this.endpoints.filter((e) => e.supportsInputCluster('ssIasZone'))) {
595
+ debug.log(`Interview - IAS - enrolling '${this.ieeeAddr}' endpoint '${endpoint.ID}'`);
596
+ const stateBefore = await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState'], { sendWhen: 'immediate' });
597
+ debug.log(`Interview - IAS - before enrolling state: '${JSON.stringify(stateBefore)}'`);
598
+ // Do not enroll when device has already been enrolled
599
+ if (stateBefore.zoneState !== 1 || stateBefore.iasCieAddr !== coordinator.ieeeAddr) {
600
+ debug.log(`Interview - IAS - not enrolled, enrolling`);
601
+ await endpoint.write('ssIasZone', { 'iasCieAddr': coordinator.ieeeAddr }, { sendWhen: 'immediate' });
602
+ debug.log(`Interview - IAS - wrote iasCieAddr`);
603
+ // There are 2 enrollment procedures:
604
+ // - Auto enroll: coordinator has to send enrollResponse without receiving an enroll request
605
+ // this case is handled below.
606
+ // - Manual enroll: coordinator replies to enroll request with an enroll response.
607
+ // this case in hanled in onZclData().
608
+ // https://github.com/Koenkk/zigbee2mqtt/issues/4569#issuecomment-706075676
609
+ await (0, utils_1.Wait)(500);
610
+ debug.log(`IAS - '${this.ieeeAddr}' sending enroll response (auto enroll)`);
611
+ const payload = { enrollrspcode: 0, zoneid: 23 };
612
+ await endpoint.command('ssIasZone', 'enrollRsp', payload, { disableDefaultResponse: true, sendWhen: 'immediate' });
613
+ let enrolled = false;
614
+ for (let attempt = 0; attempt < 20; attempt++) {
615
+ await (0, utils_1.Wait)(500);
616
+ const stateAfter = await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState'], { sendWhen: 'immediate' });
617
+ debug.log(`Interview - IAS - after enrolling state (${attempt}): '${JSON.stringify(stateAfter)}'`);
618
+ if (stateAfter.zoneState === 1) {
619
+ enrolled = true;
620
+ break;
621
+ }
622
+ }
623
+ if (enrolled) {
624
+ debug.log(`Interview - IAS successfully enrolled '${this.ieeeAddr}' endpoint '${endpoint.ID}'`);
625
+ }
626
+ else {
627
+ throw new Error(`Interview failed because of failed IAS enroll (zoneState didn't change ('${this.ieeeAddr}')`);
628
+ }
629
+ }
630
+ else {
631
+ debug.log(`Interview - IAS - already enrolled, skipping enroll`);
632
+ }
633
+ }
634
+ // Bind poll control
635
+ try {
636
+ for (const endpoint of this.endpoints.filter((e) => e.supportsInputCluster('genPollCtrl'))) {
637
+ debug.log(`Interview - Poll control - binding '${this.ieeeAddr}' endpoint '${endpoint.ID}'`);
638
+ await endpoint.bind('genPollCtrl', coordinator.endpoints[0]);
639
+ const pollPeriod = await endpoint.read('genPollCtrl', ['checkinInterval'], { sendWhen: 'immediate' });
640
+ this._checkinInterval = pollPeriod.checkinInterval / 4; // convert to seconds
641
+ this.pendingRequestTimeout = this._checkinInterval * 1000; // milliseconds
642
+ if (pollPeriod.checkinInterval <= 2400) { // 10 minutes
643
+ this.defaultSendRequestWhen = 'fastpoll';
644
+ }
645
+ else {
646
+ this.defaultSendRequestWhen = 'active';
647
+ }
648
+ }
649
+ }
650
+ catch (error) {
651
+ /* istanbul ignore next */
652
+ debug.log(`Interview - failed to bind genPollCtrl (${error})`);
653
+ }
654
+ }
655
+ async removeFromNetwork() {
656
+ if (this._type === 'GreenPower') {
657
+ const payload = {
658
+ options: 0x002550,
659
+ srcID: Number(this.ieeeAddr),
660
+ };
661
+ const frame = Zcl.ZclFrame.create(Zcl.FrameType.SPECIFIC, Zcl.Direction.SERVER_TO_CLIENT, true, null, zclTransactionSequenceNumber_1.default.next(), 'pairing', 33, payload);
662
+ await entity_1.default.getAdapterByID(this.databaseID).sendZclFrameToAll(242, frame, 242);
663
+ }
664
+ else
665
+ await entity_1.default.getAdapterByID(this.databaseID).removeDevice(this.networkAddress, this.ieeeAddr);
666
+ await this.removeFromDatabase();
667
+ }
668
+ async removeFromDatabase() {
669
+ Device.loadFromDatabaseIfNecessary();
670
+ const database = entity_1.default.getDatabaseByID(this.databaseID);
671
+ for (const endpoint of this.endpoints) {
672
+ endpoint.removeFromAllGroupsDatabase();
673
+ }
674
+ if (database.has(this.ID)) {
675
+ database.remove(this.ID);
676
+ }
677
+ this._deleted = true;
678
+ // Clear all data in case device joins again
679
+ this._interviewCompleted = false;
680
+ this._interviewing = false;
681
+ this.meta = {};
682
+ const newEndpoints = [];
683
+ for (const endpoint of this.endpoints) {
684
+ newEndpoints.push(endpoint_1.default.create(database.id, endpoint.ID, endpoint.profileID, endpoint.deviceID, endpoint.inputClusters, endpoint.outputClusters, this.networkAddress, this.ieeeAddr));
685
+ }
686
+ this._endpoints = newEndpoints;
687
+ }
688
+ async lqi() {
689
+ return entity_1.default.getAdapterByID(this.databaseID).lqi(this.networkAddress);
690
+ }
691
+ async routingTable() {
692
+ return entity_1.default.getAdapterByID(this.databaseID).routingTable(this.networkAddress);
693
+ }
694
+ async ping(disableRecovery = true) {
695
+ var _a;
696
+ // Zigbee does not have an official pining mechamism. Use a read request
697
+ // of a mandatory basic cluster attribute to keep it as lightweight as
698
+ // possible.
699
+ const endpoint = (_a = this.endpoints.find((ep) => ep.inputClusters.includes(0))) !== null && _a !== void 0 ? _a : this.endpoints[0];
700
+ await endpoint.read('genBasic', ['zclVersion'], { disableRecovery });
701
+ }
702
+ }
703
+ // This lookup contains all devices that are queried from the database, this is to ensure that always
704
+ // the same instance is returned.
705
+ Device.devices = new Map();
706
+ Device.ReportablePropertiesMapping = {
707
+ modelId: { key: 'modelID', set: (v, d) => { d.modelID = v; } },
708
+ manufacturerName: { key: 'manufacturerName', set: (v, d) => { d.manufacturerName = v; } },
709
+ powerSource: { key: 'powerSource', set: (v, d) => { d.powerSource = v; } },
710
+ zclVersion: { key: 'zclVersion', set: (v, d) => { d.zclVersion = v; } },
711
+ appVersion: { key: 'applicationVersion', set: (v, d) => { d.applicationVersion = v; } },
712
+ stackVersion: { key: 'stackVersion', set: (v, d) => { d.stackVersion = v; } },
713
+ hwVersion: { key: 'hardwareVersion', set: (v, d) => { d.hardwareVersion = v; } },
714
+ dateCode: { key: 'dateCode', set: (v, d) => { d.dateCode = v; } },
715
+ swBuildId: { key: 'softwareBuildID', set: (v, d) => { d.softwareBuildID = v; } },
716
+ };
717
+ exports.default = Device;
709
718
  //# sourceMappingURL=device.js.map