@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,14 +1,14 @@
1
1
  import * as plugins from '../../plugins.js';
2
2
  import { BaseIntegration } from '../../core/classes.baseintegration.js';
3
- import { NoSqlConfigStore } from '../../core/classes.configstore.js';
4
- import type { IDeviceFeatureWriteRequest, IIntegrationEntity, IIntegrationEvent, IIntegrationRuntime, IIntegrationSetupContext, IServiceCallRequest, IServiceCallResult } from '../../core/types.js';
3
+ import { NativeIntegrationOwner } from '../../core/classes.nativeintegrationowner.js';
4
+ import type { IDeviceFeatureWriteRequest, IIntegrationEntity, IIntegrationEvent, IIntegrationRuntime, INativeIntegrationSetupContext, IServiceCallRequest, IServiceCallResult } from '../../core/types.js';
5
5
  import { WebostvClient } from './webostv.classes.client.js';
6
6
  import { WebostvConfigFlow } from './webostv.classes.configflow.js';
7
7
  import { createWebostvDiscoveryDescriptor } from './webostv.discovery.js';
8
8
  import { WebostvMapper } from './webostv.mapper.js';
9
9
  import type { IWebostvConfig, IWebostvIntegrationOptions, IWebostvSnapshot, TWebostvCommand } from './webostv.types.js';
10
10
  import { webostvProfile } from './webostv.types.js';
11
- import { webostvKeys, webostvObject } from './webostv.validation.js';
11
+ import { normalizeWebostvConfig, normalizeWebostvOptions, webostvKeys, webostvObject } from './webostv.validation.js';
12
12
 
