@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,117 +1,57 @@
1
- export type TDenonavrZone = 'Main' | 'Zone2' | 'Zone3';
2
-
3
- export type TDenonavrPowerState = 'ON' | 'STANDBY' | 'OFF' | string;
4
-
5
- export type TDenonavrMediaState = 'on' | 'off' | 'playing' | 'paused' | 'stopped' | 'idle' | string;
6
-
7
- export type TDenonavrReceiverType = 'avr' | 'avr-x' | 'avr-x-2016' | string;
8
-
9
- export type TDenonavrCommand =
10
- | 'turn_on'
11
- | 'turn_off'
12
- | 'volume_up'
13
- | 'volume_down'
14
- | 'set_volume'
15
- | 'mute'
16
- | 'select_source'
17
- | 'play'
18
- | 'pause'
19
- | 'stop'
20
- | 'play_pause'
21
- | 'previous_track'
22
- | 'next_track'
23
- | 'get_command';
1
+ export type TDenonavrModel = 'AVR-1913' | 'AVR-2113CI' | 'NR1605' | 'SR5009';
24
2
 
25
3
  export interface IDenonavrConfig {
26
- host?: string;
27
- port?: number;
4
+ homeId: string;
5
+ host: string;
6
+ descriptionUrl: string;
7
+ expectedUdn: string;
8
+ model: TDenonavrModel;
9
+ maximumVolumeDb: number;
28
10
  name?: string;
29
- model?: string;
30
- manufacturer?: string;
31
- serialNumber?: string;
32
- receiverType?: TDenonavrReceiverType;
33
- descriptionPort?: number;
34
- showAllSources?: boolean;
35
- zone2?: boolean;
36
- zone3?: boolean;
37
- sourceMap?: Record<string, string>;
38
- receiverInfo?: IDenonavrReceiverInfo;
39
- zones?: IDenonavrZoneState[];
40
- snapshot?: IDenonavrSnapshot;
41
- }
42
-
43
- export interface IHomeAssistantDenonavrConfig extends IDenonavrConfig {}
44
-
45
- export interface IDenonavrReceiverInfo {
46
- host?: string;
47
11
  port?: number;
48
- name?: string;
49
- friendlyName?: string;
50
- manufacturer?: string;
51
- modelName?: string;
52
- modelNumber?: string;
53
- serialNumber?: string;
54
- receiverType?: TDenonavrReceiverType;
55
- receiverPort?: number;
56
- presentationUrl?: string;
57
- softwareVersion?: string;
12
+ timeoutMs?: number;
13
+ maxPendingOperations?: number;
14
+ maxDescriptionBytes?: number;
58
15
  }
59
16
 
60
- export interface IDenonavrMediaInfo {
61
- title?: string;
62
- artist?: string;
63
- album?: string;
64
- band?: string;
65
- frequency?: string;
66
- station?: string;
67
- imageUrl?: string;
68
- contentType?: string;
69
- }
17
+ export interface IHomeAssistantDenonavrConfig extends IDenonavrConfig {}
70
18
 
