@smarthome.exchange/integrations 1.0.0 → 3.0.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 (529) hide show
  1. package/changelog.md +84 -0
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/core/classes.configstore.d.ts +2 -0
  4. package/dist_ts/core/classes.configstore.js +84 -27
  5. package/dist_ts/core/classes.nativehomeadmission.d.ts +19 -0
  6. package/dist_ts/core/classes.nativehomeadmission.js +84 -0
  7. package/dist_ts/core/classes.nativeintegrationowner.d.ts +81 -0
  8. package/dist_ts/core/classes.nativeintegrationowner.js +273 -0
  9. package/dist_ts/core/classes.runtimemanager.d.ts +2 -0
  10. package/dist_ts/core/classes.runtimemanager.js +11 -3
  11. package/dist_ts/core/index.d.ts +2 -0
  12. package/dist_ts/core/index.js +3 -1
  13. package/dist_ts/core/types.d.ts +16 -0
  14. package/dist_ts/integrations/braviatv/braviatv.classes.client.d.ts +23 -72
  15. package/dist_ts/integrations/braviatv/braviatv.classes.client.js +265 -625
  16. package/dist_ts/integrations/braviatv/braviatv.classes.configflow.d.ts +7 -7
  17. package/dist_ts/integrations/braviatv/braviatv.classes.configflow.js +51 -68
  18. package/dist_ts/integrations/braviatv/braviatv.classes.integration.d.ts +8 -11
  19. package/dist_ts/integrations/braviatv/braviatv.classes.integration.js +186 -135
  20. package/dist_ts/integrations/braviatv/braviatv.discovery.js +12 -11
  21. package/dist_ts/integrations/braviatv/braviatv.mapper.d.ts +6 -14
  22. package/dist_ts/integrations/braviatv/braviatv.mapper.js +37 -131
  23. package/dist_ts/integrations/braviatv/braviatv.types.d.ts +55 -133
  24. package/dist_ts/integrations/braviatv/braviatv.validation.d.ts +7 -0
  25. package/dist_ts/integrations/braviatv/braviatv.validation.js +72 -0
  26. package/dist_ts/integrations/broadlink/broadlink.actions.d.ts +2 -0
  27. package/dist_ts/integrations/broadlink/broadlink.actions.js +36 -0
  28. package/dist_ts/integrations/broadlink/broadlink.classes.client.d.ts +21 -19
  29. package/dist_ts/integrations/broadlink/broadlink.classes.client.js +185 -124
  30. package/dist_ts/integrations/broadlink/broadlink.classes.configflow.d.ts +5 -4
  31. package/dist_ts/integrations/broadlink/broadlink.classes.configflow.js +64 -83
  32. package/dist_ts/integrations/broadlink/broadlink.classes.integration.d.ts +16 -19
  33. package/dist_ts/integrations/broadlink/broadlink.classes.integration.js +114 -59
  34. package/dist_ts/integrations/broadlink/broadlink.mapper.d.ts +3 -2
  35. package/dist_ts/integrations/broadlink/broadlink.mapper.js +2 -1
  36. package/dist_ts/integrations/broadlink/broadlink.selected.mapper.d.ts +10 -0
  37. package/dist_ts/integrations/broadlink/broadlink.selected.mapper.js +41 -0
  38. package/dist_ts/integrations/broadlink/broadlink.types.d.ts +43 -7
  39. package/dist_ts/integrations/broadlink/broadlink.validation.d.ts +7 -0
  40. package/dist_ts/integrations/broadlink/broadlink.validation.js +63 -0
  41. package/dist_ts/integrations/daikin/daikin.classes.client.d.ts +10 -52
  42. package/dist_ts/integrations/daikin/daikin.classes.client.js +55 -614
  43. package/dist_ts/integrations/daikin/daikin.classes.configflow.d.ts +8 -12
  44. package/dist_ts/integrations/daikin/daikin.classes.configflow.js +77 -138
  45. package/dist_ts/integrations/daikin/daikin.classes.integration.d.ts +7 -24
  46. package/dist_ts/integrations/daikin/daikin.classes.integration.js +175 -67
  47. package/dist_ts/integrations/daikin/daikin.discovery.d.ts +11 -24
  48. package/dist_ts/integrations/daikin/daikin.discovery.js +98 -305
  49. package/dist_ts/integrations/daikin/daikin.mapper.d.ts +6 -7
  50. package/dist_ts/integrations/daikin/daikin.mapper.js +95 -155
  51. package/dist_ts/integrations/daikin/daikin.types.d.ts +24 -44
  52. package/dist_ts/integrations/daikin/daikin.types.js +1 -1
  53. package/dist_ts/integrations/daikin/daikin.validation.d.ts +5 -0
  54. package/dist_ts/integrations/daikin/daikin.validation.js +53 -0
  55. package/dist_ts/integrations/deconz/deconz.classes.client.d.ts +37 -29
  56. package/dist_ts/integrations/deconz/deconz.classes.client.js +521 -294
  57. package/dist_ts/integrations/deconz/deconz.classes.configflow.d.ts +6 -1
  58. package/dist_ts/integrations/deconz/deconz.classes.configflow.js +63 -33
  59. package/dist_ts/integrations/deconz/deconz.classes.integration.d.ts +4 -3
  60. package/dist_ts/integrations/deconz/deconz.classes.integration.js +85 -40
  61. package/dist_ts/integrations/deconz/deconz.mapper.d.ts +1 -0
  62. package/dist_ts/integrations/deconz/deconz.mapper.js +24 -6
  63. package/dist_ts/integrations/deconz/deconz.types.d.ts +43 -6
  64. package/dist_ts/integrations/deconz/deconz.validation.d.ts +7 -1
  65. package/dist_ts/integrations/deconz/deconz.validation.js +215 -4
  66. package/dist_ts/integrations/denonavr/denonavr.classes.client.d.ts +31 -40
  67. package/dist_ts/integrations/denonavr/denonavr.classes.client.js +149 -404
  68. package/dist_ts/integrations/denonavr/denonavr.classes.configflow.d.ts +7 -2
  69. package/dist_ts/integrations/denonavr/denonavr.classes.configflow.js +44 -36
  70. package/dist_ts/integrations/denonavr/denonavr.classes.integration.d.ts +6 -11
  71. package/dist_ts/integrations/denonavr/denonavr.classes.integration.js +192 -113
  72. package/dist_ts/integrations/denonavr/denonavr.discovery.d.ts +10 -18
  73. package/dist_ts/integrations/denonavr/denonavr.discovery.js +71 -193
  74. package/dist_ts/integrations/denonavr/denonavr.mapper.d.ts +5 -13
  75. package/dist_ts/integrations/denonavr/denonavr.mapper.js +35 -159
  76. package/dist_ts/integrations/denonavr/denonavr.types.d.ts +55 -90
  77. package/dist_ts/integrations/denonavr/denonavr.validation.d.ts +10 -0
  78. package/dist_ts/integrations/denonavr/denonavr.validation.js +86 -0
  79. package/dist_ts/integrations/esphome/esphome.classes.client.d.ts +19 -7
  80. package/dist_ts/integrations/esphome/esphome.classes.client.js +183 -89
  81. package/dist_ts/integrations/esphome/esphome.classes.configflow.d.ts +5 -2
  82. package/dist_ts/integrations/esphome/esphome.classes.configflow.js +43 -33
  83. package/dist_ts/integrations/esphome/esphome.classes.integration.d.ts +8 -6
  84. package/dist_ts/integrations/esphome/esphome.classes.integration.js +71 -43
  85. package/dist_ts/integrations/esphome/esphome.discovery.js +4 -12
  86. package/dist_ts/integrations/esphome/esphome.mapper.d.ts +2 -2
  87. package/dist_ts/integrations/esphome/esphome.mapper.js +6 -4
  88. package/dist_ts/integrations/esphome/esphome.types.d.ts +31 -6
  89. package/dist_ts/integrations/esphome/esphome.types.js +1 -1
  90. package/dist_ts/integrations/esphome/esphome.validation.d.ts +7 -0
  91. package/dist_ts/integrations/esphome/esphome.validation.js +66 -0
  92. package/dist_ts/integrations/homeassistant/homeassistant.classes.client.d.ts +32 -4
  93. package/dist_ts/integrations/homeassistant/homeassistant.classes.client.js +345 -116
  94. package/dist_ts/integrations/homeassistant/homeassistant.classes.integration.d.ts +8 -5
  95. package/dist_ts/integrations/homeassistant/homeassistant.classes.integration.js +34 -8
  96. package/dist_ts/integrations/homeassistant/homeassistant.classes.runtime.d.ts +7 -4
  97. package/dist_ts/integrations/homeassistant/homeassistant.classes.runtime.js +44 -56
  98. package/dist_ts/integrations/homeassistant/homeassistant.types.d.ts +12 -1
  99. package/dist_ts/integrations/hue/hue.classes.client.d.ts +6 -1
  100. package/dist_ts/integrations/hue/hue.classes.client.js +120 -45
  101. package/dist_ts/integrations/hue/hue.classes.configflow.js +16 -7
  102. package/dist_ts/integrations/hue/hue.classes.integration.d.ts +2 -0
  103. package/dist_ts/integrations/hue/hue.classes.integration.js +103 -30
  104. package/dist_ts/integrations/hue/hue.validation.d.ts +1 -0
  105. package/dist_ts/integrations/hue/hue.validation.js +14 -4
  106. package/dist_ts/integrations/kodi/kodi.classes.client.d.ts +11 -45
  107. package/dist_ts/integrations/kodi/kodi.classes.client.js +84 -310
  108. package/dist_ts/integrations/kodi/kodi.classes.configflow.d.ts +9 -8
  109. package/dist_ts/integrations/kodi/kodi.classes.configflow.js +53 -48
  110. package/dist_ts/integrations/kodi/kodi.classes.integration.d.ts +11 -11
  111. package/dist_ts/integrations/kodi/kodi.classes.integration.js +114 -171
  112. package/dist_ts/integrations/kodi/kodi.discovery.js +53 -29
  113. package/dist_ts/integrations/kodi/kodi.mapper.d.ts +7 -2
  114. package/dist_ts/integrations/kodi/kodi.mapper.js +48 -10
  115. package/dist_ts/integrations/kodi/kodi.types.d.ts +32 -28
  116. package/dist_ts/integrations/kodi/kodi.validation.d.ts +9 -0
  117. package/dist_ts/integrations/kodi/kodi.validation.js +62 -0
  118. package/dist_ts/integrations/modbus/modbus.classes.client.d.ts +15 -61
  119. package/dist_ts/integrations/modbus/modbus.classes.client.js +117 -776
  120. package/dist_ts/integrations/modbus/modbus.classes.configflow.d.ts +9 -5
  121. package/dist_ts/integrations/modbus/modbus.classes.configflow.js +70 -38
  122. package/dist_ts/integrations/modbus/modbus.classes.integration.d.ts +6 -17
  123. package/dist_ts/integrations/modbus/modbus.classes.integration.js +133 -57
  124. package/dist_ts/integrations/modbus/modbus.discovery.d.ts +5 -8
  125. package/dist_ts/integrations/modbus/modbus.discovery.js +38 -93
  126. package/dist_ts/integrations/modbus/modbus.mapper.d.ts +8 -32
  127. package/dist_ts/integrations/modbus/modbus.mapper.js +71 -431
  128. package/dist_ts/integrations/modbus/modbus.types.d.ts +82 -332
  129. package/dist_ts/integrations/modbus/modbus.validation.d.ts +10 -0
  130. package/dist_ts/integrations/modbus/modbus.validation.js +128 -0
  131. package/dist_ts/integrations/mqtt/mqtt.classes.client.d.ts +15 -2
  132. package/dist_ts/integrations/mqtt/mqtt.classes.client.js +173 -23
  133. package/dist_ts/integrations/mqtt/mqtt.classes.configflow.d.ts +5 -1
  134. package/dist_ts/integrations/mqtt/mqtt.classes.configflow.js +53 -33
  135. package/dist_ts/integrations/mqtt/mqtt.classes.integration.d.ts +4 -1
  136. package/dist_ts/integrations/mqtt/mqtt.classes.integration.js +127 -56
  137. package/dist_ts/integrations/mqtt/mqtt.classes.session.d.ts +13 -0
  138. package/dist_ts/integrations/mqtt/mqtt.classes.session.js +130 -0
  139. package/dist_ts/integrations/mqtt/mqtt.mapper.d.ts +3 -0
  140. package/dist_ts/integrations/mqtt/mqtt.mapper.js +44 -1
  141. package/dist_ts/integrations/mqtt/mqtt.types.d.ts +46 -0
  142. package/dist_ts/integrations/mqtt/mqtt.validation.d.ts +7 -0
  143. package/dist_ts/integrations/mqtt/mqtt.validation.js +180 -0
  144. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.client.d.ts +12 -34
  145. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.client.js +143 -231
  146. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.configflow.d.ts +6 -1
  147. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.configflow.js +105 -18
  148. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.integration.d.ts +8 -8
  149. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.integration.js +141 -155
  150. package/dist_ts/integrations/nanoleaf/nanoleaf.credentials.d.ts +9 -0
  151. package/dist_ts/integrations/nanoleaf/nanoleaf.credentials.js +35 -0
  152. package/dist_ts/integrations/nanoleaf/nanoleaf.mapper.d.ts +6 -18
  153. package/dist_ts/integrations/nanoleaf/nanoleaf.mapper.js +45 -220
  154. package/dist_ts/integrations/nanoleaf/nanoleaf.types.d.ts +33 -147
  155. package/dist_ts/integrations/nanoleaf/nanoleaf.validation.d.ts +6 -0
  156. package/dist_ts/integrations/nanoleaf/nanoleaf.validation.js +82 -0
  157. package/dist_ts/integrations/onkyo/onkyo.classes.client.d.ts +15 -20
  158. package/dist_ts/integrations/onkyo/onkyo.classes.client.js +161 -404
  159. package/dist_ts/integrations/onkyo/onkyo.classes.configflow.d.ts +9 -4
  160. package/dist_ts/integrations/onkyo/onkyo.classes.configflow.js +51 -6
  161. package/dist_ts/integrations/onkyo/onkyo.classes.integration.d.ts +22 -6
  162. package/dist_ts/integrations/onkyo/onkyo.classes.integration.js +144 -11
  163. package/dist_ts/integrations/onkyo/onkyo.discovery.d.ts +6 -1
  164. package/dist_ts/integrations/onkyo/onkyo.discovery.js +40 -4
  165. package/dist_ts/integrations/onkyo/onkyo.mapper.d.ts +7 -8
  166. package/dist_ts/integrations/onkyo/onkyo.mapper.js +45 -12
  167. package/dist_ts/integrations/onkyo/onkyo.types.d.ts +58 -8
  168. package/dist_ts/integrations/onkyo/onkyo.types.js +13 -102
  169. package/dist_ts/integrations/onkyo/onkyo.validation.d.ts +16 -0
  170. package/dist_ts/integrations/onkyo/onkyo.validation.js +77 -0
  171. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.client.d.ts +25 -25
  172. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.client.js +116 -360
  173. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.configflow.d.ts +11 -3
  174. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.configflow.js +76 -6
  175. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.integration.d.ts +26 -4
  176. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.integration.js +142 -9
  177. package/dist_ts/integrations/panasonic_viera/panasonic_viera.discovery.d.ts +2 -1
  178. package/dist_ts/integrations/panasonic_viera/panasonic_viera.discovery.js +31 -4
  179. package/dist_ts/integrations/panasonic_viera/panasonic_viera.mapper.d.ts +9 -9
  180. package/dist_ts/integrations/panasonic_viera/panasonic_viera.mapper.js +31 -149
  181. package/dist_ts/integrations/panasonic_viera/panasonic_viera.types.d.ts +51 -11
  182. package/dist_ts/integrations/panasonic_viera/panasonic_viera.types.js +22 -87
  183. package/dist_ts/integrations/panasonic_viera/panasonic_viera.validation.d.ts +6 -0
  184. package/dist_ts/integrations/panasonic_viera/panasonic_viera.validation.js +56 -0
  185. package/dist_ts/integrations/pioneer/pioneer.classes.client.d.ts +31 -16
  186. package/dist_ts/integrations/pioneer/pioneer.classes.client.js +167 -409
  187. package/dist_ts/integrations/pioneer/pioneer.classes.configflow.d.ts +10 -3
  188. package/dist_ts/integrations/pioneer/pioneer.classes.configflow.js +66 -6
  189. package/dist_ts/integrations/pioneer/pioneer.classes.integration.d.ts +13 -5
  190. package/dist_ts/integrations/pioneer/pioneer.classes.integration.js +239 -11
  191. package/dist_ts/integrations/pioneer/pioneer.discovery.d.ts +14 -1
  192. package/dist_ts/integrations/pioneer/pioneer.discovery.js +51 -4
  193. package/dist_ts/integrations/pioneer/pioneer.mapper.d.ts +14 -8
  194. package/dist_ts/integrations/pioneer/pioneer.mapper.js +54 -12
  195. package/dist_ts/integrations/pioneer/pioneer.types.d.ts +79 -12
  196. package/dist_ts/integrations/pioneer/pioneer.types.js +12 -76
  197. package/dist_ts/integrations/pioneer/pioneer.validation.d.ts +10 -0
  198. package/dist_ts/integrations/pioneer/pioneer.validation.js +85 -0
  199. package/dist_ts/integrations/samsungtv/samsungtv.classes.client.d.ts +4 -2
  200. package/dist_ts/integrations/samsungtv/samsungtv.classes.client.js +41 -16
  201. package/dist_ts/integrations/samsungtv/samsungtv.classes.configflow.d.ts +5 -0
  202. package/dist_ts/integrations/samsungtv/samsungtv.classes.configflow.js +20 -7
  203. package/dist_ts/integrations/samsungtv/samsungtv.classes.integration.d.ts +1 -0
  204. package/dist_ts/integrations/samsungtv/samsungtv.classes.integration.js +53 -50
  205. package/dist_ts/integrations/samsungtv/samsungtv.types.d.ts +4 -1
  206. package/dist_ts/integrations/shelly/shelly.classes.client.d.ts +26 -30
  207. package/dist_ts/integrations/shelly/shelly.classes.client.js +504 -276
  208. package/dist_ts/integrations/shelly/shelly.classes.configflow.d.ts +8 -3
  209. package/dist_ts/integrations/shelly/shelly.classes.configflow.js +43 -38
  210. package/dist_ts/integrations/shelly/shelly.classes.integration.d.ts +6 -6
  211. package/dist_ts/integrations/shelly/shelly.classes.integration.js +103 -101
  212. package/dist_ts/integrations/shelly/shelly.classes.subscription.d.ts +4 -0
  213. package/dist_ts/integrations/shelly/shelly.classes.subscription.js +24 -2
  214. package/dist_ts/integrations/shelly/shelly.mapper.d.ts +1 -2
  215. package/dist_ts/integrations/shelly/shelly.mapper.js +21 -20
  216. package/dist_ts/integrations/shelly/shelly.rpc.js +4 -1
  217. package/dist_ts/integrations/shelly/shelly.types.d.ts +42 -2
  218. package/dist_ts/integrations/shelly/shelly.types.js +11 -2
  219. package/dist_ts/integrations/shelly/shelly.validation.d.ts +7 -0
  220. package/dist_ts/integrations/shelly/shelly.validation.js +79 -0
  221. package/dist_ts/integrations/sonos/sonos.classes.client.d.ts +79 -22
  222. package/dist_ts/integrations/sonos/sonos.classes.client.js +427 -139
  223. package/dist_ts/integrations/sonos/sonos.classes.configflow.d.ts +9 -2
  224. package/dist_ts/integrations/sonos/sonos.classes.configflow.js +26 -28
  225. package/dist_ts/integrations/sonos/sonos.classes.integration.d.ts +7 -6
  226. package/dist_ts/integrations/sonos/sonos.classes.integration.js +289 -224
  227. package/dist_ts/integrations/sonos/sonos.config.d.ts +6 -1
  228. package/dist_ts/integrations/sonos/sonos.config.js +23 -2
  229. package/dist_ts/integrations/sonos/sonos.mapper.d.ts +1 -1
  230. package/dist_ts/integrations/sonos/sonos.mapper.js +14 -9
  231. package/dist_ts/integrations/sonos/sonos.matchers.js +3 -3
  232. package/dist_ts/integrations/sonos/sonos.types.d.ts +26 -1
  233. package/dist_ts/integrations/soundtouch/soundtouch.classes.client.d.ts +13 -36
  234. package/dist_ts/integrations/soundtouch/soundtouch.classes.client.js +179 -502
  235. package/dist_ts/integrations/soundtouch/soundtouch.classes.configflow.d.ts +6 -1
  236. package/dist_ts/integrations/soundtouch/soundtouch.classes.configflow.js +50 -40
  237. package/dist_ts/integrations/soundtouch/soundtouch.classes.integration.d.ts +14 -15
  238. package/dist_ts/integrations/soundtouch/soundtouch.classes.integration.js +108 -128
  239. package/dist_ts/integrations/soundtouch/soundtouch.discovery.d.ts +2 -20
  240. package/dist_ts/integrations/soundtouch/soundtouch.discovery.js +23 -113
  241. package/dist_ts/integrations/soundtouch/soundtouch.mapper.d.ts +3 -12
  242. package/dist_ts/integrations/soundtouch/soundtouch.mapper.js +35 -189
  243. package/dist_ts/integrations/soundtouch/soundtouch.types.d.ts +57 -142
  244. package/dist_ts/integrations/soundtouch/soundtouch.types.js +18 -3
  245. package/dist_ts/integrations/soundtouch/soundtouch.validation.d.ts +22 -0
  246. package/dist_ts/integrations/soundtouch/soundtouch.validation.js +65 -0
  247. package/dist_ts/integrations/tasmota/tasmota.classes.integration.d.ts +5 -0
  248. package/dist_ts/integrations/tasmota/tasmota.classes.integration.js +23 -7
  249. package/dist_ts/integrations/tasmota/tasmota.classes.runtime.d.ts +19 -5
  250. package/dist_ts/integrations/tasmota/tasmota.classes.runtime.js +115 -41
  251. package/dist_ts/integrations/tasmota/tasmota.config.js +23 -1
  252. package/dist_ts/integrations/tasmota/tasmota.credentials.d.ts +11 -0
  253. package/dist_ts/integrations/tasmota/tasmota.credentials.js +97 -0
  254. package/dist_ts/integrations/tasmota/tasmota.discovery.d.ts +7 -1
  255. package/dist_ts/integrations/tasmota/tasmota.discovery.js +108 -30
  256. package/dist_ts/integrations/tasmota/tasmota.mapper.d.ts +2 -2
  257. package/dist_ts/integrations/tasmota/tasmota.mapper.js +7 -7
  258. package/dist_ts/integrations/tasmota/tasmota.types.d.ts +41 -2
  259. package/dist_ts/integrations/tuya/tuya.classes.integration.d.ts +4 -1
  260. package/dist_ts/integrations/tuya/tuya.classes.integration.js +79 -110
  261. package/dist_ts/integrations/tuya/tuya.classes.runtime.d.ts +5 -2
  262. package/dist_ts/integrations/tuya/tuya.classes.runtime.js +73 -26
  263. package/dist_ts/integrations/tuya/tuya.mapper.js +2 -2
  264. package/dist_ts/integrations/tuya/tuya.types.d.ts +3 -0
  265. package/dist_ts/integrations/tuya/tuya.validation.d.ts +2 -2
  266. package/dist_ts/integrations/tuya/tuya.validation.js +6 -11
  267. package/dist_ts/integrations/webostv/webostv.classes.client.d.ts +6 -5
  268. package/dist_ts/integrations/webostv/webostv.classes.client.js +147 -76
  269. package/dist_ts/integrations/webostv/webostv.classes.configflow.d.ts +7 -2
  270. package/dist_ts/integrations/webostv/webostv.classes.configflow.js +36 -20
  271. package/dist_ts/integrations/webostv/webostv.classes.integration.d.ts +4 -4
  272. package/dist_ts/integrations/webostv/webostv.classes.integration.js +46 -54
  273. package/dist_ts/integrations/webostv/webostv.mapper.js +5 -5
  274. package/dist_ts/integrations/webostv/webostv.types.d.ts +16 -3
  275. package/dist_ts/integrations/webostv/webostv.types.js +1 -1
  276. package/dist_ts/integrations/webostv/webostv.validation.d.ts +3 -1
  277. package/dist_ts/integrations/webostv/webostv.validation.js +23 -1
  278. package/dist_ts/integrations/wiz/index.d.ts +1 -0
  279. package/dist_ts/integrations/wiz/index.js +2 -1
  280. package/dist_ts/integrations/wiz/wiz.classes.client.d.ts +14 -28
  281. package/dist_ts/integrations/wiz/wiz.classes.client.js +93 -229
  282. package/dist_ts/integrations/wiz/wiz.classes.configflow.d.ts +6 -4
  283. package/dist_ts/integrations/wiz/wiz.classes.configflow.js +38 -67
  284. package/dist_ts/integrations/wiz/wiz.classes.integration.d.ts +10 -21
  285. package/dist_ts/integrations/wiz/wiz.classes.integration.js +204 -44
  286. package/dist_ts/integrations/wiz/wiz.discovery.d.ts +10 -31
  287. package/dist_ts/integrations/wiz/wiz.discovery.js +48 -249
  288. package/dist_ts/integrations/wiz/wiz.mapper.d.ts +7 -51
  289. package/dist_ts/integrations/wiz/wiz.mapper.js +82 -668
  290. package/dist_ts/integrations/wiz/wiz.types.d.ts +34 -208
  291. package/dist_ts/integrations/wiz/wiz.validation.d.ts +8 -0
  292. package/dist_ts/integrations/wiz/wiz.validation.js +72 -0
  293. package/dist_ts/integrations/yamaha/index.d.ts +1 -0
  294. package/dist_ts/integrations/yamaha/index.js +2 -1
  295. package/dist_ts/integrations/yamaha/yamaha.classes.client.d.ts +30 -4
  296. package/dist_ts/integrations/yamaha/yamaha.classes.client.js +162 -6
  297. package/dist_ts/integrations/yamaha/yamaha.classes.configflow.d.ts +10 -3
  298. package/dist_ts/integrations/yamaha/yamaha.classes.configflow.js +106 -6
  299. package/dist_ts/integrations/yamaha/yamaha.classes.integration.d.ts +22 -4
  300. package/dist_ts/integrations/yamaha/yamaha.classes.integration.js +243 -8
  301. package/dist_ts/integrations/yamaha/yamaha.discovery.d.ts +13 -1
  302. package/dist_ts/integrations/yamaha/yamaha.discovery.js +57 -4
  303. package/dist_ts/integrations/yamaha/yamaha.mapper.d.ts +16 -8
  304. package/dist_ts/integrations/yamaha/yamaha.mapper.js +58 -12
  305. package/dist_ts/integrations/yamaha/yamaha.types.d.ts +73 -6
  306. package/dist_ts/integrations/yamaha/yamaha.types.js +8 -94
  307. package/dist_ts/integrations/yamaha/yamaha.validation.d.ts +12 -0
  308. package/dist_ts/integrations/yamaha/yamaha.validation.js +89 -0
  309. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.configflow.d.ts +6 -1
  310. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.configflow.js +18 -8
  311. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.integration.d.ts +4 -1
  312. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.integration.js +54 -6
  313. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.runtime.d.ts +12 -6
  314. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.runtime.js +73 -59
  315. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.mapper.d.ts +3 -2
  316. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.mapper.js +17 -12
  317. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.types.d.ts +31 -2
  318. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.validation.d.ts +9 -1
  319. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.validation.js +57 -7
  320. package/dist_ts/integrations/zwave_js/zwave_js.classes.client.d.ts +10 -4
  321. package/dist_ts/integrations/zwave_js/zwave_js.classes.client.js +167 -31
  322. package/dist_ts/integrations/zwave_js/zwave_js.classes.configflow.d.ts +6 -1
  323. package/dist_ts/integrations/zwave_js/zwave_js.classes.configflow.js +57 -35
  324. package/dist_ts/integrations/zwave_js/zwave_js.classes.integration.d.ts +3 -1
  325. package/dist_ts/integrations/zwave_js/zwave_js.classes.integration.js +59 -37
  326. package/dist_ts/integrations/zwave_js/zwave_js.discovery.js +3 -3
  327. package/dist_ts/integrations/zwave_js/zwave_js.mapper.js +41 -24
  328. package/dist_ts/integrations/zwave_js/zwave_js.types.d.ts +25 -3
  329. package/dist_ts/integrations/zwave_js/zwave_js.validation.d.ts +6 -2
  330. package/dist_ts/integrations/zwave_js/zwave_js.validation.js +52 -17
  331. package/dist_ts/plugins.d.ts +2 -1
  332. package/dist_ts/plugins.js +3 -2
  333. package/package.json +9 -9
  334. package/readme.md +539 -8
  335. package/ts/00_commitinfo_data.ts +1 -1
  336. package/ts/core/classes.configstore.ts +84 -27
  337. package/ts/core/classes.nativehomeadmission.ts +96 -0
  338. package/ts/core/classes.nativeintegrationowner.ts +302 -0
  339. package/ts/core/classes.runtimemanager.ts +11 -2
  340. package/ts/core/index.ts +2 -0
  341. package/ts/core/types.ts +18 -0
  342. package/ts/integrations/braviatv/braviatv.classes.client.ts +245 -709
  343. package/ts/integrations/braviatv/braviatv.classes.configflow.ts +47 -76
  344. package/ts/integrations/braviatv/braviatv.classes.integration.ts +167 -147
  345. package/ts/integrations/braviatv/braviatv.discovery.ts +11 -10
  346. package/ts/integrations/braviatv/braviatv.mapper.ts +39 -142
  347. package/ts/integrations/braviatv/braviatv.types.ts +53 -173
  348. package/ts/integrations/braviatv/braviatv.validation.ts +68 -0
  349. package/ts/integrations/broadlink/broadlink.actions.ts +31 -0
  350. package/ts/integrations/broadlink/broadlink.classes.client.ts +170 -144
  351. package/ts/integrations/broadlink/broadlink.classes.configflow.ts +59 -89
  352. package/ts/integrations/broadlink/broadlink.classes.integration.ts +110 -64
  353. package/ts/integrations/broadlink/broadlink.mapper.ts +5 -4
  354. package/ts/integrations/broadlink/broadlink.selected.mapper.ts +46 -0
  355. package/ts/integrations/broadlink/broadlink.types.ts +48 -8
  356. package/ts/integrations/broadlink/broadlink.validation.ts +66 -0
  357. package/ts/integrations/daikin/daikin.classes.client.ts +52 -661
  358. package/ts/integrations/daikin/daikin.classes.configflow.ts +72 -152
  359. package/ts/integrations/daikin/daikin.classes.integration.ts +166 -71
  360. package/ts/integrations/daikin/daikin.discovery.ts +94 -346
  361. package/ts/integrations/daikin/daikin.mapper.ts +83 -166
  362. package/ts/integrations/daikin/daikin.types.ts +27 -48
  363. package/ts/integrations/daikin/daikin.validation.ts +51 -0
  364. package/ts/integrations/daikin/readme.md +83 -0
  365. package/ts/integrations/deconz/deconz.classes.client.ts +348 -252
  366. package/ts/integrations/deconz/deconz.classes.configflow.ts +57 -29
  367. package/ts/integrations/deconz/deconz.classes.integration.ts +69 -37
  368. package/ts/integrations/deconz/deconz.mapper.ts +22 -5
  369. package/ts/integrations/deconz/deconz.types.ts +49 -6
  370. package/ts/integrations/deconz/deconz.validation.ts +177 -4
  371. package/ts/integrations/deconz/readme.md +15 -1
  372. package/ts/integrations/denonavr/denonavr.classes.client.ts +132 -421
  373. package/ts/integrations/denonavr/denonavr.classes.configflow.ts +44 -37
  374. package/ts/integrations/denonavr/denonavr.classes.integration.ts +171 -117
  375. package/ts/integrations/denonavr/denonavr.discovery.ts +67 -220
  376. package/ts/integrations/denonavr/denonavr.mapper.ts +36 -182
  377. package/ts/integrations/denonavr/denonavr.types.ts +48 -111
  378. package/ts/integrations/denonavr/denonavr.validation.ts +87 -0
  379. package/ts/integrations/esphome/esphome.classes.client.ts +179 -94
  380. package/ts/integrations/esphome/esphome.classes.configflow.ts +40 -33
  381. package/ts/integrations/esphome/esphome.classes.integration.ts +63 -36
  382. package/ts/integrations/esphome/esphome.discovery.ts +3 -12
  383. package/ts/integrations/esphome/esphome.mapper.ts +9 -7
  384. package/ts/integrations/esphome/esphome.types.ts +35 -6
  385. package/ts/integrations/esphome/esphome.validation.ts +66 -0
  386. package/ts/integrations/esphome/readme.md +20 -6
  387. package/ts/integrations/homeassistant/homeassistant.classes.client.ts +353 -125
  388. package/ts/integrations/homeassistant/homeassistant.classes.integration.ts +35 -18
  389. package/ts/integrations/homeassistant/homeassistant.classes.runtime.ts +50 -63
  390. package/ts/integrations/homeassistant/homeassistant.types.ts +17 -1
  391. package/ts/integrations/homeassistant/readme.md +22 -6
  392. package/ts/integrations/hue/hue.classes.client.ts +110 -41
  393. package/ts/integrations/hue/hue.classes.configflow.ts +12 -6
  394. package/ts/integrations/hue/hue.classes.integration.ts +92 -26
  395. package/ts/integrations/hue/hue.validation.ts +13 -3
  396. package/ts/integrations/hue/readme.md +3 -3
  397. package/ts/integrations/kodi/kodi.classes.client.ts +72 -352
  398. package/ts/integrations/kodi/kodi.classes.configflow.ts +52 -55
  399. package/ts/integrations/kodi/kodi.classes.integration.ts +97 -178
  400. package/ts/integrations/kodi/kodi.discovery.ts +49 -28
  401. package/ts/integrations/kodi/kodi.mapper.ts +52 -10
  402. package/ts/integrations/kodi/kodi.types.ts +33 -58
  403. package/ts/integrations/kodi/kodi.validation.ts +60 -0
  404. package/ts/integrations/modbus/modbus.classes.client.ts +100 -842
  405. package/ts/integrations/modbus/modbus.classes.configflow.ts +70 -41
  406. package/ts/integrations/modbus/modbus.classes.integration.ts +115 -60
  407. package/ts/integrations/modbus/modbus.discovery.ts +38 -106
  408. package/ts/integrations/modbus/modbus.mapper.ts +64 -467
  409. package/ts/integrations/modbus/modbus.types.ts +64 -381
  410. package/ts/integrations/modbus/modbus.validation.ts +119 -0
  411. package/ts/integrations/mqtt/mqtt.classes.client.ts +136 -19
  412. package/ts/integrations/mqtt/mqtt.classes.configflow.ts +45 -32
  413. package/ts/integrations/mqtt/mqtt.classes.integration.ts +99 -38
  414. package/ts/integrations/mqtt/mqtt.classes.session.ts +118 -0
  415. package/ts/integrations/mqtt/mqtt.mapper.ts +43 -1
  416. package/ts/integrations/mqtt/mqtt.types.ts +52 -0
  417. package/ts/integrations/mqtt/mqtt.validation.ts +157 -0
  418. package/ts/integrations/mqtt/readme.md +12 -3
  419. package/ts/integrations/nanoleaf/nanoleaf.classes.client.ts +115 -249
  420. package/ts/integrations/nanoleaf/nanoleaf.classes.configflow.ts +90 -19
  421. package/ts/integrations/nanoleaf/nanoleaf.classes.integration.ts +130 -181
  422. package/ts/integrations/nanoleaf/nanoleaf.credentials.ts +39 -0
  423. package/ts/integrations/nanoleaf/nanoleaf.mapper.ts +47 -260
  424. package/ts/integrations/nanoleaf/nanoleaf.types.ts +29 -147
  425. package/ts/integrations/nanoleaf/nanoleaf.validation.ts +77 -0
  426. package/ts/integrations/onkyo/onkyo.classes.client.ts +148 -457
  427. package/ts/integrations/onkyo/onkyo.classes.configflow.ts +52 -6
  428. package/ts/integrations/onkyo/onkyo.classes.integration.ts +121 -12
  429. package/ts/integrations/onkyo/onkyo.discovery.ts +41 -3
  430. package/ts/integrations/onkyo/onkyo.mapper.ts +46 -19
  431. package/ts/integrations/onkyo/onkyo.types.ts +55 -110
  432. package/ts/integrations/onkyo/onkyo.validation.ts +85 -0
  433. package/ts/integrations/onkyo/readme.md +126 -0
  434. package/ts/integrations/panasonic_viera/panasonic_viera.classes.client.ts +101 -398
  435. package/ts/integrations/panasonic_viera/panasonic_viera.classes.configflow.ts +69 -5
  436. package/ts/integrations/panasonic_viera/panasonic_viera.classes.integration.ts +141 -9
  437. package/ts/integrations/panasonic_viera/panasonic_viera.discovery.ts +32 -3
  438. package/ts/integrations/panasonic_viera/panasonic_viera.mapper.ts +34 -161
  439. package/ts/integrations/panasonic_viera/panasonic_viera.types.ts +52 -96
  440. package/ts/integrations/panasonic_viera/panasonic_viera.validation.ts +56 -0
  441. package/ts/integrations/pioneer/pioneer.classes.client.ts +132 -428
  442. package/ts/integrations/pioneer/pioneer.classes.configflow.ts +67 -5
  443. package/ts/integrations/pioneer/pioneer.classes.integration.ts +225 -11
  444. package/ts/integrations/pioneer/pioneer.discovery.ts +45 -3
  445. package/ts/integrations/pioneer/pioneer.mapper.ts +57 -13
  446. package/ts/integrations/pioneer/pioneer.types.ts +87 -85
  447. package/ts/integrations/pioneer/pioneer.validation.ts +84 -0
  448. package/ts/integrations/samsungtv/readme.md +11 -5
  449. package/ts/integrations/samsungtv/samsungtv.classes.client.ts +40 -15
  450. package/ts/integrations/samsungtv/samsungtv.classes.configflow.ts +21 -6
  451. package/ts/integrations/samsungtv/samsungtv.classes.integration.ts +53 -38
  452. package/ts/integrations/samsungtv/samsungtv.types.ts +4 -1
  453. package/ts/integrations/shelly/readme.md +22 -7
  454. package/ts/integrations/shelly/shelly.classes.client.ts +386 -269
  455. package/ts/integrations/shelly/shelly.classes.configflow.ts +43 -38
  456. package/ts/integrations/shelly/shelly.classes.integration.ts +92 -111
  457. package/ts/integrations/shelly/shelly.classes.subscription.ts +24 -1
  458. package/ts/integrations/shelly/shelly.mapper.ts +19 -20
  459. package/ts/integrations/shelly/shelly.rpc.ts +2 -0
  460. package/ts/integrations/shelly/shelly.types.ts +50 -2
  461. package/ts/integrations/shelly/shelly.validation.ts +76 -0
  462. package/ts/integrations/sonos/readme.md +117 -38
  463. package/ts/integrations/sonos/sonos.classes.client.ts +392 -123
  464. package/ts/integrations/sonos/sonos.classes.configflow.ts +25 -28
  465. package/ts/integrations/sonos/sonos.classes.integration.ts +211 -186
  466. package/ts/integrations/sonos/sonos.config.ts +24 -2
  467. package/ts/integrations/sonos/sonos.mapper.ts +13 -8
  468. package/ts/integrations/sonos/sonos.matchers.ts +2 -2
  469. package/ts/integrations/sonos/sonos.types.ts +30 -1
  470. package/ts/integrations/soundtouch/soundtouch.classes.client.ts +152 -571
  471. package/ts/integrations/soundtouch/soundtouch.classes.configflow.ts +47 -42
  472. package/ts/integrations/soundtouch/soundtouch.classes.integration.ts +95 -150
  473. package/ts/integrations/soundtouch/soundtouch.discovery.ts +21 -133
  474. package/ts/integrations/soundtouch/soundtouch.mapper.ts +36 -207
  475. package/ts/integrations/soundtouch/soundtouch.types.ts +50 -195
  476. package/ts/integrations/soundtouch/soundtouch.validation.ts +60 -0
  477. package/ts/integrations/tasmota/readme.md +16 -5
  478. package/ts/integrations/tasmota/tasmota.classes.integration.ts +24 -6
  479. package/ts/integrations/tasmota/tasmota.classes.runtime.ts +112 -43
  480. package/ts/integrations/tasmota/tasmota.config.ts +17 -0
  481. package/ts/integrations/tasmota/tasmota.credentials.ts +92 -0
  482. package/ts/integrations/tasmota/tasmota.discovery.ts +87 -24
  483. package/ts/integrations/tasmota/tasmota.mapper.ts +6 -6
  484. package/ts/integrations/tasmota/tasmota.types.ts +43 -2
  485. package/ts/integrations/tuya/readme.md +52 -10
  486. package/ts/integrations/tuya/tuya.classes.integration.ts +69 -98
  487. package/ts/integrations/tuya/tuya.classes.runtime.ts +70 -24
  488. package/ts/integrations/tuya/tuya.mapper.ts +1 -1
  489. package/ts/integrations/tuya/tuya.types.ts +4 -0
  490. package/ts/integrations/tuya/tuya.validation.ts +5 -8
  491. package/ts/integrations/webostv/readme.md +115 -22
  492. package/ts/integrations/webostv/webostv.classes.client.ts +123 -50
  493. package/ts/integrations/webostv/webostv.classes.configflow.ts +35 -15
  494. package/ts/integrations/webostv/webostv.classes.integration.ts +44 -37
  495. package/ts/integrations/webostv/webostv.mapper.ts +4 -4
  496. package/ts/integrations/webostv/webostv.types.ts +18 -3
  497. package/ts/integrations/webostv/webostv.validation.ts +22 -1
  498. package/ts/integrations/wiz/index.ts +1 -0
  499. package/ts/integrations/wiz/wiz.classes.client.ts +84 -254
  500. package/ts/integrations/wiz/wiz.classes.configflow.ts +39 -70
  501. package/ts/integrations/wiz/wiz.classes.integration.ts +189 -45
  502. package/ts/integrations/wiz/wiz.discovery.ts +48 -291
  503. package/ts/integrations/wiz/wiz.mapper.ts +81 -754
  504. package/ts/integrations/wiz/wiz.types.ts +24 -224
  505. package/ts/integrations/wiz/wiz.validation.ts +73 -0
  506. package/ts/integrations/yamaha/index.ts +1 -0
  507. package/ts/integrations/yamaha/yamaha.classes.client.ts +150 -6
  508. package/ts/integrations/yamaha/yamaha.classes.configflow.ts +100 -5
  509. package/ts/integrations/yamaha/yamaha.classes.integration.ts +236 -8
  510. package/ts/integrations/yamaha/yamaha.discovery.ts +52 -3
  511. package/ts/integrations/yamaha/yamaha.mapper.ts +65 -13
  512. package/ts/integrations/yamaha/yamaha.types.ts +77 -97
  513. package/ts/integrations/yamaha/yamaha.validation.ts +86 -0
  514. package/ts/integrations/zigbee2mqtt/readme.md +22 -27
  515. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.configflow.ts +16 -7
  516. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.integration.ts +53 -6
  517. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.runtime.ts +62 -49
  518. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.mapper.ts +17 -11
  519. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.types.ts +35 -2
  520. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.validation.ts +53 -7
  521. package/ts/integrations/zwave_js/readme.md +42 -9
  522. package/ts/integrations/zwave_js/zwave_js.classes.client.ts +155 -31
  523. package/ts/integrations/zwave_js/zwave_js.classes.configflow.ts +55 -31
  524. package/ts/integrations/zwave_js/zwave_js.classes.integration.ts +57 -35
  525. package/ts/integrations/zwave_js/zwave_js.discovery.ts +2 -2
  526. package/ts/integrations/zwave_js/zwave_js.mapper.ts +37 -23
  527. package/ts/integrations/zwave_js/zwave_js.types.ts +26 -3
  528. package/ts/integrations/zwave_js/zwave_js.validation.ts +52 -18
  529. package/ts/plugins.ts +2 -1