13
13
  export class WebostvIntegration extends BaseIntegration<IWebostvConfig> {
14
14
  public readonly domain = 'webostv';
@@ -16,51 +16,58 @@ export class WebostvIntegration extends BaseIntegration<IWebostvConfig> {
16
16
  public readonly status = 'control-runtime' as const;
17
17
  public readonly metadata = webostvProfile.metadata;
18
18
  public readonly discoveryDescriptor = createWebostvDiscoveryDescriptor();
19
- public readonly configFlow = new WebostvConfigFlow();
20
- readonly #runtimes = new Set<WebostvRuntime>();
21
- readonly #pending = new Set<WebostvClient>();
22
- #destroyed = false;
19
+ public readonly configFlow: WebostvConfigFlow;
20
+ readonly #owner: NativeIntegrationOwner;
21
+ readonly #options: Readonly<IWebostvIntegrationOptions>;
23
22
  #destroy?: Promise<void>;
24
23
 
25
- constructor(private readonly options?: IWebostvIntegrationOptions) { super(); }
26
-
27
- public async setup(configArg: IWebostvConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
28
- if (this.#destroyed) throw new Error('webOS integration is destroyed.');
29
- if (contextArg.configStore !== undefined && !(contextArg.configStore instanceof NoSqlConfigStore)) throw new TypeError('webOS runtime context requires an encrypted NoSqlConfigStore.');
30
- const client = new WebostvClient(configArg, contextArg.configStore ?? this.options, contextArg.abortSignal);
31
- this.#pending.add(client);
32
- try {
33
- const snapshot = await client.getSnapshot(contextArg.abortSignal);
34
- if (this.#destroyed) throw new Error('webOS integration was destroyed during setup.');
35
- const runtime = new WebostvRuntime(client, snapshot, () => this.#runtimes.delete(runtime));
36
- this.#runtimes.add(runtime);
37
- return runtime;
38
- } catch (errorArg) { await client.destroy(); throw errorArg; }
39
- finally { this.#pending.delete(client); }
24
+ constructor(optionsArg: IWebostvIntegrationOptions = {}) {
25
+ super();
26
+ this.#options = normalizeWebostvOptions(optionsArg);
27
+ this.#owner = new NativeIntegrationOwner(this.domain, { homeId: this.#options.homeId, capacity: this.#options.capacity });
28
+ this.configFlow = new WebostvConfigFlow({}, this.#owner);
40
29
  }
41
30
 
42
- public destroy(): Promise<void> {
43
- if (this.#destroy) return this.#destroy;
44
- this.#destroyed = true;
45
- this.#destroy = Promise.all([...this.#runtimes].map((runtimeArg) => runtimeArg.destroy()).concat([...this.#pending].map((clientArg) => clientArg.destroy()))).then(() => undefined);
46
- return this.#destroy;
31
+ public setup(configArg: IWebostvConfig, contextArg: INativeIntegrationSetupContext): Promise<IIntegrationRuntime> {
32
+ const config = normalizeWebostvConfig(configArg);
33
+ return this.#owner.runSetup(contextArg, config.homeId, async (leaseArg) => {
34
+ const client = new WebostvClient(config, leaseArg.context, this.#options);
35
+ let cleanupStarted = false;
36
+ leaseArg.own(() => {
37
+ if (cleanupStarted) return client.retryCleanup();
38
+ cleanupStarted = true;
39
+ return client.destroy();
40
+ });
41
+ const snapshot = await client.getSnapshot();
42
+ leaseArg.promoteToRuntime();
43
+ return new WebostvRuntime(client, snapshot, () => leaseArg.close(), (promiseArg) => promiseArg === this.#destroy);
44
+ });
47
45
  }
46
+
47
+ public destroy(): Promise<void> { return this.#destroy ??= this.#owner.close(); }
48
+ public retryCleanup(): Promise<void> { return this.#owner.retryCleanup(); }
48
49
  }
49
50
 
50
51
  export class HomeAssistantWebostvIntegration extends WebostvIntegration {}
51
52
 
52
53
  class WebostvRuntime implements IIntegrationRuntime {
53
54
  public readonly domain = 'webostv';
54
- constructor(private readonly client: WebostvClient, private readonly identity: IWebostvSnapshot, private readonly onDestroyed: () => void) {}
55
+ #destroy?: Promise<void>;
56
+ constructor(private readonly client: WebostvClient, private readonly identity: IWebostvSnapshot,
57
+ private readonly close: () => Promise<void>, private readonly isOwnerClose: (promiseArg: unknown) => boolean) {}
55
58
  public isHealthy(): boolean { return this.client.isHealthy(); }
56
59
  public async devices(signalArg?: AbortSignal): Promise<plugins.shxInterfaces.data.IDeviceDefinition[]> { return WebostvMapper.toDevices(await this.client.getSnapshot(signalArg)); }
57
60
  public async entities(): Promise<IIntegrationEntity[]> { return WebostvMapper.toEntities(await this.client.getSnapshot()); }
58
61
  public async subscribe(handlerArg: (eventArg: IIntegrationEvent) => void, signalArg?: AbortSignal): Promise<() => Promise<void>> {
62
+ const handler = handlerArg as (eventArg: IIntegrationEvent) => void | Promise<void>;
59
63
  return this.client.subscribe((eventArg) => {
60
64
  const base = { integrationDomain: this.domain, deviceId: WebostvMapper.deviceId(this.identity), timestamp: Date.now() };
61
- if (eventArg.type === 'state') handlerArg({ ...base, type: 'state_changed', entityId: `${base.deviceId}:media_player`, data: { entity: WebostvMapper.toEntities(eventArg.snapshot)[0] } });
62
- else if (eventArg.type === 'availability') handlerArg({ ...base, type: 'availability_changed', data: { available: eventArg.available } });
63
- else handlerArg({ ...base, type: 'error', data: { message: eventArg.error.message } });
65
+ const result = eventArg.type === 'state'
66
+ ? handler({ ...base, type: 'state_changed', entityId: `${base.deviceId}:media_player`, data: { entity: WebostvMapper.toEntities(eventArg.snapshot)[0] } })
67
+ : eventArg.type === 'availability'
68
+ ? handler({ ...base, type: 'availability_changed', data: { available: eventArg.available } })
69
+ : handler({ ...base, type: 'error', data: { message: eventArg.error.message } });
70
+ return result === this.#destroy || this.isOwnerClose(result) ? undefined : result;
64
71
  }, signalArg);
65
72
  }
66
73
 
@@ -81,8 +88,7 @@ class WebostvRuntime implements IIntegrationRuntime {
81
88
  else if (service === 'volume_mute') { webostvKeys(data, ['mute']); argument = data.mute; }
82
89
  else if (service === 'select_source') { webostvKeys(data, ['inputId']); argument = data.inputId; }
83
90
  else webostvKeys(data, []);
84
- await this.client.execute(service as TWebostvCommand, argument);
85
- return { success: true, data: { acknowledgement: 'tv-response', state: 'observe-subscriptions' } };
91
+ return { success: true, data: await this.client.execute(service as TWebostvCommand, argument) };
86
92
  } catch (errorArg) { return { success: false, error: errorArg instanceof Error ? errorArg.message : 'webOS operation failed.' }; }
87
93
  }
88
94
 
@@ -92,11 +98,12 @@ class WebostvRuntime implements IIntegrationRuntime {
92
98
  const device = webostvObject(request.device, 'feature device');
93
99
  const feature = webostvObject(request.feature, 'feature');
94
100
  if (device.id !== WebostvMapper.deviceId(this.identity) || device.integrationDomain !== this.domain) throw new Error('webOS feature device is not owned by this runtime.');
95
- if (feature.id === 'volume') await this.client.execute('volume_set', request.value, signalArg);
96
- else if (feature.id === 'muted') await this.client.execute('volume_mute', request.value, signalArg);
101
+ let result;
102
+ if (feature.id === 'volume') result = await this.client.execute('volume_set', request.value, signalArg);
103
+ else if (feature.id === 'muted') result = await this.client.execute('volume_mute', request.value, signalArg);
97
104
  else throw new Error('webOS feature is not writable.');
98
- return { success: true, data: { acknowledgement: 'tv-response', state: 'observe-subscriptions' } };
105
+ return { success: true, data: result };
99
106
  } catch (errorArg) { return { success: false, error: errorArg instanceof Error ? errorArg.message : 'webOS feature write failed.' }; }
100
107
  }
101
- public async destroy(): Promise<void> { await this.client.destroy(); this.onDestroyed(); }
108
+ public destroy(): Promise<void> { return this.#destroy ??= this.close(); }
102
109
  }
@@ -13,15 +13,15 @@ export class WebostvMapper {
13
13
  ];
14
14
  return [{
15
15
  id: this.deviceId(snapshotArg), integrationDomain: 'webostv', name: snapshotArg.name,
16
- protocol: 'http', manufacturer: 'LG', model: 'webOS TV', online: snapshotArg.connected,
16
+ protocol: 'http', manufacturer: 'LG', model: snapshotArg.model, online: snapshotArg.connected,
17
17
  features: [
18
18
  { id: 'power', capability: 'media', name: 'Power state', readable: true, writable: false },
19
- { id: 'volume', capability: 'media', name: 'Volume', readable: true, writable: true, unit: '%' },
20
- { id: 'muted', capability: 'media', name: 'Muted', readable: true, writable: true },
19
+ { id: 'volume', capability: 'media', name: 'Volume', readable: true, writable: snapshotArg.connected && snapshotArg.authorizedCommands.includes('volume_set'), unit: '%' },
20
+ { id: 'muted', capability: 'media', name: 'Muted', readable: true, writable: snapshotArg.connected && snapshotArg.authorizedCommands.includes('volume_mute') },
21
21
  { id: 'current_app', capability: 'media', name: 'Foreground application', readable: true, writable: false },
22
22
  ],
23
23
  state: states.map(([featureId, value]) => ({ featureId, value, updatedAt: snapshotArg.observedAt })),
24
- metadata: { homeId: snapshotArg.homeId, deviceUUID: snapshotArg.deviceUUID, transport: 'ssap-websocket', inputs: structuredClone(snapshotArg.inputs) },
24
+ metadata: { homeId: snapshotArg.homeId, deviceUUID: snapshotArg.deviceUUID, transport: 'ssap-websocket', inputs: structuredClone(snapshotArg.inputs), authorizedCommands: [...snapshotArg.authorizedCommands], modelKnown: snapshotArg.model !== undefined },
25
25
  }];
26
26
  }
27
27
 
@@ -1,5 +1,6 @@
1
1
  import type * as plugins from '../../plugins.js';
2
2
  import type { IConfigEncryptionKeyProvider } from '../../core/classes.configstore.js';
3
+ import type { INativeIntegrationOwnerOptions } from '../../core/classes.nativeintegrationowner.js';
3
4
 
4
5
  export const webostvDomain = 'webostv';
5
6
  export const webostvDefaultName = 'LG webOS TV';
@@ -19,9 +20,11 @@ export interface IWebostvConfig {
19
20
  maxPendingRequests?: number;
20
21
  }
21
22
 
22
- export interface IWebostvIntegrationOptions {
23
- db: plugins.nosqldb.SmartdataDb;
24
- keyProvider: IConfigEncryptionKeyProvider;
23
+ export interface IWebostvIntegrationOptions extends INativeIntegrationOwnerOptions {
24
+ db?: plugins.nosqldb.SmartdataDb;
25
+ keyProvider?: IConfigEncryptionKeyProvider;
26
+ /** Runtime-only owner permission. An omitted or empty list authorizes no effects. */
27
+ allowedCommands?: readonly TWebostvCommand[];
25
28
  }
26
29
 
27
30
  export interface IHomeAssistantWebostvConfig extends IWebostvConfig {}
@@ -30,7 +33,10 @@ export interface IWebostvSnapshot {
30
33
  homeId: string;
31
34
  deviceUUID: string;
32
35
  name: string;
36
+ /** Actual system/getSystemInfo modelName, absent when the TV did not report one. */
37
+ model?: string;
33
38
  connected: boolean;
39
+ authorizedCommands: readonly TWebostvCommand[];
34
40
  powerState: string;
35
41
  volume: number;
36
42
  muted: boolean;
@@ -45,6 +51,15 @@ export type TWebostvClientEvent =
45
51
  | { type: 'error'; error: Error };
46
52
  export type TWebostvCommand = 'turn_on' | 'turn_off' | 'volume_set' | 'volume_up' | 'volume_down' | 'volume_mute' | 'select_source' | 'media_play' | 'media_pause' | 'media_stop';
47
53
 
54
+ export interface IWebostvCommandResult {
55
+ acknowledgement: 'tv-response';
56
+ physicalCompletion: 'unverified';
57
+ observationStatus: 'observed' | 'unavailable';
58
+ observation?: IWebostvSnapshot;
59
+ }
60
+
61
+ export type TWebostvClientHandler = (eventArg: TWebostvClientEvent) => void | Promise<void>;
62
+
48
63
  export const webostvProfile = {
49
64
  domain: webostvDomain, displayName: webostvDefaultName, status: 'control-runtime',
50
65
  metadata: { source: 'home-assistant/core', upstreamDomain: webostvDomain, localApi: { implemented: ['verified SSAP registration', 'encrypted durable pairing keys', 'authoritative state subscriptions'], explicitUnsupported: ['fixture or injected-client production configs', 'deep standby wake-on-LAN', 'next/previous media track', 'automatic command replay'] } },
@@ -1,4 +1,25 @@
1
- import type { IWebostvConfig } from './webostv.types.js';
1
+ import type { IWebostvConfig, IWebostvIntegrationOptions, TWebostvCommand } from './webostv.types.js';
2
+
3
+ export const webostvCommands: readonly TWebostvCommand[] = Object.freeze(['turn_on', 'turn_off', 'volume_set', 'volume_up', 'volume_down', 'volume_mute', 'select_source', 'media_play', 'media_pause', 'media_stop']);
4
+
5
+ export const normalizeWebostvOptions = (valueArg: IWebostvIntegrationOptions = {}): Readonly<IWebostvIntegrationOptions> => {
6
+ const value = webostvObject(valueArg, 'webOS owner options');
7
+ webostvKeys(value, ['homeId', 'capacity', 'db', 'keyProvider', 'allowedCommands']);
8
+ if ((value.db === undefined) !== (value.keyProvider === undefined)) throw new TypeError('webOS persistence requires both database and keyProvider.');
9
+ const commands = value.allowedCommands ?? [];
10
+ if (!Array.isArray(commands) || commands.length > webostvCommands.length || Reflect.ownKeys(commands).some((keyArg) => {
11
+ const descriptor = Object.getOwnPropertyDescriptor(commands, keyArg);
12
+ return typeof keyArg !== 'string' || !descriptor || !('value' in descriptor) || (keyArg !== 'length' && !/^(0|[1-9][0-9]*)$/.test(keyArg));
13
+ })) throw new TypeError('webOS allowedCommands must be a finite data array.');
14
+ const captured: TWebostvCommand[] = [];
15
+ for (let index = 0; index < commands.length; index += 1) {
16
+ const command: unknown = Object.getOwnPropertyDescriptor(commands, String(index))?.value;
17
+ if (typeof command !== 'string' || !webostvCommands.includes(command as TWebostvCommand) || captured.includes(command as TWebostvCommand)) throw new TypeError('webOS allowedCommands contains an unsupported or duplicate command.');
18
+ captured.push(command as TWebostvCommand);
19
+ }
20
+ return Object.freeze({ homeId: value.homeId as string | undefined, capacity: value.capacity as number | undefined,
21
+ db: value.db as IWebostvIntegrationOptions['db'], keyProvider: value.keyProvider as IWebostvIntegrationOptions['keyProvider'], allowedCommands: Object.freeze(captured) });
22
+ };
2
23
 
3
24
  export const webostvObject = (valueArg: unknown, nameArg: string): Record<string, unknown> => {
4
25
  if (!valueArg || typeof valueArg !== 'object' || Array.isArray(valueArg) || ![Object.prototype, null].includes(Object.getPrototypeOf(valueArg))) throw new TypeError(`${nameArg} must be a plain object.`);
@@ -4,3 +4,4 @@ export * from './wiz.classes.integration.js';
4
4
  export * from './wiz.discovery.js';
5
5
  export * from './wiz.mapper.js';
6
6
  export * from './wiz.types.js';
7
+ export * from './wiz.validation.js';
@@ -1,278 +1,108 @@
1
- import type {
2
- IWizClientCommand,
3
- IWizCommandResult,
4
- IWizConfig,
5
- IWizDeviceInfo,
6
- IWizEvent,
7
- IWizPilotPatch,
8
- IWizPilotState,
9
- IWizSnapshot,
10
- IWizSnapshotDevice,
11
- IWizUdpCommand,
12
- IWizUdpResponse,
13
- } from './wiz.types.js';
14
- import { wizDefaultPort } from './wiz.types.js';
15
- import { WizMapper } from './wiz.mapper.js';
16
-
17
- type TWizEventHandler = (eventArg: IWizEvent) => void;
1
+ import * as plugins from '../../plugins.js';
2
+ import { NativeHomeAdmission } from '../../core/classes.nativehomeadmission.js';
3
+ import type { IIntegrationSetupContext } from '../../core/types.js';
4
+ import type { IWizConfig, IWizSnapshot, IWizWriteResult, TWizControl } from './wiz.types.js';
5
+ import { normalizeWizConfig, wizControl } from './wiz.validation.js';
18
6
 
19
7
  export class WizClient {
20
- private readonly events: IWizEvent[] = [];
21
- private readonly eventHandlers = new Set<TWizEventHandler>();
22
-
23
- constructor(private readonly config: IWizConfig) {}
24
-
25
- public async getSnapshot(): Promise<IWizSnapshot> {
26
- const host = this.host();
27
- if (!host) {
28
- return WizMapper.toSnapshot(this.config, false, this.events);
29
- }
30
-
31
- try {
32
- const pilot = await this.getPilot();
33
- const deviceInfo = await this.liveDeviceInfo(pilot).catch(() => this.staticDeviceInfo(pilot));
34
- const device: IWizSnapshotDevice = {
35
- host,
36
- port: this.port(),
37
- mac: pilot.mac || deviceInfo.mac || this.config.mac,
38
- name: this.config.name || deviceInfo.name,
39
- deviceInfo,
40
- pilot,
41
- available: true,
42
- };
43
- return WizMapper.toSnapshot({ ...this.config, snapshot: undefined, devices: [device], manualEntries: undefined }, true, this.events);
44
- } catch (error) {
45
- const message = error instanceof Error ? error.message : String(error);
46
- this.emit({ type: 'error', data: { message }, timestamp: Date.now() });
47
- return WizMapper.toSnapshot(this.config, false, this.events);
48
- }
49
- }
50
-
51
- public onEvent(handlerArg: TWizEventHandler): () => void {
52
- this.eventHandlers.add(handlerArg);
53
- return () => this.eventHandlers.delete(handlerArg);
54
- }
55
-
56
- public async getPilot(): Promise<IWizPilotState> {
57
- const response = await this.sendUdp<IWizPilotState>({ method: 'getPilot', params: {} });
58
- const result = this.record(response.result) ? response.result as IWizPilotState : {};
59
- this.emit({ type: 'pilot', data: result, timestamp: Date.now() });
60
- return result;
61
- }
62
-
63
- public async setPilot(payloadArg: IWizPilotPatch): Promise<IWizUdpResponse<Record<string, unknown>>> {
64
- return this.sendUdp<Record<string, unknown>>({ method: 'setPilot', params: payloadArg as Record<string, unknown> });
65
- }
66
-
67
- public async getSystemConfig(): Promise<IWizUdpResponse<Record<string, unknown>>> {
68
- return this.sendUdp<Record<string, unknown>>({ method: 'getSystemConfig', params: {} });
69
- }
70
-
71
- public async sendCommand(commandArg: IWizClientCommand): Promise<IWizCommandResult> {
72
- let result: IWizCommandResult;
73
- if (this.config.commandExecutor) {
74
- result = this.commandResult(await this.config.commandExecutor(commandArg), commandArg);
75
- } else if (!this.host()) {
76
- result = {
77
- success: false,
78
- error: this.unsupportedLiveControlMessage(),
79
- data: { command: commandArg },
80
- };
81
- } else {
82
- try {
83
- const response = await this.setPilot(commandArg.payload);
84
- result = { success: true, data: response.result || response };
85
- } catch (error) {
86
- result = {
87
- success: false,
88
- error: error instanceof Error ? error.message : String(error),
89
- data: { command: commandArg },
90
- };
91
- }
92
- }
93
-
94
- this.emit({
95
- type: result.success ? 'command_mapped' : 'command_failed',
96
- command: commandArg,
97
- data: result,
98
- timestamp: Date.now(),
99
- deviceId: commandArg.deviceId,
100
- entityId: commandArg.entityId,
8
+ readonly #config: ReturnType<typeof normalizeWizConfig>;
9
+ readonly #protocol: plugins.ecobridgeDeviceManager.WizProtocol;
10
+ readonly #lifetime = new AbortController();
11
+ readonly #operations = new Set<Promise<unknown>>();
12
+ readonly #ownerSignal?: AbortSignal;
13
+ #healthy = false;
14
+ #closed = false;
15
+ #closing?: Promise<void>;
16
+ #retrying?: Promise<void>;
17
+
18
+ constructor(configArg: IWizConfig, contextArg: IIntegrationSetupContext) {
19
+ this.#config = normalizeWizConfig(configArg);
20
+ const context = new NativeHomeAdmission('wiz').admit(contextArg, this.#config.homeId);
21
+ const ownerSignalArg = context.abortSignal;
22
+ ownerSignalArg?.throwIfAborted();
23
+ this.#ownerSignal = ownerSignalArg;
24
+ this.#protocol = new plugins.ecobridgeDeviceManager.WizProtocol({
25
+ host: this.#config.host, port: this.#config.port, macAddress: this.#config.macAddress,
26
+ timeoutMs: this.#config.timeoutMs, maxPendingOperations: this.#config.maxPendingOperations, maxExchanges: this.#config.maxExchanges,
101
27
  });
102
- return result;
103
28
  }
104
29
 
105
- public async destroy(): Promise<void> {
106
- this.eventHandlers.clear();
107
- }
30
+ public get homeId(): string { return this.#config.homeId; }
31
+ public get pollIntervalMs(): number { return this.#config.pollIntervalMs; }
32
+ public isHealthy(): boolean { return this.#healthy && !this.#closed; }
108
33
 
109
- private async liveDeviceInfo(pilotArg: IWizPilotState): Promise<IWizDeviceInfo> {
110
- const systemConfig = await this.getSystemConfig();
111
- const result = this.record(systemConfig.result) ? systemConfig.result : {};
112
- return this.staticDeviceInfo(pilotArg, result);
113
- }
114
-
115
- private staticDeviceInfo(pilotArg?: IWizPilotState, systemArg: Record<string, unknown> = {}): IWizDeviceInfo {
116
- const moduleName = this.stringValue(systemArg.moduleName) || this.config.deviceInfo?.moduleName;
117
- const model = this.config.deviceInfo?.model || moduleName;
118
- const isSocket = this.config.deviceInfo?.isSocket ?? this.textContainsSocket(model, moduleName, systemArg.typeId);
119
- return {
120
- ...this.config.deviceInfo,
121
- host: this.host(),
122
- port: this.port(),
123
- mac: this.stringValue(systemArg.mac) || pilotArg?.mac || this.config.mac || this.config.deviceInfo?.mac,
124
- name: this.config.name || this.config.deviceInfo?.name,
125
- manufacturer: this.config.deviceInfo?.manufacturer || 'WiZ',
126
- model,
127
- moduleName,
128
- fwVersion: this.stringValue(systemArg.fwVersion) || this.config.deviceInfo?.fwVersion,
129
- typeId: this.stringValue(systemArg.typeId) || this.numberValue(systemArg.typeId) || this.config.deviceInfo?.typeId,
130
- isSocket,
131
- features: {
132
- light: !isSocket,
133
- switch: isSocket,
134
- brightness: !isSocket || typeof pilotArg?.dimming === 'number',
135
- color: ['r', 'g', 'b'].every((keyArg) => typeof pilotArg?.[keyArg] === 'number') || this.textContains(moduleName, 'rgb'),
136
- colorTemp: typeof pilotArg?.temp === 'number' || this.textContains(moduleName, 'tw'),
137
- effect: typeof pilotArg?.sceneId === 'number' || typeof pilotArg?.schdPsetId === 'number',
138
- fan: typeof pilotArg?.fanState === 'number',
139
- power: typeof pilotArg?.pc === 'number',
140
- occupancy: pilotArg?.src === 'pir',
141
- ...this.config.deviceInfo?.features,
142
- },
143
- };
34
+ public getSnapshot(signalArg?: AbortSignal): Promise<IWizSnapshot> {
35
+ return this.track(async () => {
36
+ const signal = this.signal(signalArg);
37
+ const info = await this.#protocol.getDeviceInfo({ signal });
38
+ signal.throwIfAborted();
39
+ this.#healthy = true;
40
+ return Object.freeze({ homeId: this.#config.homeId, host: this.#config.host, port: this.#config.port,
41
+ name: this.#config.name, info, observedAt: new Date().toISOString() });
42
+ });
144
43
  }
145
44
 
146
- private async sendUdp<TResult>(commandArg: IWizUdpCommand): Promise<IWizUdpResponse<TResult>> {
147
- const host = this.host();
148
- if (!host) {
149
- throw new Error(this.unsupportedLiveControlMessage());
150
- }
151
- const port = this.port();
152
- const timeoutMs = this.timeoutMs();
153
- const { createSocket } = await import('node:dgram');
154
- const payload = Buffer.from(JSON.stringify(commandArg));
155
-
156
- return new Promise<IWizUdpResponse<TResult>>((resolve, reject) => {
157
- const socket = createSocket('udp4');
158
- const timers: Array<ReturnType<typeof setTimeout>> = [];
159
- let settled = false;
160
-
161
- const cleanup = () => {
162
- for (const timer of timers) {
163
- clearTimeout(timer);
164
- }
165
- socket.removeAllListeners();
166
- try {
167
- socket.close();
168
- } catch {
169
- // The socket may already be closed after an early UDP error.
170
- }
171
- };
172
-
173
- const finish = (errorArg: Error | undefined, responseArg?: IWizUdpResponse<TResult>) => {
174
- if (settled) {
175
- return;
176
- }
177
- settled = true;
178
- cleanup();
179
- if (errorArg) {
180
- reject(errorArg);
181
- } else {
182
- resolve(responseArg || {});
183
- }
184
- };
185
-
186
- const send = () => {
187
- if (settled) {
188
- return;
189
- }
190
- socket.send(payload, port, host, (errorArg) => {
191
- if (errorArg) {
192
- finish(errorArg);
193
- }
194
- });
195
- };
196
-
197
- socket.on('error', (errorArg) => finish(errorArg));
198
- socket.on('message', (messageArg) => {
199
- let response: IWizUdpResponse<TResult>;
200
- try {
201
- response = JSON.parse(messageArg.toString('utf8')) as IWizUdpResponse<TResult>;
202
- } catch (error) {
203
- finish(error instanceof Error ? error : new Error(String(error)));
204
- return;
205
- }
206
- if (response.method && response.method !== commandArg.method) {
207
- return;
208
- }
209
- if (response.error) {
210
- finish(new Error(response.error.message || `WiZ UDP error ${response.error.code ?? 'unknown'}`));
211
- return;
45
+ public write(controlArg: TWizControl, signalArg?: AbortSignal): Promise<IWizWriteResult> {
46
+ return this.track(async () => {
47
+ let control: TWizControl;
48
+ let signal: AbortSignal;
49
+ try { control = wizControl(controlArg); signal = this.signal(signalArg); }
50
+ catch { return Object.freeze({ outcome: 'not-sent', physicalStateConfirmed: false }); }
51
+ try {
52
+ const result = control.feature === 'power' ? await this.#protocol.setPower(control.value, { signal }) :
53
+ control.feature === 'brightness' ? await this.#protocol.setBrightness(control.value, { signal }) :
54
+ await this.#protocol.setRgb(control.value, { signal });
55
+ this.#healthy = true;
56
+ return Object.freeze({ outcome: result.outcome, physicalStateConfirmed: false, observedState: result.observedState });
57
+ } catch (errorArg) {
58
+ this.#healthy = false;
59
+ if (errorArg instanceof plugins.ecobridgeDeviceManager.WizProtocolError) {
60
+ return Object.freeze({ outcome: errorArg.effectOutcome, physicalStateConfirmed: false, errorCode: errorArg.code });
212
61
  }
213
- this.emit({ type: 'udp_response', data: response, timestamp: Date.now() });
214
- finish(undefined, response);
215
- });
216
-
217
- send();
218
- timers.push(setTimeout(send, 750));
219
- timers.push(setTimeout(send, 2250));
220
- timers.push(setTimeout(() => finish(new Error(`Timed out waiting for WiZ ${commandArg.method} response from ${host}:${port}.`)), timeoutMs));
62
+ throw errorArg;
63
+ }
221
64
  });
222
65
  }
223
66
 
224
- private emit(eventArg: IWizEvent): void {
225
- this.events.push(eventArg);
226
- for (const handler of this.eventHandlers) {
227
- handler(eventArg);
67
+ public destroy(): Promise<void> {
68
+ if (!this.#closing) {
69
+ this.#closed = true;
70
+ this.#healthy = false;
71
+ this.#closing = Promise.resolve().then(() => this.cleanup());
72
+ this.#lifetime.abort(new Error('WiZ client is destroyed.'));
228
73
  }
74
+ return this.#closing;
229
75
  }
230
76
 
231
- private commandResult(resultArg: unknown, commandArg: IWizClientCommand): IWizCommandResult {
232
- if (this.isCommandResult(resultArg)) {
233
- return resultArg;
77
+ /** Explicitly retries retained cleanup; no protocol operation is replayed. */
78
+ public retryCleanup(): Promise<void> {
79
+ if (!this.#closed) return Promise.reject(new Error('WiZ cleanup retry requires terminal shutdown.'));
80
+ if (!this.#retrying) {
81
+ this.#retrying = Promise.resolve().then(() => this.cleanup()).finally(() => { this.#retrying = undefined; });
234
82
  }
235
- return { success: true, data: resultArg ?? { command: commandArg } };
236
- }
237
-
238
- private isCommandResult(valueArg: unknown): valueArg is IWizCommandResult {
239
- return typeof valueArg === 'object' && valueArg !== null && 'success' in valueArg;
240
- }
241
-
242
- private host(): string | undefined {
243
- return this.config.host || this.config.manualEntries?.find((entryArg) => entryArg.host)?.host;
244
- }
245
-
246
- private port(): number {
247
- const manualPort = this.config.manualEntries?.find((entryArg) => entryArg.host)?.port;
248
- return this.config.port || manualPort || wizDefaultPort;
249
- }
250
-
251
- private timeoutMs(): number {
252
- return typeof this.config.timeoutMs === 'number' && this.config.timeoutMs > 0 ? this.config.timeoutMs : 5000;
253
- }
254
-
255
- private unsupportedLiveControlMessage(): string {
256
- return 'WiZ live UDP control requires a configured host. Snapshot-only WiZ configs are read-only unless commandExecutor is provided.';
257
- }
258
-
259
- private textContainsSocket(...valuesArg: unknown[]): boolean {
260
- return valuesArg.some((valueArg) => this.textContains(valueArg, 'socket') || this.textContains(valueArg, 'plug'));
261
- }
262
-
263
- private textContains(valueArg: unknown, fragmentArg: string): boolean {
264
- return typeof valueArg === 'string' && valueArg.toLowerCase().includes(fragmentArg);
83
+ return this.#retrying;
265
84
  }
266
85
 
267
- private stringValue(valueArg: unknown): string | undefined {
268
- return typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim() : undefined;
86
+ private async cleanup(): Promise<void> {
87
+ const results = await Promise.allSettled([
88
+ Promise.resolve().then(() => this.#protocol.close()),
89
+ Promise.allSettled([...this.#operations]),
90
+ ]);
91
+ const errors = results.flatMap((resultArg) => resultArg.status === 'rejected' ? [resultArg.reason] : []);
92
+ if (errors.length) throw new AggregateError(errors, 'WiZ client cleanup failed after draining admitted operations.');
269
93
  }
270
94
 
271
- private numberValue(valueArg: unknown): number | undefined {
272
- return typeof valueArg === 'number' && Number.isFinite(valueArg) ? valueArg : undefined;
95
+ private signal(signalArg?: AbortSignal): AbortSignal {
96
+ if (this.#closed) throw new Error('WiZ client is destroyed.');
97
+ const signal = AbortSignal.any([this.#lifetime.signal, ...(this.#ownerSignal ? [this.#ownerSignal] : []), ...(signalArg ? [signalArg] : [])]);
98
+ signal.throwIfAborted();
99
+ return signal;
273
100
  }
274
101
 
275
- private record(valueArg: unknown): valueArg is Record<string, unknown> {
276
- return typeof valueArg === 'object' && valueArg !== null && !Array.isArray(valueArg);
102
+ private track<T>(workArg: () => Promise<T>): Promise<T> {
103
+ const operation = workArg().catch((errorArg: unknown) => { this.#healthy = false; throw errorArg; });
104
+ this.#operations.add(operation);
105
+ void operation.then(() => this.#operations.delete(operation), () => this.#operations.delete(operation));
106
+ return operation;
277
107
  }
278
108
  }