71
- export interface IDenonavrZoneState {
72
- zone: TDenonavrZone;
73
- name?: string;
74
- power?: TDenonavrPowerState;
75
- state?: TDenonavrMediaState;
76
- volumeDb?: number;
77
- volumeLevel?: number;
78
- muted?: boolean;
79
- source?: string;
80
- sourceList?: string[];
81
- sourceMap?: Record<string, string>;
82
- soundMode?: string;
83
- soundModeRaw?: string;
84
- soundModeList?: string[];
85
- dynamicEq?: boolean;
86
- ecoMode?: string;
87
- media?: IDenonavrMediaInfo;
88
- available?: boolean;
19
+ export interface IDenonavrIdentity {
20
+ readonly udn: string;
21
+ readonly manufacturer: 'Denon' | 'Marantz';
22
+ readonly model: TDenonavrModel;
23
+ readonly name: string;
24
+ readonly serialNumber?: string;
25
+ readonly source: 'selected-upnp-description';
26
+ readonly observedAt: string;
89
27
  }
90
28
 
29
+ /** Receiver settings are observations, not proof of electrical or acoustic effects. */
91
30
  export interface IDenonavrSnapshot {
92
- receiverInfo: IDenonavrReceiverInfo;
93
- zones: IDenonavrZoneState[];
94
- events?: IDenonavrEvent[];
95
- lastUpdated?: string;
31
+ readonly homeId: string;
32
+ readonly host: string;
33
+ readonly port: number;
34
+ readonly name: string;
35
+ readonly identity: IDenonavrIdentity;
36
+ readonly power: 'on' | 'standby';
37
+ readonly muted: boolean;
38
+ readonly volumeDb: number | null;
39
+ readonly input: string;
40
+ readonly observedInputs: readonly string[];
41
+ readonly maximumVolumeDb: number;
42
+ readonly observedAt: string;
96
43
  }
97
44
 
98
- export interface IDenonavrCommandRequest {
99
- command: TDenonavrCommand;
100
- zone?: TDenonavrZone;
101
- source?: string;
102
- volumeLevel?: number;
103
- volumeDb?: number;
104
- muted?: boolean;
105
- path?: string;
106
- }
45
+ export type TDenonavrControl =
46
+ | { feature: 'power'; value: boolean }
47
+ | { feature: 'muted'; value: boolean }
48
+ | { feature: 'volume_db'; value: number }
49
+ | { feature: 'input'; value: string };
107
50
 
108
- export interface IDenonavrEvent {
109
- type: 'telnet' | 'http' | 'state';
110
- zone?: TDenonavrZone;
111
- event?: string;
112
- parameter?: string;
113
- command?: TDenonavrCommand;
114
- timestamp: number;
51
+ export interface IDenonavrEffectResult {
52
+ readonly dispatch: 'written';
53
+ readonly outcome: 'observed-target' | 'indeterminate';
54
+ readonly snapshot: IDenonavrSnapshot;
115
55
  }
116
56
 
117
57
  export interface IDenonavrSsdpRecord {
@@ -122,21 +62,18 @@ export interface IDenonavrSsdpRecord {
122
62
  upnp?: Record<string, string | undefined>;
123
63
  }
124
64
 
125
- export interface IDenonavrMdnsRecord {
126
- name?: string;
127
- type?: string;
128
- host?: string;
129
- port?: number;
130
- txt?: Record<string, string | undefined>;
131
- }
132
-
133
65
  export interface IDenonavrManualEntry {
134
66
  host?: string;
135
- port?: number;
67
+ descriptionUrl?: string;
68
+ expectedUdn?: string;
136
69
  id?: string;
137
70
  name?: string;
138
71
  manufacturer?: string;
139
72
  model?: string;
140
- serialNumber?: string;
141
73
  metadata?: Record<string, unknown>;
142
74
  }
75
+
76
+ export interface IDenonavrIntegrationOptions {
77
+ readonly homeId?: string;
78
+ readonly capacity?: number;
79
+ }
@@ -0,0 +1,87 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import type { IDenonavrConfig, TDenonavrControl, TDenonavrModel } from './denonavr.types.js';
3
+
4
+ export function denonavrRecord(value: unknown, allowed?: readonly string[]): Record<string, unknown> {
5
+ if (!value || typeof value !== 'object' || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) {
6
+ throw new TypeError('DenonAVR input must be a plain data object.');
7
+ }
8
+ const result: Record<string, unknown> = {};
9
+ for (const key of Reflect.ownKeys(value)) {
10
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
11
+ if (typeof key !== 'string' || (allowed && !allowed.includes(key)) || !descriptor || !('value' in descriptor) || !descriptor.enumerable) {
12
+ throw new TypeError('DenonAVR input contains an unsupported field or accessor.');
13
+ }
14
+ Object.defineProperty(result, key, { value: descriptor.value, enumerable: true });
15
+ }
16
+ return result;
17
+ }
18
+
19
+ export function denonavrText(value: unknown, label: string, maximum = 256): string {
20
+ if (typeof value !== 'string' || !value || value.length > maximum || value !== value.trim() || /[\p{Cc}\p{Cs}]/u.test(value)) {
21
+ throw new TypeError(`DenonAVR ${label} must be bounded text without control characters.`);
22
+ }
23
+ return value;
24
+ }
25
+
26
+ export function denonavrHost(value: unknown): string {
27
+ if (typeof value !== 'string' || plugins.net.isIP(value) !== 4) throw new TypeError('DenonAVR requires a selected private IPv4 address.');
28
+ const [first, second, , last] = value.split('.').map(Number);
29
+ if (!(first === 10 || first === 127 || (first === 172 && second >= 16 && second <= 31) || (first === 192 && second === 168)) || last === 0 || last === 255) {
30
+ throw new TypeError('DenonAVR requires a selected private or loopback host IPv4 address.');
31
+ }
32
+ return value;
33
+ }
34
+
35
+ export function denonavrModel(value: unknown): TDenonavrModel {
36
+ if (value !== 'AVR-1913' && value !== 'AVR-2113CI' && value !== 'NR1605' && value !== 'SR5009') throw new TypeError('DenonAVR model has no documented selected-receiver profile.');
37
+ return value;
38
+ }
39
+
40
+ export function denonavrUdn(value: unknown): string {
41
+ if (typeof value !== 'string' || !/^uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) throw new TypeError('DenonAVR requires an exact UPnP UUID.');
42
+ return value.toLowerCase();
43
+ }
44
+
45
+ export function denonavrDescription(value: unknown, host: string): string {
46
+ const text = denonavrText(value, 'description URL', 2048);
47
+ const url = new URL(text);
48
+ if (url.protocol !== 'http:' || url.hostname !== host || url.username || url.password || url.search || url.hash || url.pathname === '/') {
49
+ throw new TypeError('DenonAVR description URL must select an HTTP path at the receiver IP without credentials, query or fragment.');
50
+ }
51
+ return url.href;
52
+ }
53
+
54
+ export function denonavrVolume(value: unknown): number {
55
+ if (typeof value !== 'number' || !Number.isFinite(value) || value < -79.5 || value > 18 || !Number.isInteger(value * 2)) {
56
+ throw new TypeError('DenonAVR absolute volume must be -79.5 through 18 dB in half-decibel steps.');
57
+ }
58
+ return value;
59
+ }
60
+
61
+ function integer(value: unknown, fallback: number, minimum: number, maximum: number): number {
62
+ if (value === undefined) return fallback;
63
+ if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < minimum || value > maximum) throw new RangeError('DenonAVR numeric bound is invalid.');
64
+ return value;
65
+ }
66
+
67
+ export function normalizeDenonavrConfig(value: unknown): Readonly<Required<Omit<IDenonavrConfig, 'name'>> & Pick<IDenonavrConfig, 'name'>> {
68
+ const config = denonavrRecord(value, ['homeId', 'host', 'descriptionUrl', 'expectedUdn', 'model', 'maximumVolumeDb', 'name', 'port', 'timeoutMs', 'maxPendingOperations', 'maxDescriptionBytes']);
69
+ const host = denonavrHost(config.host);
70
+ return Object.freeze({
71
+ homeId: denonavrText(config.homeId, 'home ID'), host,
72
+ descriptionUrl: denonavrDescription(config.descriptionUrl, host), expectedUdn: denonavrUdn(config.expectedUdn),
73
+ model: denonavrModel(config.model), maximumVolumeDb: denonavrVolume(config.maximumVolumeDb),
74
+ ...(config.name === undefined ? {} : { name: denonavrText(config.name, 'name', 128) }),
75
+ port: integer(config.port, 23, 1, 65535), timeoutMs: integer(config.timeoutMs, 5000, 1, 60000),
76
+ maxPendingOperations: integer(config.maxPendingOperations, 16, 1, 128), maxDescriptionBytes: integer(config.maxDescriptionBytes, 65536, 256, 1048576),
77
+ });
78
+ }
79
+
80
+ export function validateDenonavrControl(value: unknown): TDenonavrControl {
81
+ const control = denonavrRecord(value, ['feature', 'value']);
82
+ if (control.feature === 'power' && typeof control.value === 'boolean') return { feature: 'power', value: control.value };
83
+ if (control.feature === 'muted' && typeof control.value === 'boolean') return { feature: 'muted', value: control.value };
84
+ if (control.feature === 'volume_db') return { feature: 'volume_db', value: denonavrVolume(control.value) };
85
+ if (control.feature === 'input') return { feature: 'input', value: denonavrText(control.value, 'physical input', 25) };
86
+ throw new TypeError('DenonAVR supports one explicit power, mute, absolute volume or observed physical input control.');
87
+ }
@@ -1,10 +1,15 @@
1
1
  import * as plugins from '../../plugins.js';
2
- import type { IEsphomeClientCommand, IEsphomeCommandResult, IEsphomeConfig, IEsphomeEntityDescriptor, IEsphomeEntityState, IEsphomeEvent, IEsphomeSnapshot } from './esphome.types.js';
3
- import { esphomeDefaultNativeApiPort } from './esphome.types.js';
2
+ import type { IEsphomeClientCommand, IEsphomeCommandResult, IEsphomeConfig, IEsphomeEntityDescriptor, IEsphomeEntityState, IEsphomeEvent, IEsphomeSnapshot, TEsphomeCredentials } from './esphome.types.js';
3
+ import { captureEsphomeConfig, captureEsphomeCredentials, esphomeMac } from './esphome.validation.js';
4
4
 
5
5
  type TEsphomeEventHandler = (eventArg: IEsphomeEvent) => void;
6
6
  type TNativeClient = plugins.esphomeNativeApi.EspHomeClient;
7
7
  type TNativeEntity = plugins.esphomeNativeApi.Entity;
8
+ interface IEsphomeCleanup {
9
+ readonly cleanup: () => void;
10
+ failed: boolean;
11
+ promise?: Promise<void>;
12
+ }
8
13
 
9
14
  const readablePlatforms = new Set(['light', 'switch', 'fan', 'cover', 'climate', 'number', 'select', 'text', 'sensor', 'binary_sensor', 'text_sensor']);
10
15
  const writablePlatforms = new Set(['light', 'switch', 'fan', 'cover', 'number', 'select', 'text']);
@@ -12,12 +17,12 @@ const writablePlatforms = new Set(['light', 'switch', 'fan', 'cover', 'number',
12
17
  export class EsphomeClient {
13
18
  private readonly connection: EsphomeNativeApiConnection;
14
19
 
15
- constructor(configArg: IEsphomeConfig, nativeClientArg?: TNativeClient) {
16
- this.connection = new EsphomeNativeApiConnection(configArg, nativeClientArg);
20
+ constructor(configArg: IEsphomeConfig, nativeClientArg?: TNativeClient, credentialsArg: TEsphomeCredentials = {}) {
21
+ this.connection = new EsphomeNativeApiConnection(configArg, nativeClientArg, credentialsArg);
17
22
  }
18
23
 
19
- public async connectLive(signalArg?: AbortSignal): Promise<void> {
20
- await this.connection.connect(signalArg);
24
+ public connectLive(signalArg?: AbortSignal): Promise<void> {
25
+ return this.connection.connect(signalArg);
21
26
  }
22
27
 
23
28
  public isHealthy(): boolean {
@@ -28,16 +33,20 @@ export class EsphomeClient {
28
33
  return this.connection.getSnapshot();
29
34
  }
30
35
 
31
- public onEvent(handlerArg: TEsphomeEventHandler): () => void {
32
- return this.connection.onEvent(handlerArg);
36
+ public onEvent(handlerArg: TEsphomeEventHandler, signalArg?: AbortSignal): () => void {
37
+ return this.connection.onEvent(handlerArg, signalArg);
33
38
  }
34
39
 
35
40
  public async sendCommand(commandArg: IEsphomeClientCommand, signalArg?: AbortSignal): Promise<IEsphomeCommandResult> {
36
41
  return this.connection.sendCommand(commandArg, signalArg);
37
42
  }
38
43
 
39
- public async destroy(): Promise<void> {
40
- await this.connection.destroy();
44
+ public destroy(): Promise<void> {
45
+ return this.connection.destroy();
46
+ }
47
+
48
+ public retryCleanup(): Promise<void> {
49
+ return this.connection.retryCleanup();
41
50
  }
42
51
  }
43
52
 
@@ -48,83 +57,88 @@ export class EsphomeNativeApiConnection {
48
57
  private readonly setupTimeoutMs: number;
49
58
  private readonly commandTimeoutMs: number;
50
59
  private readonly requireEncryption: boolean;
60
+ private readonly config: IEsphomeConfig;
51
61
  private readonly handlers = new Set<TEsphomeEventHandler>();
52
- private readonly subscriptions: Disposable[] = [];
62
+ private readonly cleanup = new Set<IEsphomeCleanup>();
63
+ private readonly operations = new Set<Promise<unknown>>();
53
64
  private readonly pendingCommands = new Set<string>();
54
65
  private readonly lifetime = new AbortController();
55
66
  private ready = false;
56
67
  private connecting = false;
57
68
  private destroyed = false;
69
+ private subscriptionsStarted = false;
70
+ private identityMismatch = false;
71
+ private closePromise?: Promise<void>;
72
+ private retryPromise?: Promise<void>;
58
73
 
59
- constructor(configArg: IEsphomeConfig, nativeClientArg?: TNativeClient) {
60
- const manual = configArg.manualEntries?.[0];
61
- if (configArg.password || manual?.password) {
62
- throw new Error('ESPHome legacy API password authentication is unsupported. Upgrade the device to ESPHome 2025.10 or newer and configure native API encryption.');
63
- }
64
- if (configArg.snapshot || configArg.entities || configArg.states || configArg.services || configArg.commandExecutor) {
65
- throw new Error('ESPHome live setup does not accept injected snapshots, states, services, or command executors.');
66
- }
67
- if (configArg.manualEntries && configArg.manualEntries.length > 1) {
68
- throw new Error('Configure each ESPHome native API device as a separate integration instance.');
69
- }
70
- this.host = (configArg.host || manual?.host || '').trim();
71
- if (!this.host || /\s/.test(this.host)) {
72
- throw new Error('ESPHome native API requires a valid host.');
73
- }
74
- this.port = configArg.port ?? manual?.port ?? esphomeDefaultNativeApiPort;
75
- if (!Number.isInteger(this.port) || this.port < 1 || this.port > 65535) {
76
- throw new Error('ESPHome native API port must be an integer from 1 through 65535.');
77
- }
78
- this.setupTimeoutMs = this.timeout(configArg.initialSnapshotTimeoutMs, 15000);
79
- this.commandTimeoutMs = this.timeout(configArg.commandTimeoutMs, 5000);
80
- const keys = [configArg.encryptionKey, configArg.noisePsk, manual?.encryptionKey, manual?.noisePsk].filter((valueArg): valueArg is string => valueArg !== undefined);
81
- if (new Set(keys).size > 1) throw new Error('ESPHome encryption key configuration is conflicting.');
82
- const key = keys[0];
83
- if (key !== undefined && (!/^[A-Za-z0-9+/]{43}=$/.test(key) || Buffer.from(key, 'base64').length !== 32 || Buffer.from(key, 'base64').toString('base64') !== key)) {
84
- throw new Error('ESPHome encryption key must be a canonical base64-encoded 32-byte key.');
85
- }
86
- this.requireEncryption = key !== undefined;
74
+ constructor(configArg: IEsphomeConfig, nativeClientArg?: TNativeClient, credentialsArg: TEsphomeCredentials = {}) {
75
+ this.config = captureEsphomeConfig(configArg);
76
+ const credentials = captureEsphomeCredentials(credentialsArg);
77
+ this.host = this.config.host;
78
+ this.port = this.config.port ?? 6053;
79
+ this.setupTimeoutMs = this.config.initialSnapshotTimeoutMs ?? 15000;
80
+ this.commandTimeoutMs = this.config.commandTimeoutMs ?? 5000;
81
+ this.requireEncryption = credentials.psk != null;
87
82
  this.native = nativeClientArg ?? new plugins.esphomeNativeApi.EspHomeClient({
88
83
  host: this.host,
89
84
  port: this.port,
90
- psk: key,
91
- serverName: configArg.deviceName || manual?.deviceName,
85
+ psk: credentials.psk,
86
+ serverName: this.config.deviceName,
92
87
  connectTimeoutMs: this.setupTimeoutMs,
93
88
  });
94
- this.subscriptions.push(
95
- this.native.on('telemetry', (eventArg) => {
96
- this.refreshReadiness();
97
- this.emit({ type: 'state_changed', entityId: this.eventEntityId(eventArg), data: eventArg });
98
- }),
99
- this.native.onHealthChange((healthArg) => {
100
- if (healthArg.state === 'connected' && this.requireEncryption && !healthArg.encrypted) {
101
- this.ready = false;
102
- this.native.disconnect();
103
- this.emit({ type: 'error', data: { message: 'ESPHome encryption is required; the plaintext session was rejected.' } });
104
- return;
105
- }
106
- this.refreshReadiness();
107
- this.emit({ type: 'availability_changed', data: { available: this.isHealthy() } });
108
- }),
109
- );
89
+ // Construction is inert: own the native client before invoking any subscription callbacks.
90
+ this.cleanup.add({ cleanup: () => this.native.disconnect(), failed: false });
91
+ }
92
+
93
+ private startSubscriptions(): void {
94
+ if (this.subscriptionsStarted) return;
95
+ this.subscriptionsStarted = true;
96
+ const telemetry = this.native.on('telemetry', (eventArg) => {
97
+ this.refreshReadiness();
98
+ if (!this.identityMismatch) this.emit({ type: 'state_changed', entityId: this.eventEntityId(eventArg), data: eventArg });
99
+ });
100
+ this.cleanup.add({ cleanup: () => telemetry[Symbol.dispose](), failed: false });
101
+ this.lifetime.signal.throwIfAborted();
102
+ const health = this.native.onHealthChange((healthArg) => {
103
+ if (healthArg.state === 'connected' && this.requireEncryption && !healthArg.encrypted) {
104
+ this.ready = false;
105
+ this.native.disconnect();
106
+ this.emit({ type: 'error', data: { message: 'ESPHome encryption is required; the plaintext session was rejected.' } });
107
+ return;
108
+ }
109
+ this.refreshReadiness();
110
+ this.emit({ type: 'availability_changed', data: { available: this.isHealthy() } });
111
+ });
112
+ this.cleanup.add({ cleanup: () => health[Symbol.dispose](), failed: false });
113
+ this.lifetime.signal.throwIfAborted();
110
114
  }
111
115
 
112
- public async connect(signalArg?: AbortSignal): Promise<void> {
113
- if (this.destroyed || this.connecting || this.isHealthy()) throw new Error('ESPHome connection is destroyed, already connecting, or already connected.');
116
+ public connect(signalArg?: AbortSignal): Promise<void> {
117
+ if (this.destroyed || this.connecting || this.isHealthy()) return Promise.reject(new Error('ESPHome connection is destroyed, already connecting, or already connected.'));
114
118
  this.connecting = true;
115
- const signal = AbortSignal.any([this.lifetime.signal, AbortSignal.timeout(this.setupTimeoutMs), ...(signalArg ? [signalArg] : [])]);
116
- try {
117
- signal.throwIfAborted();
118
- await this.native.connect({ signal });
119
- if (this.requireEncryption && !this.native.health().encrypted) throw new Error('ESPHome encryption is required; the plaintext session was rejected.');
120
- if (this.native.deviceInfo()?.usesPassword) throw new Error('ESPHome legacy API password authentication is unsupported.');
121
- await this.waitForInitialStates(signal);
122
- } catch (error) {
123
- await this.destroy();
124
- throw error;
125
- } finally {
126
- this.connecting = false;
127
- }
119
+ const operation = this.track(async () => {
120
+ const signal = AbortSignal.any([this.lifetime.signal, AbortSignal.timeout(this.setupTimeoutMs), ...(signalArg ? [signalArg] : [])]);
121
+ try {
122
+ signal.throwIfAborted();
123
+ this.startSubscriptions();
124
+ await this.native.connect({ signal });
125
+ if (this.requireEncryption && !this.native.health().encrypted) throw new Error('ESPHome encryption is required; the plaintext session was rejected.');
126
+ if (this.native.deviceInfo()?.usesPassword) throw new Error('ESPHome legacy API password authentication is unsupported.');
127
+ this.assertIdentity();
128
+ await this.waitForInitialStates(signal);
129
+ } catch {
130
+ // SDK diagnostics may contain credentials; do not expose their messages or causes.
131
+ throw new Error('ESPHome native setup failed or was cancelled.');
132
+ } finally {
133
+ this.connecting = false;
134
+ }
135
+ });
136
+ // Join cleanup only after the tracked connect operation settles, so cleanup never awaits itself.
137
+ return operation.catch(async (errorArg: unknown) => {
138
+ try { await this.destroy(); }
139
+ catch (cleanupErrorArg) { throw new AggregateError([errorArg, cleanupErrorArg], 'ESPHome setup and owned cleanup failed.'); }
140
+ throw errorArg;
141
+ });
128
142
  }
129
143
 
130
144
  public isHealthy(): boolean {
@@ -132,9 +146,11 @@ export class EsphomeNativeApiConnection {
132
146
  }
133
147
 
134
148
  public getSnapshot(): IEsphomeSnapshot {
149
+ if (this.identityMismatch) throw new Error('ESPHome selected device identity changed; native observations are unavailable.');
135
150
  const connected = this.isHealthy();
136
151
  const entities = this.native.entitiesByDevice(undefined).filter((entityArg) => readablePlatforms.has(entityArg.type));
137
152
  return {
153
+ homeId: this.config.homeId,
138
154
  host: this.host,
139
155
  port: this.port,
140
156
  connected,
@@ -155,13 +171,31 @@ export class EsphomeNativeApiConnection {
155
171
  };
156
172
  }
157
173
 
158
- public onEvent(handlerArg: TEsphomeEventHandler): () => void {
174
+ public onEvent(handlerArg: TEsphomeEventHandler, signalArg?: AbortSignal): () => void {
159
175
  if (this.destroyed) throw new Error('ESPHome connection was destroyed.');
176
+ signalArg?.throwIfAborted();
177
+ let active = true;
178
+ const dispose = (): void => {
179
+ if (!active) return;
180
+ signalArg?.removeEventListener('abort', dispose);
181
+ this.handlers.delete(handlerArg);
182
+ active = false;
183
+ this.cleanup.delete(resource);
184
+ };
185
+ const resource: IEsphomeCleanup = { cleanup: dispose, failed: false };
186
+ // Own the handler and caller-signal listener before the foreign registration method.
187
+ this.cleanup.add(resource);
160
188
  this.handlers.add(handlerArg);
161
- return () => this.handlers.delete(handlerArg);
189
+ signalArg?.addEventListener('abort', dispose, { once: true });
190
+ if (signalArg?.aborted) dispose();
191
+ return dispose;
162
192
  }
163
193
 
164
- public async sendCommand(commandArg: IEsphomeClientCommand, signalArg?: AbortSignal): Promise<IEsphomeCommandResult> {
194
+ public sendCommand(commandArg: IEsphomeClientCommand, signalArg?: AbortSignal): Promise<IEsphomeCommandResult> {
195
+ return this.track(() => this.executeTrackedCommand(commandArg, signalArg));
196
+ }
197
+
198
+ private async executeTrackedCommand(commandArg: IEsphomeClientCommand, signalArg?: AbortSignal): Promise<IEsphomeCommandResult> {
165
199
  signalArg?.throwIfAborted();
166
200
  if (!this.isHealthy()) return { success: false, error: 'ESPHome is unavailable or has not received its authoritative initial states.' };
167
201
  const entity = this.native.entitiesByDevice(undefined).find((entityArg) => entityArg.key === commandArg.key && entityArg.type === commandArg.platform && (entityArg.deviceId ?? 0) === (commandArg.deviceId ?? 0));
@@ -174,22 +208,56 @@ export class EsphomeNativeApiConnection {
174
208
  try {
175
209
  const state = await this.executeCommand(entity, commandArg.payload, signalArg);
176
210
  return { success: true, data: structuredClone(state) };
177
- } catch (error) {
178
- return { success: false, error: error instanceof Error ? error.message : 'ESPHome command failed.' };
211
+ } catch {
212
+ return { success: false, error: 'ESPHome command failed or was cancelled; its physical effect is unknown.' };
179
213
  } finally {
180
214
  this.pendingCommands.delete(plugins.esphomeNativeApi.entityId(entity.type, entity.objectId));
181
215
  }
182
216
  }
183
217
 
184
- public async destroy(): Promise<void> {
185
- if (this.destroyed) return;
186
- this.destroyed = true;
187
- this.ready = false;
188
- this.lifetime.abort(new Error('ESPHome connection was destroyed.'));
189
- this.native.disconnect();
190
- for (const subscription of this.subscriptions) subscription[Symbol.dispose]();
191
- this.subscriptions.length = 0;
218
+ public destroy(): Promise<void> {
219
+ if (!this.closePromise) {
220
+ this.destroyed = true;
221
+ this.ready = false;
222
+ this.closePromise = Promise.resolve().then(() => this.drain(false));
223
+ this.lifetime.abort(new Error('ESPHome connection was destroyed.'));
224
+ }
225
+ return this.closePromise;
226
+ }
227
+
228
+ public retryCleanup(): Promise<void> {
229
+ if (!this.closePromise) throw new Error('ESPHome cleanup retry requires a closed client.');
230
+ if (!this.retryPromise) {
231
+ this.retryPromise = Promise.resolve().then(() => this.drain(true));
232
+ const settled = () => { this.retryPromise = undefined; };
233
+ void this.retryPromise.then(settled, settled);
234
+ }
235
+ return this.retryPromise;
236
+ }
237
+
238
+ private track<T>(workArg: () => Promise<T>): Promise<T> {
239
+ if (this.destroyed) return Promise.reject(new Error('ESPHome connection was destroyed.'));
240
+ // Store the actual operation before invoking native or consumer callbacks.
241
+ const operation = Promise.resolve().then(workArg);
242
+ this.operations.add(operation);
243
+ const settled = () => this.operations.delete(operation);
244
+ void operation.then(settled, settled);
245
+ return operation;
246
+ }
247
+
248
+ private async drain(retryArg: boolean): Promise<void> {
249
+ const cleanup = [...this.cleanup].reverse().map((resourceArg) => {
250
+ if (retryArg && resourceArg.failed) { resourceArg.promise = undefined; resourceArg.failed = false; }
251
+ resourceArg.promise ??= Promise.resolve().then(() => resourceArg.cleanup()).then(() => undefined, () => {
252
+ resourceArg.failed = true;
253
+ throw new Error('ESPHome owned resource cleanup failed.');
254
+ });
255
+ return resourceArg.promise;
256
+ });
257
+ const [results] = await Promise.all([Promise.allSettled(cleanup), Promise.allSettled([...this.operations])]);
192
258
  this.handlers.clear();
259
+ const failures = results.filter((resultArg) => resultArg.status === 'rejected');
260
+ if (failures.length) throw new AggregateError(failures.map(() => new Error('ESPHome owned resource cleanup failed.')), 'ESPHome cleanup failed; resources remain retained for explicit retry.');
193
261
  }
194
262
 
195
263
  private async executeCommand(entityArg: TNativeEntity, payloadArg: Record<string, unknown>, signalArg?: AbortSignal): Promise<unknown> {
@@ -253,14 +321,32 @@ export class EsphomeNativeApiConnection {
253
321
 
254
322
  private refreshReadiness(): void {
255
323
  const health = this.native.health();
256
- this.ready = health.state === 'connected' && (!this.requireEncryption || health.encrypted) && this.native.entitiesByDevice(undefined).filter((entityArg) => readablePlatforms.has(entityArg.type)).every((entityArg) => this.native.latest(plugins.esphomeNativeApi.entityId(entityArg.type, entityArg.objectId)) != null);
324
+ if (health.state === 'connected') {
325
+ try { this.assertIdentity(); }
326
+ catch {
327
+ this.identityMismatch = true;
328
+ this.ready = false;
329
+ this.native.disconnect();
330
+ this.emit({ type: 'error', data: { message: 'ESPHome selected native device identity changed.' } });
331
+ return;
332
+ }
333
+ }
334
+ this.ready = !this.destroyed && !this.identityMismatch && health.state === 'connected' && (!this.requireEncryption || health.encrypted) && this.native.entitiesByDevice(undefined).filter((entityArg) => readablePlatforms.has(entityArg.type)).every((entityArg) => this.native.latest(plugins.esphomeNativeApi.entityId(entityArg.type, entityArg.objectId)) != null);
335
+ }
336
+
337
+ private assertIdentity(): void {
338
+ const info = this.native.deviceInfo();
339
+ if (!info || esphomeMac(info.macAddress) !== this.config.expectedMacAddress || info.name !== this.config.deviceName
340
+ || (this.config.expectedModel !== undefined && info.model !== this.config.expectedModel)) {
341
+ throw new Error('ESPHome native device identity does not match the selected endpoint.');
342
+ }
257
343
  }
258
344
 
259
345
  private entityId(entityArg: TNativeEntity): string {
260
346
  const macAddress = this.native.deviceInfo()?.macAddress?.toLowerCase().replace(/[^a-f0-9]/g, '');
261
347
  const identity = macAddress?.length === 12 ? macAddress : `${this.host}:${this.port}`;
262
348
  const digest = plugins.crypto.createHash('sha256').update(JSON.stringify([identity, entityArg.type, entityArg.key, entityArg.deviceId ?? 0])).digest('hex').slice(0, 24);
263
- return `${entityArg.type}.esphome_${digest}`;
349
+ return `home.${encodeURIComponent(this.config.homeId)}.${entityArg.type}.esphome_${digest}`;
264
350
  }
265
351
 
266
352
  private eventEntityId(eventArg: plugins.esphomeNativeApi.TelemetryEvent): string | undefined {
@@ -285,12 +371,11 @@ export class EsphomeNativeApiConnection {
285
371
  }
286
372
 
287
373
  private emit(eventArg: IEsphomeEvent): void {
288
- for (const handler of this.handlers) handler({ ...structuredClone(eventArg), timestamp: Date.now() });
289
- }
290
-
291
- private timeout(valueArg: number | undefined, defaultArg: number): number {
292
- const value = valueArg ?? defaultArg;
293
- if (!Number.isInteger(value) || value < 1 || value > 2147483647) throw new Error('ESPHome timeouts must be integers from 1 through 2147483647.');
294
- return value;
374
+ const failures: unknown[] = [];
375
+ for (const handler of this.handlers) {
376
+ try { handler({ ...structuredClone(eventArg), timestamp: Date.now() }); }
377
+ catch (error) { failures.push(error); }
378
+ }
379
+ if (failures.length) throw new AggregateError(failures.map(() => new Error('ESPHome subscriber callback failed.')), 'ESPHome event callbacks failed.');
295
380
  }
296
381
  }