@@ -1,13 +1,17 @@
1
1
  import type { TEntityPlatform } from '../../core/types.js';
2
2
  import type * as plugins from '../../plugins.js';
3
+ import type { INativeIntegrationOwnerOptions } from '../../core/classes.nativeintegrationowner.js';
4
+ import type { IMqttCredentialRequest, IMqttTransportCredentials } from '../mqtt/mqtt.types.js';
3
5
 
4
6
  export interface IZigbee2MqttConfig {
7
+ homeId: string;
8
+ authentication: 'anonymous' | 'provider' | 'config-store';
9
+ credentialKey?: string;
10
+ allowUnencryptedLan?: boolean;
5
11
  host: string;
6
12
  baseTopic: string;
7
13
  port?: number;
8
14
  protocol?: 'mqtt' | 'mqtts';
9
- username?: string;
10
- password?: string;
11
15
  clientId?: string;
12
16
  connectTimeoutMs?: number;
13
17
  operationTimeoutMs?: number;
@@ -18,6 +22,33 @@ export interface IZigbee2MqttConfig {
18
22
  maxBufferedBytes?: number;
19
23
  }
20
24
 
25
+ /** Runtime-only owner permission; it is never learned from MQTT or editable config. */
26
+ export interface IZigbee2MqttControlPermission {
27
+ readonly ieeeAddress: string;
28
+ readonly property: string;
29
+ }
30
+
31
+ export interface IZigbee2MqttCredentialRequest extends IMqttCredentialRequest {
32
+ readonly host: string;
33
+ readonly port: number;
34
+ readonly protocol: 'mqtt' | 'mqtts';
35
+ readonly baseTopic: string;
36
+ }
37
+
38
+ export interface IZigbee2MqttStoredCredential {
39
+ version: 1;
40
+ host: string;
41
+ port: number;
42
+ protocol: 'mqtt' | 'mqtts';
43
+ baseTopic: string;
44
+ credentials: IMqttTransportCredentials;
45
+ }
46
+
47
+ export interface IZigbee2MqttIntegrationOptions extends INativeIntegrationOwnerOptions {
48
+ readonly controls?: readonly IZigbee2MqttControlPermission[];
49
+ readonly resolveCredentials?: (requestArg: IZigbee2MqttCredentialRequest) => Promise<IMqttTransportCredentials>;
50
+ }
51
+
21
52
  export type TZigbee2MqttPrimitive = string | number | boolean;
22
53
 
23
54
  export interface IZigbee2MqttExpose {
@@ -54,6 +85,8 @@ export interface IZigbee2MqttReportedState {
54
85
  }
55
86
 
56
87
  export interface IZigbee2MqttSnapshot {
88
+ homeId: string;
89
+ controls: ReadonlySet<string>;
57
90
  devices: IZigbee2MqttDevice[];
58
91
  connected: boolean;
59
92
  bridgeOnline: boolean;
@@ -1,5 +1,7 @@
1
1
  import { validateMqttTopic } from '../mqtt/mqtt.classes.client.js';
2
- import type { IZigbee2MqttConfig, TZigbee2MqttPrimitive } from './zigbee2mqtt.types.js';
2
+ import { mqttCredentials } from '../mqtt/mqtt.validation.js';
3
+ import type { IMqttTransportCredentials } from '../mqtt/mqtt.types.js';
4
+ import type { IZigbee2MqttConfig, IZigbee2MqttIntegrationOptions, TZigbee2MqttPrimitive } from './zigbee2mqtt.types.js';
3
5
 
4
6
  export const zigbee2MqttRecord = (valueArg: unknown): Record<string, unknown> | undefined => {
5
7
  if (!valueArg || typeof valueArg !== 'object' || Array.isArray(valueArg)) return undefined;
@@ -25,9 +27,14 @@ export const zigbee2MqttInteger = (valueArg: unknown, defaultArg: number, maximu
25
27
 
26
28
  export const zigbee2MqttConfig = (configArg: IZigbee2MqttConfig): IZigbee2MqttConfig => {
27
29
  const config = zigbee2MqttRecord(configArg);
28
- const allowed = new Set(['host', 'baseTopic', 'port', 'protocol', 'username', 'password', 'clientId',
30
+ const allowed = new Set(['homeId', 'authentication', 'credentialKey', 'allowUnencryptedLan', 'host', 'baseTopic', 'port', 'protocol', 'clientId',
29
31
  'connectTimeoutMs', 'operationTimeoutMs', 'inventoryTimeoutMs', 'reconnectPeriodMs', 'maxDevices', 'maxPayloadBytes', 'maxBufferedBytes']);
30
32
  if (!config || Object.keys(config).some((keyArg) => !allowed.has(keyArg))) throw new Error('Unsupported Zigbee2MQTT configuration field.');
33
+ const homeId = zigbee2MqttIdentifier(config.homeId);
34
+ if (typeof config.authentication !== 'string' || !['anonymous', 'provider', 'config-store'].includes(config.authentication)) throw new Error('Explicit Zigbee2MQTT authentication mode is required.');
35
+ const authentication = config.authentication as IZigbee2MqttConfig['authentication'];
36
+ const credentialKey = config.credentialKey === undefined ? undefined : zigbee2MqttIdentifier(config.credentialKey);
37
+ if ((authentication === 'anonymous') !== (credentialKey === undefined)) throw new Error('Zigbee2MQTT credential reference must agree with the explicit authentication mode.');
31
38
  if (typeof config.host !== 'string' || !config.host || config.host.length > 253 || /[\s/@?#\u0000]/.test(config.host)) {
32
39
  throw new Error('An explicit Zigbee2MQTT broker hostname or IP address is required.');
33
40
  }
@@ -37,7 +44,9 @@ export const zigbee2MqttConfig = (configArg: IZigbee2MqttConfig): IZigbee2MqttCo
37
44
  }
38
45
  validateMqttTopic(config.baseTopic);
39
46
  if (config.protocol !== undefined && config.protocol !== 'mqtt' && config.protocol !== 'mqtts') throw new Error('Unsupported Zigbee2MQTT broker protocol.');
40
- for (const key of ['username', 'password', 'clientId']) {
47
+ if (config.allowUnencryptedLan !== undefined && typeof config.allowUnencryptedLan !== 'boolean') throw new Error('Zigbee2MQTT plaintext LAN consent must be explicit.');
48
+ if (config.protocol !== 'mqtts' && config.allowUnencryptedLan !== true) throw new Error('Unencrypted Zigbee2MQTT MQTT requires explicit LAN consent.');
49
+ for (const key of ['clientId']) {
41
50
  if (config[key] !== undefined && (typeof config[key] !== 'string' || String(config[key]).length > 1024 || String(config[key]).includes('\u0000'))) {
42
51
  throw new Error('Invalid Zigbee2MQTT broker credential or client identifier.');
43
52
  }
@@ -46,13 +55,12 @@ export const zigbee2MqttConfig = (configArg: IZigbee2MqttConfig): IZigbee2MqttCo
46
55
  if (typeof reconnect !== 'number' || !Number.isSafeInteger(reconnect) || reconnect < 0 || reconnect > 60000) {
47
56
  throw new Error('Invalid Zigbee2MQTT reconnect interval.');
48
57
  }
49
- return {
58
+ return Object.freeze({
59
+ homeId, authentication, credentialKey, allowUnencryptedLan: config.allowUnencryptedLan as boolean | undefined,
50
60
  host: config.host,
51
61
  baseTopic: config.baseTopic,
52
62
  protocol: config.protocol ?? 'mqtt',
53
63
  port: zigbee2MqttInteger(config.port, config.protocol === 'mqtts' ? 8883 : 1883, 65535),
54
- username: config.username as string | undefined,
55
- password: config.password as string | undefined,
56
64
  clientId: config.clientId as string | undefined,
57
65
  connectTimeoutMs: zigbee2MqttInteger(config.connectTimeoutMs, 7000, 60000),
58
66
  operationTimeoutMs: zigbee2MqttInteger(config.operationTimeoutMs, 7000, 60000),
@@ -61,5 +69,43 @@ export const zigbee2MqttConfig = (configArg: IZigbee2MqttConfig): IZigbee2MqttCo
61
69
  maxDevices: zigbee2MqttInteger(config.maxDevices, 2048, 10000),
62
70
  maxPayloadBytes: zigbee2MqttInteger(config.maxPayloadBytes, 8388608, 16777216),
63
71
  maxBufferedBytes: zigbee2MqttInteger(config.maxBufferedBytes, 33554432, 67108864),
64
- };
72
+ });
73
+ };
74
+
75
+ export const zigbee2MqttIdentifier = (valueArg: unknown): string => {
76
+ if (typeof valueArg !== 'string' || !valueArg || valueArg.length > 256 || valueArg.trim() !== valueArg || /[\p{Cc}\p{Cs}]/u.test(valueArg)) {
77
+ throw new TypeError('Zigbee2MQTT identifier must be a bounded non-empty data string.');
78
+ }
79
+ return valueArg;
80
+ };
81
+
82
+ export const zigbee2MqttControlKey = (ieeeAddressArg: string, propertyArg: string): string => JSON.stringify([ieeeAddressArg, propertyArg]);
83
+
84
+ export const zigbee2MqttCredentials = (configArg: IZigbee2MqttConfig, valueArg: unknown): IMqttTransportCredentials => {
85
+ const credentials = mqttCredentials(valueArg);
86
+ if (configArg.protocol !== 'mqtts' && ['ca', 'cert', 'key', 'servername'].some((key) => Object.hasOwn(credentials, key))) {
87
+ throw new TypeError('Zigbee2MQTT TLS credentials require an explicitly selected mqtts transport.');
88
+ }
89
+ return credentials;
90
+ };
91
+
92
+ export const zigbee2MqttOptions = (optionsArg: IZigbee2MqttIntegrationOptions): { options: IZigbee2MqttIntegrationOptions; controls: ReadonlySet<string> } => {
93
+ const fields = zigbee2MqttRecord(optionsArg);
94
+ if (!fields || Object.keys(fields).some((key) => !['homeId', 'capacity', 'controls', 'resolveCredentials'].includes(key))) throw new TypeError('Unsupported Zigbee2MQTT owner options.');
95
+ if (fields.resolveCredentials !== undefined && typeof fields.resolveCredentials !== 'function') throw new TypeError('Zigbee2MQTT resolver must be a function.');
96
+ const controls = new Set<string>();
97
+ if (fields.controls !== undefined) {
98
+ if (!Array.isArray(fields.controls) || fields.controls.length > 10000 || Reflect.ownKeys(fields.controls).length !== fields.controls.length + 1) throw new TypeError('Zigbee2MQTT owner controls must be a finite data array.');
99
+ for (let index = 0; index < fields.controls.length; index++) {
100
+ const descriptor = Object.getOwnPropertyDescriptor(fields.controls, String(index));
101
+ const permission = descriptor && 'value' in descriptor && descriptor.enumerable ? zigbee2MqttRecord(descriptor.value) : undefined;
102
+ if (!permission || Object.keys(permission).length !== 2 || typeof permission.ieeeAddress !== 'string' || !/^0x[0-9a-f]{16}$/.test(permission.ieeeAddress) ||
103
+ typeof permission.property !== 'string' || !/^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/.test(permission.property)) throw new TypeError('Zigbee2MQTT controls require exact lowercase IEEE/property pairs.');
104
+ const key = zigbee2MqttControlKey(permission.ieeeAddress, permission.property);
105
+ if (controls.has(key)) throw new TypeError('Duplicate Zigbee2MQTT owner control permission.');
106
+ controls.add(key);
107
+ }
108
+ }
109
+ return { options: Object.freeze({ homeId: fields.homeId as string | undefined, capacity: fields.capacity as number | undefined,
110
+ resolveCredentials: fields.resolveCredentials as IZigbee2MqttIntegrationOptions['resolveCredentials'] }), controls };
65
111
  };
@@ -1,23 +1,56 @@
1
1
  # Native Z-Wave JS
2
2
 
3
- This adapter connects directly to the [Z-Wave JS server WebSocket API](https://github.com/zwave-js/zwave-js-server#readme). Home Assistant is not part of the runtime path. It negotiates schema 32, verifies the configured Z-Wave network home ID, and awaits the server's authoritative initial state before setup succeeds.
3
+ This adapter connects to one explicitly selected [Z-Wave JS server WebSocket API](https://github.com/zwave-js/zwave-js-server#readme). It negotiates schema32, available from server1.32.0, verifies the numeric Z-Wave network ID in both greeting and authoritative initial state, and waits for that state before setup succeeds. Home Assistant and serial-controller access are outside this runtime.
4
4
 
5
5
  ```typescript
6
- const runtime = await new ZwaveJsIntegration().setup({
6
+ const homeId = 'my-home'; // Independently selected by the embedding host.
7
+ const integration = new ZwaveJsIntegration();
8
+ const runtime = await integration.setup({
9
+ homeId,
7
10
  url: 'ws://zwave-js.local:3000',
8
11
  expectedHomeId: 123456,
12
+ allowControls: true, // Optional; the default is read-only.
9
13
  requestTimeoutMs: 10000,
10
- }, { abortSignal });
14
+ }, { homeId });
15
+ try {
16
+ const devices = await runtime.devices();
17
+ } finally {
18
+ await integration.destroy();
19
+ }
11
20
  ```
12
21
 
13
- The network home ID is the decimal form of the ID displayed by Z-Wave JS. Device and entity identities include it, the node ID and the exact value ID. Renaming a node does not change its identity. The new network-scoped IDs replace the earlier node-only IDs. Configuration requires an explicit endpoint and expected home ID; injected nodes, state, version and schema overrides are removed.
22
+ `context.homeId` is required separately for setup, direct `ZwaveJsClient(config, context, options?)` and config-flow start. Constructor `{ homeId?, capacity? }` adds an optional consistency constraint; it never substitutes for context. Config, form, discovery and store scope cannot select host home. The first admitted host remains bound even after failed setup. Integration and flow share admission and terminal ownership. The flow captures context before returning its form, has no editable SHX home field, rejects conflicting submitted home, and returns the captured home. This is a trusted embedding-host contract, not a home string used as authentication.
14
23
 
15
- The native server does not implement authentication. Use `ws` only on an explicitly trusted network. For an operator-managed TLS proxy that enforces HTTP authorization, configure `wss` and an `authorization` header. Certificate and hostname verification stay enabled; optional `ca` adds a PEM trust anchor. Credentials in URLs, query strings, plaintext authorization and redirects are rejected. Endpoint discovery supplies a setup hint and never proves authorization or network ownership. The config flow verifies a live connection before completing. Store its resulting configuration through the host's encrypted config store.
24
+ `config.homeId` is the SHX consistency value. `expectedHomeId` is the separate decimal32-bit Z-Wave network ID shown by the server. Device/entity IDs include encoded SHX home, observed network ID, node and exact value tuple. These IDs replace the earlier network-only IDs; saved canonical targets must be refreshed. Node renaming does not change identity. Endpoint, property and optional propertyKey retain their actual number/string distinctions. No persistent authenticated server UUID is exposed by this protocol. Network ID and software versions alone do not prove server ownership or physical controller identity. The server connection record explicitly represents an adapter observation.
16
25
 
17
- The read surface includes server-reported node state and non-secret values. Driver/controller management, inclusion, arbitrary command-class APIs, locks, thermostat actions and configuration writes are excluded. Supported controls are advertised writable `targetValue` entries for Binary Switch (CC 37, boolean) and Multilevel Switch (CC 38, integer 0–99 constrained further by server metadata). Use `writeFeature` with the exact current device and feature, or `switch`/`light` `turn_on`, `turn_off`, `set_value` with an exact entity/device target. Device targets with multiple supported values require `data.featureId`; ambiguous, foreign and missing targets fail. Caller-supplied feature capabilities do not grant access; the verified server snapshot owns eligibility.
26
+ The native server supplies no authentication. Use `ws` on an explicitly trusted selected network. An operator-managed verified TLS proxy can enforce HTTP authorization. URLs cannot contain credentials, query strings or fragments, and redirects are disabled. Certificate and hostname verification remain enabled; optional `ca` adds a PEM trust anchor. Discovery is an endpoint/setup hint and never grants authorization.
18
27
 
19
- The [driver's SetValueResult contract](https://github.com/zwave-js/node-zwave-js/blob/master/packages/cc/src/lib/SetValueResult.ts) distinguishes supervised completion from acceptance. Only status 255 returns `success: true` with `outcome: 'confirmed'`. Working (1) and unsupervised (254) replies return `success: false` with their indeterminate outcome; device rejection retains its status. Command responses never invent device state. Reported changes come from `value updated` events. Metadata and membership changes trigger bounded, coalesced reconciliation from the server.
28
+ Proxy configuration contains only `credentialRef` and explicit `credentialSource`. The two sources have no fallback:
20
29
 
21
- One client owns one socket, at most 64 pending commands, a 2 MiB frame limit, bounded JSON depth/work, and 50–60000 ms deadlines (default 10000 ms). Caller cancellation before dispatch sends nothing; cancellation after dispatch cannot undo a physical command and rejects as indeterminate. Disconnect, protocol failure and deadline expiry stop the client. Reads retain the detached last snapshot with all devices unavailable and never restart the transport. There are no automatic reconnects or uncertain write retries; explicitly replace the runtime after inspecting the operation's outcome. `destroy()` is idempotent, rejects pending work, drains startup/reconciliation, removes subscriptions and waits for the owned socket to close. Setup's abort signal owns the runtime lifetime.
30
+ - `provider`: constructor options supply caller-owned asynchronous `resolveAuthorization({ homeId, credentialRef, signal, deadline })`. The request is immutable; `deadline` uses monotonic `performance.now()` milliseconds and covers resolution plus setup. The host stores/retrieves the header outside the integration.
31
+ - `config-store`: explicit `context.configStore` is an actual encrypted `NoSqlConfigStore` scoped to the same SHX home and `zwave_js`; its public `get<string>(credentialRef)` returns the externally stored header.
22
32
 
23
- Local protocol fixtures cover startup, TLS proxy admission and rejected credentials, certificate verification, network identity, authoritative events, membership reconciliation, controls, execution outcomes, malformed frames, cancellation and cleanup. Physical acceptance against a real controller and representative supervised/unsupervised nodes remains required; the adapter has not been exercised against the owner's hardware.
33
+ Both require `wss`. The adapter never returns, logs or persists the resolved header. The reference is not a credential and never selects a home. No source is required without proxy authorization. An optional genuine store is checked for exact home/domain even on store-free paths, remains borrowed, and is never closed along with its database/key provider. There is no filesystem persistence, raw-driver access, dummy store or implicit source replacement.
34
+
35
+ ```typescript
36
+ const integration = new ZwaveJsIntegration({
37
+ resolveAuthorization: async ({ homeId, credentialRef, signal, deadline }) =>
38
+ hostCredentials.resolve({ homeId, credentialRef, signal, deadline }),
39
+ });
40
+ const runtime = await integration.setup({
41
+ homeId: 'my-home', url: 'wss://zwave-js.example/', expectedHomeId: 123456,
42
+ credentialRef: 'zwave-proxy', credentialSource: 'provider',
43
+ }, { homeId: 'my-home' });
44
+ ```
45
+
46
+ Read observations are server-reported nodes, readiness/status and non-secret values; no default nodes/capabilities are inferred. Controls require explicit `allowControls: true`, a healthy runtime, an unambiguous canonical target, and a fresh authoritative membership/metadata snapshot before each one-shot dispatch. Supported writes remain advertised writable `targetValue` entries for Binary Switch(CC37, boolean) and Multilevel Switch(CC38, integer0..99 further restricted by actual metadata). Offline/stale/read-only features and entities advertise no writability. Supplied feature flags cannot grant access.
47
+
48
+ Use `writeFeature` with exact current device/feature, or `switch`/`light`/`zwave_js` `turn_on`, `turn_off`, `set_value` with exact entity/device target. Device targets with multiple supported values require `data.featureId`. Missing, ambiguous, foreign, unadvertised, secret, unavailable and incompatible targets fail. Only the documented absolute value is sent; no toggles, relative commands, inclusion, management, configuration, locks, thermostat, raw command-class APIs or controller operations exist.
49
+
50
+ Each request has a unique messageId. Correlated [SetValueResult](https://github.com/zwave-js/node-zwave-js/blob/master/packages/cc/src/lib/SetValueResult.ts) status255 retains legacy `outcome: 'confirmed'` and reports supervised device success. Status1 is working,254 is unsupervised/unknown execution, and other supported statuses are rejection/unsupported/invalid. Every result includes `causalityConfirmed: false`: a device-reported result is not independent physical acceptance or causality proof. Commands never invent reported state. Value-update events and authoritative reconciliation supply separate state observations.
51
+
52
+ Shared native ownership defaults to16 pending admissions, live runtimes and retained failed cleanup handles combined. The client bounds pending requests, admitted controls/subscriber work and subscribers to64 each, with a2MiB frame limit,128 fragments/chunks and bounded JSON depth/work. Request/setup deadline is50..60000ms(default10000). Cancellation before dispatch sends no effect; cancellation after dispatch cannot undo a command and remains indeterminate. Protocol failure, disconnect and deadline expiry are terminal; retained detached observations become unavailable and never reconnect or replay.
53
+
54
+ `destroy()` installs its stable memo before abort or callbacks, stops admission, joins actual provider/setup/reconciliation/control/subscriber work and socket closure with allSettled, and then reports cleanup errors. A late provider result after cancellation cannot allocate a socket. An unrelated late provider/subscriber rejection is sanitized and retained; cooperative rejection with the admitted signal reason remains cancellation. Providers own retrieval resources and must eventually settle their admitted promises; close intentionally waits for that actual work after request timeout. `retryCleanup()` explicitly retries cleanup/acknowledges settled failures only. It never invokes the resolver, reconnects or repeats a command, and the original destroy promise remains stable. Closing a flow stops its submissions and leaves sibling runtimes usable; integration close joins all scopes.
55
+
56
+ Local WebSocket/TLS fixtures cover schema/network admission, events and reconciliation, exact controls/correlation, read-only defaults, home/store/provider boundaries, deadlines, late provider work, capacity, callback failures and terminal cleanup. Physical acceptance on the owner's controller and representative nodes remains unverified; no real controller was accessed.
@@ -1,9 +1,17 @@
1
1
  import * as plugins from '../../plugins.js';
2
- import type { IZwaveJsConfig, IZwaveJsServerCommand, IZwaveJsServerEvent, IZwaveJsSnapshot, IZwaveJsState, IZwaveJsVersion, IZwaveJsWriteResult } from './zwave_js.types.js';
2
+ import { NativeHomeAdmission, type INativeAdmissionContext } from '../../core/classes.nativehomeadmission.js';
3
+ import type { IIntegrationSetupContext } from '../../core/types.js';
4
+ import type { IZwaveJsConfig, IZwaveJsIntegrationOptions, IZwaveJsServerCommand, IZwaveJsServerEvent, IZwaveJsSnapshot, IZwaveJsState, IZwaveJsVersion, IZwaveJsWriteResult } from './zwave_js.types.js';
3
5
  import { ZwaveJsMapper } from './zwave_js.mapper.js';
4
- import { parseZwaveJsState, parseZwaveJsValueId, parseZwaveJsVersion, validateZwaveJsConfig, validateZwaveJsJson, validateZwaveJsValueData, zwaveJsRecord, zwaveJsSchemaVersion, zwaveJsValueKey } from './zwave_js.validation.js';
6
+ import { normalizeZwaveJsOptions, parseZwaveJsState, parseZwaveJsValueId, parseZwaveJsVersion, validateZwaveJsConfig, validateZwaveJsJson, validateZwaveJsValueData, zwaveJsRecord, zwaveJsSchemaVersion, zwaveJsValueKey } from './zwave_js.validation.js';
5
7
 
6
- type TEventHandler = (eventArg: IZwaveJsServerEvent) => void;
8
+ type TEventHandler = (eventArg: IZwaveJsServerEvent) => void | Promise<void>;
9
+ interface ISubscription {
10
+ handler: TEventHandler;
11
+ active: boolean;
12
+ pending: Set<Promise<void>>;
13
+ failures: Set<Error>;
14
+ }
7
15
 
8
16
  interface IPendingRequest {
9
17
  resolve(valueArg: unknown): void;
@@ -14,10 +22,16 @@ interface IPendingRequest {
14
22
 
15
23
  export class ZwaveJsClient {
16
24
  #config: IZwaveJsConfig;
25
+ readonly #context: INativeAdmissionContext;
26
+ readonly #options: Readonly<IZwaveJsIntegrationOptions>;
27
+ readonly #lifetime = new AbortController();
28
+ readonly #operations = new Set<Promise<unknown>>();
29
+ readonly #cleanupFailures = new Set<Error>();
17
30
  #socket?: plugins.ws.WebSocket;
18
31
  #socketClosed?: Promise<void>;
19
32
  #startPromise?: Promise<void>;
20
33
  #closePromise?: Promise<void>;
34
+ #retryPromise?: Promise<void>;
21
35
  #version?: IZwaveJsVersion;
22
36
  #state?: IZwaveJsState;
23
37
  #ready = false;
@@ -25,7 +39,7 @@ export class ZwaveJsClient {
25
39
  #failure?: Error;
26
40
  #greeting?: { resolve(): void; reject(errorArg: Error): void };
27
41
  #pending = new Map<string, IPendingRequest>();
28
- #handlers = new Set<TEventHandler>();
42
+ #handlers = new Set<ISubscription>();
29
43
  #events: IZwaveJsServerEvent[] = [];
30
44
  #refreshNeeded = false;
31
45
  #snapshotStale = false;
@@ -33,9 +47,18 @@ export class ZwaveJsClient {
33
47
  #lifetimeSignal?: AbortSignal;
34
48
  #lifetimeAbort?: () => void;
35
49
 
36
- constructor(configArg: IZwaveJsConfig, lifetimeSignalArg?: AbortSignal) {
50
+ constructor(configArg: IZwaveJsConfig, contextArg: IIntegrationSetupContext, optionsArg: IZwaveJsIntegrationOptions = {}) {
37
51
  this.#config = validateZwaveJsConfig(configArg);
38
- this.#lifetimeSignal = lifetimeSignalArg;
52
+ this.#options = normalizeZwaveJsOptions(optionsArg);
53
+ this.#context = new NativeHomeAdmission('zwave_js', { homeId: this.#options.homeId }).admit(contextArg, this.#config.homeId);
54
+ if (this.#config.credentialSource === 'provider' && !this.#options.resolveAuthorization ||
55
+ this.#config.credentialSource === 'config-store' && !this.#context.configStore) {
56
+ throw new Error('Z-Wave JS selected credential source is unavailable.');
57
+ }
58
+ this.#lifetimeSignal = AbortSignal.any([this.#lifetime.signal,
59
+ ...(this.#context.abortSignal ? [this.#context.abortSignal] : [])]);
60
+ this.#lifetimeAbort = () => this.fail(new Error('Z-Wave JS lifetime was aborted.'));
61
+ this.#lifetimeSignal.addEventListener('abort', this.#lifetimeAbort, { once: true });
39
62
  }
40
63
 
41
64
  public isHealthy(): boolean {
@@ -45,7 +68,7 @@ export class ZwaveJsClient {
45
68
  public start(abortSignalArg?: AbortSignal): Promise<void> {
46
69
  if (this.#destroyed || this.#failure) return Promise.reject(this.#failure ?? new Error('Z-Wave JS client is destroyed.'));
47
70
  if (abortSignalArg?.aborted || this.#lifetimeSignal?.aborted) return Promise.reject(new Error('Z-Wave JS setup was aborted.'));
48
- if (!this.#startPromise) this.#startPromise = this.connect();
71
+ if (!this.#startPromise) this.#startPromise = Promise.resolve().then(() => this.connect());
49
72
  return this.withAbort(this.#startPromise, abortSignalArg);
50
73
  }
51
74
 
@@ -56,9 +79,22 @@ export class ZwaveJsClient {
56
79
  return this.snapshot();
57
80
  }
58
81
 
59
- public async sendCommand(commandArg: IZwaveJsServerCommand, abortSignalArg?: AbortSignal): Promise<IZwaveJsWriteResult> {
82
+ public sendCommand(commandArg: IZwaveJsServerCommand, abortSignalArg?: AbortSignal): Promise<IZwaveJsWriteResult> {
83
+ if (!this.isHealthy() || !this.#config.allowControls) return Promise.reject(new Error('Z-Wave JS controls require an authorized healthy server.'));
84
+ if (this.#operations.size >= 64) return Promise.reject(new Error('Z-Wave JS operation capacity reached.'));
85
+ // Capture the exact identity/value before deferred work or foreign callbacks.
86
+ let command: IZwaveJsServerCommand;
87
+ try { command = ZwaveJsMapper.validateCommand(this.snapshot(), commandArg); }
88
+ catch (errorArg) { return Promise.reject(errorArg); }
89
+ return this.track(() => this.executeCommand(command, abortSignalArg));
90
+ }
91
+
92
+ private async executeCommand(commandArg: IZwaveJsServerCommand, abortSignalArg?: AbortSignal): Promise<IZwaveJsWriteResult> {
60
93
  if (abortSignalArg?.aborted) throw new Error('Z-Wave JS control was aborted before dispatch.');
61
94
  if (!this.isHealthy()) throw new Error('Z-Wave JS control requires a healthy verified server.');
95
+ // Fresh authoritative advertised membership/metadata is required for every effect.
96
+ await this.requestState(abortSignalArg);
97
+ if (!this.isHealthy()) throw new Error('Z-Wave JS control metadata is unavailable or stale.');
62
98
  const command = ZwaveJsMapper.validateCommand(this.snapshot(), commandArg);
63
99
  const response = await this.request('node.set_value', {
64
100
  nodeId: command.nodeId, valueId: command.valueId, value: command.value,
@@ -70,44 +106,94 @@ export class ZwaveJsClient {
70
106
  throw error;
71
107
  }
72
108
  const status = response.result.status;
73
- return { status, outcome: status === 255 ? 'confirmed' : status === 1 ? 'working' : status === 254 ? 'unsupervised' : 'rejected' };
109
+ return { status, outcome: status === 255 ? 'confirmed' : status === 1 ? 'working' : status === 254 ? 'unsupervised' : 'rejected', causalityConfirmed: false };
74
110
  }
75
111
 
76
- public onEvent(handlerArg: TEventHandler): () => void {
112
+ public onEvent(handlerArg: TEventHandler): () => Promise<void> {
77
113
  if (this.#destroyed) throw new Error('Z-Wave JS client is destroyed.');
78
- this.#handlers.add(handlerArg);
79
- return () => { this.#handlers.delete(handlerArg); };
114
+ if (typeof handlerArg !== 'function') throw new TypeError('Z-Wave JS subscriber must be a function.');
115
+ if (this.#handlers.size >= 64) throw new Error('Z-Wave JS subscriber capacity reached.');
116
+ const subscription: ISubscription = { handler: handlerArg, active: true, pending: new Set(), failures: new Set() };
117
+ this.#handlers.add(subscription);
118
+ return async () => {
119
+ subscription.active = false;
120
+ await Promise.allSettled([...subscription.pending]);
121
+ if (subscription.failures.size) throw new AggregateError([...subscription.failures], 'Z-Wave JS subscriber cleanup failed.');
122
+ this.#handlers.delete(subscription);
123
+ };
80
124
  }
81
125
 
82
126
  public destroy(): Promise<void> {
83
127
  if (!this.#closePromise) {
84
128
  this.#destroyed = true;
129
+ // Publish the stable memo before abort/failure emits any foreign callback.
130
+ this.#closePromise = Promise.resolve().then(() => this.closeOwnedResources());
85
131
  this.fail(new Error('Z-Wave JS client is destroyed.'));
86
- this.#handlers.clear();
87
- this.#closePromise = this.closeOwnedResources();
132
+ for (const subscription of this.#handlers) subscription.active = false;
88
133
  }
89
134
  return this.#closePromise;
90
135
  }
91
136
 
137
+ /** Retry retained cleanup only. Never reconnect, resolve credentials or repeat commands. */
138
+ public retryCleanup(): Promise<void> {
139
+ if (!this.#destroyed) return this.destroy();
140
+ if (!this.#retryPromise) {
141
+ this.#retryPromise = Promise.resolve().then(async () => {
142
+ await Promise.allSettled([this.#closePromise]);
143
+ this.#cleanupFailures.clear();
144
+ for (const subscription of this.#handlers) subscription.failures.clear();
145
+ await this.closeOwnedResources();
146
+ });
147
+ void this.#retryPromise.then(() => { this.#retryPromise = undefined; }, () => { this.#retryPromise = undefined; });
148
+ }
149
+ return this.#retryPromise;
150
+ }
151
+
92
152
  private async connect(): Promise<void> {
153
+ if (this.#failure || this.#destroyed || this.#lifetimeSignal?.aborted) throw new Error('Z-Wave JS setup was stopped.');
154
+ const deadlineAt = performance.now() + this.#config.requestTimeoutMs!;
155
+ let authorization: string | undefined;
156
+ if (this.#config.credentialRef) {
157
+ const signal = this.#lifetimeSignal!;
158
+ const request = Object.freeze({ homeId: this.#context.homeId, credentialRef: this.#config.credentialRef, signal, deadline: deadlineAt });
159
+ // The actual promise is owned before entering the borrowed provider/store callback.
160
+ const resolution = this.track(async () => {
161
+ try {
162
+ return this.#config.credentialSource === 'provider' ? await this.#options.resolveAuthorization!(request) :
163
+ await this.#context.configStore!.get<string>(request.credentialRef);
164
+ } catch (errorArg) {
165
+ const error = new Error('Z-Wave JS credential resolution failed.');
166
+ if (signal.aborted && errorArg !== signal.reason) this.#cleanupFailures.add(error);
167
+ throw error;
168
+ }
169
+ });
170
+ try { authorization = await this.waitForCredential(resolution, signal, deadlineAt); }
171
+ catch { this.fail(new Error('Z-Wave JS credential admission failed.')); throw this.#failure; }
172
+ if (typeof authorization !== 'string' || !authorization.trim() || authorization.length > 4096 || /[\x00-\x1f\x7f]/.test(authorization)) {
173
+ this.fail(new Error('Z-Wave JS credential provider returned an invalid authorization header.'));
174
+ throw this.#failure;
175
+ }
176
+ }
177
+ if (this.#destroyed || this.#failure || this.#lifetimeSignal?.aborted || performance.now() >= deadlineAt) {
178
+ this.fail(new Error('Z-Wave JS setup was stopped before connection.'));
179
+ throw this.#failure;
180
+ }
93
181
  const greeting = new Promise<void>((resolveArg, rejectArg) => {
94
182
  this.#greeting = { resolve: resolveArg, reject: rejectArg };
95
183
  });
96
184
  const greetingOutcome = greeting.then(() => ({ accepted: true as const }),
97
185
  (errorArg: unknown) => ({ accepted: false as const, error: errorArg }));
98
- const deadline = setTimeout(() => this.fail(new Error('Z-Wave JS setup deadline expired.')), this.#config.requestTimeoutMs);
99
- if (this.#lifetimeSignal) {
100
- this.#lifetimeAbort = () => this.fail(new Error('Z-Wave JS lifetime was aborted.'));
101
- this.#lifetimeSignal.addEventListener('abort', this.#lifetimeAbort, { once: true });
102
- }
186
+ const remainingMs = Math.max(1, deadlineAt - performance.now());
187
+ const deadline = setTimeout(() => this.fail(new Error('Z-Wave JS setup deadline expired.')), remainingMs);
103
188
  try {
104
189
  const socket = new plugins.ws.WebSocket(this.#config.url, {
105
- headers: this.#config.authorization ? { Authorization: this.#config.authorization } : undefined,
190
+ headers: authorization ? { Authorization: authorization } : undefined,
106
191
  ca: this.#config.ca, rejectUnauthorized: true, followRedirects: false,
107
- handshakeTimeout: this.#config.requestTimeoutMs,
192
+ handshakeTimeout: remainingMs,
108
193
  maxPayload: 2 * 1024 * 1024, maxFragments: 128, maxBufferedChunks: 128, perMessageDeflate: false,
109
194
  });
110
195
  this.#socket = socket;
196
+ authorization = undefined;
111
197
  this.#socketClosed = new Promise<void>((resolveArg) => socket.once('close', () => resolveArg()));
112
198
  socket.on('error', () => this.fail(new Error('Z-Wave JS WebSocket connection failed.')));
113
199
  socket.on('close', () => this.fail(new Error('Z-Wave JS WebSocket connection closed.')));
@@ -155,8 +241,8 @@ export class ZwaveJsClient {
155
241
  });
156
242
  }
157
243
 
158
- private requestState(): Promise<unknown> {
159
- return this.request('start_listening', {}, undefined, (resultArg) => {
244
+ private requestState(abortSignalArg?: AbortSignal): Promise<unknown> {
245
+ return this.request('start_listening', {}, abortSignalArg, (resultArg) => {
160
246
  if (!zwaveJsRecord(resultArg)) throw new Error('Malformed Z-Wave JS state result.');
161
247
  this.#state = parseZwaveJsState(resultArg.state, this.#config.expectedHomeId);
162
248
  this.#snapshotStale = false;
@@ -243,7 +329,7 @@ export class ZwaveJsClient {
243
329
  this.#refreshNeeded = true;
244
330
  this.#snapshotStale = true;
245
331
  if (this.#refreshPromise) return;
246
- this.#refreshPromise = (async () => {
332
+ this.#refreshPromise = Promise.resolve().then(async () => {
247
333
  try {
248
334
  while (this.#refreshNeeded && !this.#failure && !this.#destroyed) {
249
335
  this.#refreshNeeded = false;
@@ -253,22 +339,38 @@ export class ZwaveJsClient {
253
339
  } catch (errorArg) {
254
340
  this.fail(errorArg instanceof Error ? errorArg : new Error('Z-Wave JS state reconciliation failed.'));
255
341
  } finally { this.#refreshPromise = undefined; }
256
- })();
342
+ });
257
343
  }
258
344
 
259
345
  private emit(eventArg: IZwaveJsServerEvent): void {
260
346
  this.#events.push(structuredClone(eventArg));
261
347
  if (this.#events.length > 64) this.#events.shift();
262
- for (const handler of this.#handlers) {
263
- try { handler(structuredClone(eventArg)); } catch {
264
- this.#handlers.delete(handler);
265
- this.fail(new Error('Z-Wave JS subscriber failed; the runtime was stopped.'));
348
+ for (const subscription of this.#handlers) {
349
+ if (!subscription.active) continue;
350
+ if (this.#operations.size >= 64) {
351
+ this.fail(new Error('Z-Wave JS subscriber operation capacity reached.'));
352
+ break;
266
353
  }
354
+ const event = structuredClone(eventArg);
355
+ const notification = this.track(async () => {
356
+ if (!subscription.active) return;
357
+ try { await subscription.handler(event); }
358
+ catch {
359
+ const error = new Error('Z-Wave JS subscriber failed; the runtime was stopped.');
360
+ subscription.failures.add(error);
361
+ this.#cleanupFailures.add(error);
362
+ subscription.active = false;
363
+ this.fail(error);
364
+ }
365
+ });
366
+ subscription.pending.add(notification);
367
+ void notification.then(() => subscription.pending.delete(notification), () => subscription.pending.delete(notification));
267
368
  }
268
369
  }
269
370
 
270
371
  private snapshot(): IZwaveJsSnapshot {
271
372
  return structuredClone({
373
+ homeId: this.#context.homeId, allowControls: this.#config.allowControls === true,
272
374
  version: this.#version, controller: this.#state?.controller, nodes: this.#state?.nodes ?? [],
273
375
  connected: this.isHealthy(), events: this.#events,
274
376
  });
@@ -282,15 +384,36 @@ export class ZwaveJsClient {
282
384
  this.#greeting?.reject(errorArg);
283
385
  for (const pending of this.#pending.values()) { pending.cleanup(); pending.reject(errorArg); }
284
386
  this.#pending.clear();
387
+ this.#lifetime.abort(errorArg);
285
388
  this.#socket?.terminate();
286
389
  this.emit({ source: 'connection', event: 'closed' });
287
390
  }
288
391
 
289
392
  private async closeOwnedResources(): Promise<void> {
290
393
  if (this.#lifetimeAbort) this.#lifetimeSignal?.removeEventListener('abort', this.#lifetimeAbort);
291
- await Promise.allSettled([this.#startPromise, this.#refreshPromise, this.#socketClosed].filter((promiseArg) => promiseArg !== undefined));
394
+ await Promise.allSettled([this.#startPromise, this.#refreshPromise, this.#socketClosed, ...this.#operations].filter((promiseArg) => promiseArg !== undefined));
292
395
  this.#socket?.removeAllListeners();
293
396
  this.#socket = undefined;
397
+ if (this.#cleanupFailures.size) throw new AggregateError([...this.#cleanupFailures], 'Z-Wave JS cleanup failed; settled failures remain retained for explicit cleanup retry.');
398
+ this.#handlers.clear();
399
+ }
400
+
401
+ private track<TResult>(workArg: () => Promise<TResult>): Promise<TResult> {
402
+ const operation = Promise.resolve().then(workArg);
403
+ this.#operations.add(operation);
404
+ void operation.then(() => this.#operations.delete(operation), () => this.#operations.delete(operation));
405
+ return operation;
406
+ }
407
+
408
+ private waitForCredential(promiseArg: Promise<string | undefined>, signalArg: AbortSignal, deadlineArg: number): Promise<string | undefined> {
409
+ return new Promise((resolveArg, rejectArg) => {
410
+ const abort = () => { cleanup(); rejectArg(new Error('Z-Wave JS credential admission was aborted.')); };
411
+ const timer = setTimeout(() => { cleanup(); rejectArg(new Error('Z-Wave JS credential deadline expired.')); }, Math.max(1, deadlineArg - performance.now()));
412
+ const cleanup = () => { clearTimeout(timer); signalArg.removeEventListener('abort', abort); };
413
+ signalArg.addEventListener('abort', abort, { once: true });
414
+ if (signalArg.aborted) abort();
415
+ void promiseArg.then((valueArg) => { cleanup(); resolveArg(valueArg); }, () => { cleanup(); rejectArg(new Error('Z-Wave JS credential resolution failed.')); });
416
+ });
294
417
  }
295
418
 
296
419
  private withAbort(promiseArg: Promise<void>, abortSignalArg?: AbortSignal): Promise<void> {
@@ -302,7 +425,8 @@ export class ZwaveJsClient {
302
425
  rejectArg(error);
303
426
  };
304
427
  abortSignalArg.addEventListener('abort', abort, { once: true });
305
- promiseArg.then(resolveArg, rejectArg).finally(() => abortSignalArg.removeEventListener('abort', abort));
428
+ void promiseArg.then(() => { abortSignalArg.removeEventListener('abort', abort); resolveArg(); },
429
+ (errorArg: unknown) => { abortSignalArg.removeEventListener('abort', abort); rejectArg(errorArg); });
306
430
  });
307
431
  }
308
432
  }