@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,35 +1,69 @@
1
- # Native LG webOS TV
1
+ # LG webOS TV
2
2
 
3
- `WebostvIntegration` and the exported `HomeAssistantWebostvIntegration` constructor speak the TV's documented SSAP WebSocket protocol. Setup verifies an explicitly configured TV UUID, registers, persists the returned pairing credential encrypted, and obtains complete initial power, volume, mute, foreground-app and input-list subscriptions before exposing a runtime.
3
+ The native runtime pairs with a verified TV through its SSAP WebSocket endpoint,
4
+ stores pairing keys encrypted in the host's database, and observes power, volume,
5
+ mute, foreground application and external inputs through authoritative subscriptions.
6
+ It uses the installed `ws` transport and requires no Home Assistant server.
7
+ `HomeAssistantWebostvIntegration` remains an exported constructor alias.
4
8
 
5
9
  ```typescript
6
- const integration = new WebostvIntegration({ db, keyProvider });
10
+ const integration = new WebostvIntegration({
11
+ db,
12
+ keyProvider,
13
+ allowedCommands: ['volume_set', 'volume_mute'],
14
+ });
7
15
  const runtime = await integration.setup({
8
- homeId: 'home-one',
9
- endpoint: 'wss://verified-tv.example:3001/',
16
+ homeId: 'home:example',
17
+ endpoint: 'wss://tv.example:3001/',
10
18
  expectedDeviceUUID: verifiedDeviceUUID,
11
19
  pairingMode: 'prompt',
12
- certificateAuthority: verifiedPemCertificateChain,
13
- }, {});
14
- const device = (await runtime.devices())[0];
15
- await runtime.callService!({
16
- domain: 'media_player', service: 'volume_set',
17
- target: { deviceId: device.id }, data: { volume: 25 },
20
+ certificateAuthority: ownerCertificateChain,
21
+ }, { homeId: 'home:example', abortSignal });
22
+
23
+ const [device] = await runtime.devices();
24
+ const result = await runtime.callService!({
25
+ domain: 'webostv',
26
+ service: 'volume_set',
27
+ target: { deviceId: device.id },
28
+ data: { volume: 25 },
18
29
  });
19
30
  await integration.destroy();
20
31
  ```
21
32
 
22
- The default registry's no-argument constructor also supports `setup(config, { configStore })`, where `configStore` is an actual `NoSqlConfigStore` whose public `getScope()` exactly matches the configured home and `webostv` domain. Generic stores and other scopes fail before connection; an invalid provided store never falls back to constructor options. A correctly scoped context store takes precedence over constructor persistence options.
33
+ The host independently selects `context.homeId`. The integration binds that home
34
+ once, including through config flow and failed setup. An optional constructor
35
+ `homeId` constrains the selected context. Configuration, discovery, form values
36
+ and database scope cannot select a home or grant control. Config flow captures
37
+ the selected home and rejects submitted home fields. Its forms stop accepting
38
+ submissions after flow close or integration destruction.
23
39
 
24
- The caller owns `db` and the external `keyProvider` (`currentKey(signal)` / `keyForId(id, signal)`, 32-byte keys). The integration constructs the existing `NoSqlConfigStore` in the immutable `homeId` / `webostv` scope. The pairing record also binds the exact endpoint and TV UUID. Accepted keys are durably stored before subscription setup; storage failure closes setup. There is no filesystem or memory-only credential fallback. `existing` mode rejects any prompt response and closes the connection; the TV may briefly display its prompt before that close. It never waits for or accepts fresh pairing in that mode. Explicit `prompt` mode permits a user to approve initial/rejected-key registration on the TV; a valid stored key is still reused. No key appears in device state, events, command receipts or errors.
40
+ The exported client has the same host boundary:
41
+ `new WebostvClient(config, { homeId, abortSignal }, options)`. Construction performs
42
+ no pairing, provider calls or transport I/O. Setup requires either constructor
43
+ `db` and `keyProvider` together, or an actual `NoSqlConfigStore` in the context
44
+ with the exact selected home and `webostv` domain. Scope checks call the genuine
45
+ store implementation; overriding `getScope` cannot grant ownership. Context
46
+ stores, databases and key providers remain caller-owned.
47
+ The provider supplies 32-byte keys through `currentKey(signal)` and
48
+ `keyForId(id, signal)`. A correctly scoped context store takes precedence over
49
+ constructor persistence; an invalid supplied store fails without falling back.
25
50
 
26
- Use the TV's independently verified UUID and endpoint; discovery supplies no network probes or identity verification by itself. Only explicit manual candidates with `integrationDomain: 'webostv'` and metadata `{ endpoint, expectedDeviceUUID }` are accepted. WSS uses normal certificate-chain and hostname validation, optionally with the supplied PEM CA chain. TLS verification is never disabled. A deliberately selected cleartext local `ws://...:3000/` endpoint requires `allowUnencryptedConnection: true`; its UUID check is a device selection check and provides no cryptographic protection against an attacker on that network. No automatic WS downgrade, redirects or endpoint guessing occur.
51
+ Every effect defaults to denied. The host supplies a copied, immutable
52
+ `allowedCommands` list in constructor options. The supported exact values are
53
+ `turn_on`, `turn_off`, `volume_set`, `volume_up`, `volume_down`, `volume_mute`,
54
+ `select_source`, `media_play`, `media_pause` and `media_stop`. Unknown, duplicate,
55
+ sparse or accessor-bearing lists are rejected. Saved configuration and form
56
+ values cannot set permissions. Volume and mute features are writable only while
57
+ connected and explicitly authorized. Service targets and feature writes must
58
+ refer to this runtime's home-prefixed device and media-player entity; input
59
+ selection requires an advertised input ID. Deep-standby wake-on-LAN and
60
+ next/previous track controls are unsupported.
27
61
 
28
- Supported exact-target services in `webostv` or `media_player`:
62
+ Services accept the `webostv` or `media_player` domain with these exact arguments:
29
63
 
30
- | Service | Exact data | SSAP endpoint |
64
+ | Service | Data | SSAP endpoint |
31
65
  | --- | --- | --- |
32
- | `status`, `snapshot` | none | Last authoritative subscribed state, with current connection availability |
66
+ | `status`, `snapshot` | none | Last authoritative subscribed state and current availability |
33
67
  | `volume_set` | `{ volume: integer 0..100 }` | `audio/setVolume` |
34
68
  | `volume_up`, `volume_down` | none | `audio/volumeUp`, `audio/volumeDown` |
35
69
  | `volume_mute` | `{ mute: boolean }` | `audio/setMute` |
@@ -37,12 +71,71 @@ Supported exact-target services in `webostv` or `media_player`:
37
71
  | `media_play`, `media_pause`, `media_stop` | none | `media.controls/play`, `/pause`, `/stop` |
38
72
  | `turn_on`, `turn_off` | none | `system/turnOn`, `system/turnOff` |
39
73
 
40
- Calls require the connected home-scoped device id or its exact media-player entity id. Foreign domains/devices/entities, unknown services, extra arguments, accessors, arbitrary input ids and out-of-range volume fail before command I/O. `writeFeature` accepts only the owned `volume` and `muted` features with validated values. Power state and foreground-app features are read only.
74
+ The TV must report the expected UUID before an existing pairing key is sent.
75
+ Stored keys are bound to both UUID and endpoint. `prompt` explicitly permits
76
+ pairing confirmation on the TV; `existing` refuses absent or rejected durable
77
+ pairing. TLS certificate validation stays enabled, and an owner-supplied PEM
78
+ chain can establish trust. Cleartext `ws` requires explicit
79
+ `allowUnencryptedConnection: true`. Credentials in endpoints, arbitrary URL
80
+ paths and fixture/injected-client production configs are rejected.
81
+ Discovery admits only explicit manual candidates with the domain and metadata
82
+ `{ endpoint, expectedDeviceUUID }`; it does not probe or verify a TV. Cleartext
83
+ UUID selection provides no cryptographic authentication. There is no automatic
84
+ WS downgrade, endpoint guessing, filesystem persistence or credential fallback.
85
+ The accepted key is stored before subscriptions start. Existing mode never
86
+ accepts fresh pairing, though the TV may display a prompt before disconnection.
87
+
88
+ A successful command receipt reports `acknowledgement: 'tv-response'` and
89
+ `physicalCompletion: 'unverified'`. Independent one-shot reads of the documented
90
+ state endpoints produce `observationStatus: 'observed'` and an observation when
91
+ available. Failure to read back an already accepted command produces
92
+ `observationStatus: 'unavailable'`; it never replays the command or substitutes
93
+ cached state as a fresh observation. Readback does not optimistically overwrite
94
+ subscription state. Model metadata comes from `system/getSystemInfo.modelName`
95
+ and stays absent when unreported. Owner permissions are not advertised as a
96
+ TV-reported per-command capability list.
97
+ Power-off timeout remains indeterminate even if the TV physically turned off.
98
+ `turn_on` requires an already connected TV supporting that method. Media controls
99
+ depend on the foreground application. All five state subscriptions and the
100
+ pre-registration system-info request are required; unsupported firmware fails
101
+ setup explicitly. Remote pointer sockets, app launching, channels and calibration
102
+ remain outside the runtime.
103
+
104
+ The owner defaults to a combined capacity of 16 pending operations, runtimes and
105
+ retained cleanup resources. Runtime destruction releases its setup lease.
106
+ Integration destruction is terminal and shares one close promise. The client
107
+ publishes its close promise before cancellation and availability callbacks, then
108
+ drains startup, commands, accepted async subscribers and owned store cleanup.
109
+ Unsubscribe removes admission and joins callbacks already accepted for that
110
+ subscription. Throwing or rejecting subscribers are isolated and removed.
111
+ Callbacks can request teardown; directly returning the client, runtime or owner's close promise
112
+ does not create a self-wait. A callback must not await teardown that is itself
113
+ waiting for that callback to finish.
114
+
115
+ Late encryption-provider cleanup failures reject close and remain retained.
116
+ After the provider settles, explicit `retryCleanup()` acknowledges cleanup
117
+ without replaying provider calls, pairing or commands, and without reopening
118
+ admission. A client closes only stores it created; borrowed stores remain usable.
119
+ Frames, pending requests, subscribers and inputs remain bounded. Heartbeat or
120
+ malformed authoritative state closes the instance. Disconnected snapshots retain
121
+ their observation timestamp and report unavailable. Reconnection requires a new
122
+ setup; there is no surviving command queue.
41
123
 
42
- Success means a correlated positive TV response, not verified physical completion. State changes only from validated subscription responses. A timeout, cancellation or disconnect after dispatch has an indeterminate effect outcome and is never replayed automatically. TV power-off responses are sometimes absent: that is reported as indeterminate even if the TV turns off. `turn_on` requires an already connected TV that supports that SSAP method; deep standby wake-on-LAN is not implemented. Media commands depend on the current TV application, and vendor rejections fail. Next/previous track, remote pointer sockets, app launching, channels and calibration are outside this runtime.
124
+ Migration requires an independently selected home in setup, flow and client
125
+ contexts, and explicit constructor command permissions for effects. The former
126
+ client `(config, persistenceOptions, signal)` signature becomes
127
+ `(config, { homeId, configStore?, abortSignal? }, options)`. Production `rawData`,
128
+ `snapshot`, `snapshotProvider`, injected clients/command executors and fixture
129
+ mapping remain unsupported; fixture data belongs only in tests.
43
130
 
44
- Connection, pairing and requests have bounded deadlines; frames, pending requests, inputs and subscribers have limits. Heartbeat failure closes the instance. Malformed authoritative responses fail the connection; disconnected snapshots retain their observation timestamp and report unavailable. Caller abort closes setup; `destroy()` rejects pending requests, drains current encrypted-storage work and closes the owned socket. Initialization uses the config store's bounded, cancellable collection preparation. Constructor-created config stores are closed and drained on destroy; injected context stores and the caller's database remain open. Throwing event subscribers are removed without interrupting other subscribers. Reconnection requires a new setup; no command queue survives connection loss.
131
+ Protocol and transport references:
45
132
 
46
- Breaking migration: `rawData`, `snapshot`, `snapshotProvider`, injected `client` / `commandExecutor`, generic host/transport/path configuration and `WebostvMapper.toSnapshot*` fixture mapping were removed from the production webOS config. Supply the explicit native fields and constructor persistence owner above. Existing fixture-only test data belongs in a local protocol fixture, not application configuration. No existing fixture config is automatically converted because it does not prove a TV's identity or provide durable pairing credentials.
133
+ - [SSAP endpoints in aiowebostv](https://github.com/home-assistant-libs/aiowebostv/blob/main/aiowebostv/endpoints.py)
134
+ - [ConnectSDK webOS service](https://github.com/ConnectSDK/Connect-SDK-Android-Core/blob/master/src/com/connectsdk/service/WebOSTVService.java)
135
+ - [ws transport documentation](https://github.com/websockets/ws/blob/master/doc/ws.md)
136
+ - [ws on npm](https://www.npmjs.com/package/ws)
47
137
 
48
- Protocol sources: [LG Connect SDK socket registration and hello](https://github.com/ConnectSDK/Connect-SDK-Android-Core/blob/master/src/com/connectsdk/service/webos/WebOSTVServiceSocketClient.java), [LG SSAP control implementations](https://github.com/ConnectSDK/Connect-SDK-Android-Core/blob/master/src/com/connectsdk/service/WebOSTVService.java), [Home Assistant's maintained SSAP endpoints](https://github.com/home-assistant-libs/aiowebostv/blob/main/aiowebostv/endpoints.py), [pre-registration system-info sequence and state subscriptions](https://github.com/home-assistant-libs/aiowebostv/blob/main/aiowebostv/webos_client.py), [ws transport API](https://github.com/websockets/ws/blob/master/doc/ws.md). Registration uses a neutral unsigned permission manifest, not LG's test identity/signatures. Hardware pairing/firmware behavior was not verified against an owner's TV; local real WebSocket fixtures cover the documented exchange. Setup currently requires the documented pre-registration system-info request and all five state subscriptions; unsupported firmware fails explicitly.
138
+ Verification uses local WebSocket/TLS and disposable in-memory database fixtures.
139
+ Real pairing, firmware acceptance and owner-device control require separate owner
140
+ authorization. No TV-reported per-command service-list contract was confirmed;
141
+ the adapter does not invent one.
@@ -1,7 +1,9 @@
1
1
  import * as plugins from '../../plugins.js';
2
2
  import { NoSqlConfigStore } from '../../core/classes.configstore.js';
3
- import type { IWebostvConfig, IWebostvInput, IWebostvIntegrationOptions, IWebostvSnapshot, TWebostvClientEvent, TWebostvCommand } from './webostv.types.js';
4
- import { normalizeWebostvConfig, webostvInteger, webostvObject, webostvString } from './webostv.validation.js';
3
+ import { NativeHomeAdmission, type INativeAdmissionContext } from '../../core/classes.nativehomeadmission.js';
4
+ import type { INativeIntegrationSetupContext } from '../../core/types.js';
5
+ import type { IWebostvCommandResult, IWebostvConfig, IWebostvInput, IWebostvIntegrationOptions, IWebostvSnapshot, TWebostvClientEvent, TWebostvClientHandler, TWebostvCommand } from './webostv.types.js';
6
+ import { normalizeWebostvConfig, normalizeWebostvOptions, webostvInteger, webostvObject, webostvString } from './webostv.validation.js';
5
7
 
6
8
  interface IPendingRequest {
7
9
  resolve(valueArg: Record<string, unknown>): void;
@@ -26,38 +28,47 @@ export class WebostvClient {
26
28
  readonly #config: Readonly<IWebostvConfig>;
27
29
  readonly #store: NoSqlConfigStore;
28
30
  readonly #ownsStore: boolean;
31
+ readonly #context: INativeAdmissionContext;
32
+ readonly #allowedCommands: readonly TWebostvCommand[];
29
33
  readonly #lifetime = new AbortController();
30
34
  readonly #pending = new Map<string, IPendingRequest>();
31
35
  readonly #subscriptions = new Map<string, (payloadArg: Record<string, unknown>) => void>();
32
- readonly #handlers = new Set<(eventArg: TWebostvClientEvent) => void>();
36
+ readonly #handlers = new Set<TWebostvClientHandler>();
37
+ readonly #operations = new Set<Promise<unknown>>();
38
+ readonly #handlerOperations = new Map<TWebostvClientHandler, Set<Promise<void>>>();
33
39
  #socket?: plugins.ws.WebSocket;
34
40
  #snapshot?: IWebostvSnapshot;
35
41
  #sequence = 0;
36
42
  #start?: Promise<IWebostvSnapshot>;
37
43
  #destroy?: Promise<void>;
44
+ #retry?: Promise<void>;
38
45
  #heartbeat?: ReturnType<typeof setInterval>;
39
46
  #awaitingPong = false;
40
47
  #ready = false;
41
48
  #failure?: Error;
42
- #storage?: Promise<unknown>;
43
- readonly #handlerCleanups = new Map<(eventArg: TWebostvClientEvent) => void, () => void>();
49
+ readonly #handlerCleanups = new Map<TWebostvClientHandler, () => void>();
44
50
  #removeExternalAbort: () => void = () => undefined;
45
51
 
46
- constructor(configArg: IWebostvConfig, optionsArg?: IWebostvIntegrationOptions | NoSqlConfigStore, signalArg?: AbortSignal) {
52
+ constructor(configArg: IWebostvConfig, contextArg: INativeIntegrationSetupContext, optionsArg: IWebostvIntegrationOptions = {}) {
47
53
  this.#config = normalizeWebostvConfig(configArg);
48
- if (optionsArg instanceof NoSqlConfigStore) {
49
- const scope = optionsArg.getScope();
50
- if (scope.homeId !== this.#config.homeId || scope.integrationDomain !== 'webostv') throw new TypeError('webOS requires an encrypted config store scoped to the exact configured home and webostv domain.');
51
- this.#store = optionsArg;
54
+ const options = normalizeWebostvOptions(optionsArg);
55
+ this.#context = new NativeHomeAdmission('webostv', { homeId: options.homeId }).admit(contextArg, this.#config.homeId);
56
+ this.#allowedCommands = options.allowedCommands!;
57
+ if (this.#context.configStore) {
58
+ this.#store = this.#context.configStore;
52
59
  this.#ownsStore = false;
53
60
  } else {
54
- if (!optionsArg?.db || !optionsArg.keyProvider) throw new TypeError('webOS requires an owner-supplied database and external encryption key provider.');
55
- this.#store = new NoSqlConfigStore({ db: optionsArg.db, keyProvider: optionsArg.keyProvider, homeId: this.#config.homeId, integrationDomain: 'webostv' });
61
+ if (!options.db || !options.keyProvider) throw new TypeError('webOS requires an owner-supplied database and external encryption key provider.');
62
+ this.#store = new NoSqlConfigStore({ db: options.db, keyProvider: options.keyProvider, homeId: this.#context.homeId, integrationDomain: 'webostv' });
56
63
  this.#ownsStore = true;
57
64
  }
65
+ }
66
+
67
+ #startAbortListener(): void {
68
+ const signalArg = this.#context.abortSignal;
58
69
  if (signalArg) {
59
- const abort = (): void => { void this.destroy(); };
60
- if (signalArg.aborted) this.#lifetime.abort(new Error('webOS setup was aborted.'));
70
+ const abort = (): void => { void this.destroy().catch(() => undefined); };
71
+ if (signalArg.aborted) abort();
61
72
  else { signalArg.addEventListener('abort', abort, { once: true }); this.#removeExternalAbort = () => signalArg.removeEventListener('abort', abort); }
62
73
  }
63
74
  }
@@ -66,55 +77,99 @@ export class WebostvClient {
66
77
 
67
78
  public async getSnapshot(signalArg?: AbortSignal): Promise<IWebostvSnapshot> {
68
79
  if (signalArg?.aborted) throw new Error('webOS snapshot was aborted.');
69
- if (!this.#start) this.#start = this.#initialize().catch(async (errorArg: unknown) => { await this.destroy(); throw errorArg; });
80
+ if (!this.#start) {
81
+ if (this.#destroy) throw new Error('webOS client is closed.');
82
+ // Track raw startup before foreign work; its rejection handler joins close only after settlement.
83
+ this.#start = this.#track(() => this.#initialize()).catch(async (errorArg: unknown) => {
84
+ try { await this.destroy(); }
85
+ catch (cleanupErrorArg) { throw new AggregateError([errorArg, cleanupErrorArg], 'webOS setup and cleanup failed.'); }
86
+ throw errorArg;
87
+ });
88
+ this.#startAbortListener();
89
+ }
70
90
  await this.#start;
71
91
  if (signalArg?.aborted) throw new Error('webOS snapshot was aborted.');
72
92
  if (!this.#snapshot) throw new Error('webOS state is unavailable.');
73
93
  return structuredClone({ ...this.#snapshot, connected: this.isHealthy() });
74
94
  }
75
95
 
76
- public async execute(commandArg: TWebostvCommand, argumentArg?: unknown, signalArg?: AbortSignal): Promise<void> {
77
- if (!this.isHealthy()) throw new Error('webOS is disconnected; command was not sent.');
78
- let uri: string;
79
- let payload: Record<string, unknown> = {};
80
- if (commandArg === 'volume_set') { uri = 'audio/setVolume'; payload = { volume: webostvInteger(argumentArg, 'volume', 0, 100) }; }
81
- else if (commandArg === 'volume_mute') {
82
- if (typeof argumentArg !== 'boolean') throw new TypeError('webOS mute must be boolean.');
83
- uri = 'audio/setMute'; payload = { mute: argumentArg };
84
- } else if (commandArg === 'select_source') {
85
- const inputId = webostvString(argumentArg, 'inputId');
86
- if (!this.#snapshot?.inputs.some((inputArg) => inputArg.id === inputId)) throw new TypeError('webOS input must match an advertised input id.');
87
- uri = 'tv/switchInput'; payload = { inputId };
88
- } else {
89
- if (!Object.hasOwn(commandEndpoints, commandArg) || argumentArg !== undefined) throw new TypeError('Unsupported webOS command or extra arguments.');
90
- uri = commandEndpoints[commandArg];
91
- }
92
- await this.#request('request', uri, payload, undefined, signalArg);
96
+ public execute(commandArg: TWebostvCommand, argumentArg?: unknown, signalArg?: AbortSignal): Promise<IWebostvCommandResult> {
97
+ return this.#track(async () => {
98
+ if (!this.#allowedCommands.includes(commandArg)) throw new Error('webOS command is not authorized by its owner.');
99
+ if (!this.isHealthy()) throw new Error('webOS is disconnected; command was not sent.');
100
+ let uri: string;
101
+ let payload: Record<string, unknown> = {};
102
+ if (commandArg === 'volume_set') { uri = 'audio/setVolume'; payload = { volume: webostvInteger(argumentArg, 'volume', 0, 100) }; }
103
+ else if (commandArg === 'volume_mute') {
104
+ if (typeof argumentArg !== 'boolean') throw new TypeError('webOS mute must be boolean.');
105
+ uri = 'audio/setMute'; payload = { mute: argumentArg };
106
+ } else if (commandArg === 'select_source') {
107
+ const inputId = webostvString(argumentArg, 'inputId');
108
+ if (!this.#snapshot?.inputs.some((inputArg) => inputArg.id === inputId)) throw new TypeError('webOS input must match an advertised input id.');
109
+ uri = 'tv/switchInput'; payload = { inputId };
110
+ } else {
111
+ if (!Object.hasOwn(commandEndpoints, commandArg) || argumentArg !== undefined) throw new TypeError('Unsupported webOS command or extra arguments.');
112
+ uri = commandEndpoints[commandArg];
113
+ }
114
+ await this.#request('request', uri, payload, undefined, signalArg);
115
+ // Correlated acceptance is not proof of physical completion. Read state independently.
116
+ try {
117
+ const observed: Partial<IWebostvSnapshot> = {};
118
+ for (const [field, endpoint] of Object.entries(endpoints)) {
119
+ this.#applyState(observed, field as keyof typeof endpoints, await this.#request('request', endpoint, {}, undefined, signalArg));
120
+ }
121
+ if (!this.isHealthy() || !this.#snapshot) throw new Error('webOS observation is unavailable.');
122
+ const observation = { ...this.#snapshot, ...observed, observedAt: new Date().toISOString() };
123
+ return { acknowledgement: 'tv-response', physicalCompletion: 'unverified', observationStatus: 'observed', observation: structuredClone(observation) };
124
+ } catch {
125
+ return { acknowledgement: 'tv-response', physicalCompletion: 'unverified', observationStatus: 'unavailable' };
126
+ }
127
+ });
93
128
  }
94
129
 
95
- public async subscribe(handlerArg: (eventArg: TWebostvClientEvent) => void, signalArg?: AbortSignal): Promise<() => Promise<void>> {
130
+ public async subscribe(handlerArg: TWebostvClientHandler, signalArg?: AbortSignal): Promise<() => Promise<void>> {
96
131
  await this.getSnapshot(signalArg);
97
132
  if (signalArg?.aborted || this.#lifetime.signal.aborted) throw new Error('webOS subscription was aborted.');
98
133
  if (this.#handlers.size >= 128) throw new Error('webOS subscriber capacity reached.');
99
- const subscriber = (eventArg: TWebostvClientEvent): void => handlerArg(eventArg);
134
+ if (typeof handlerArg !== 'function') throw new TypeError('webOS subscriber must be a function.');
135
+ const subscriber: TWebostvClientHandler = (eventArg) => handlerArg(eventArg);
100
136
  this.#handlers.add(subscriber);
101
137
  const remove = (): void => { this.#handlers.delete(subscriber); this.#handlerCleanups.delete(subscriber); signalArg?.removeEventListener('abort', remove); };
102
138
  this.#handlerCleanups.set(subscriber, remove);
103
139
  signalArg?.addEventListener('abort', remove, { once: true });
104
- return async () => remove();
140
+ return async () => {
141
+ remove();
142
+ await Promise.allSettled([...(this.#handlerOperations.get(subscriber) ?? [])]);
143
+ };
105
144
  }
106
145
 
107
146
  public destroy(): Promise<void> {
108
147
  if (this.#destroy) return this.#destroy;
109
- this.#destroy = this.#close();
148
+ this.#destroy = Promise.resolve().then(() => this.#close());
149
+ this.#ready = false;
150
+ this.#lifetime.abort(new Error('webOS client was closed.'));
110
151
  return this.#destroy;
111
152
  }
112
153
 
154
+ public retryCleanup(): Promise<void> {
155
+ if (!this.#destroy) return Promise.reject(new Error('webOS cleanup retry requires terminal close.'));
156
+ if (!this.#retry) {
157
+ const retry = Promise.resolve().then(async () => {
158
+ await this.#destroy!.catch(() => undefined);
159
+ if (this.#ownsStore) await this.#store.retryCleanup();
160
+ });
161
+ this.#retry = retry;
162
+ const settled = (): void => { this.#retry = undefined; };
163
+ void retry.then(settled, settled);
164
+ }
165
+ return this.#retry;
166
+ }
167
+
113
168
  async #close(): Promise<void> {
114
- this.#ready = false;
115
- this.#lifetime.abort(new Error('webOS client was closed.'));
116
169
  this.#removeExternalAbort();
117
170
  const closeStore = this.#ownsStore ? this.#store.close() : Promise.resolve();
171
+ // Install a rejection handler immediately; socket shutdown and callback drainage run concurrently.
172
+ const storeResult = Promise.allSettled([closeStore]);
118
173
  clearInterval(this.#heartbeat);
119
174
  for (const request of this.#pending.values()) { clearTimeout(request.timer); request.cleanup(); request.reject(new Error('webOS connection closed; effects already sent have an indeterminate outcome.')); }
120
175
  this.#pending.clear(); this.#subscriptions.clear();
@@ -122,23 +177,21 @@ export class WebostvClient {
122
177
  for (const cleanup of [...this.#handlerCleanups.values()]) cleanup();
123
178
  this.#handlers.clear();
124
179
  const socket = this.#socket;
125
- if (!socket || socket.readyState === plugins.ws.WebSocket.CLOSED) { await Promise.all([closeStore, this.#storage?.catch(() => undefined)]); return; }
126
- await new Promise<void>((resolveArg) => {
180
+ if (socket && socket.readyState !== plugins.ws.WebSocket.CLOSED) await new Promise<void>((resolveArg) => {
127
181
  const timer = setTimeout(() => { socket.terminate(); }, 1000);
128
182
  socket.once('close', () => { clearTimeout(timer); socket.removeAllListeners(); resolveArg(); });
129
183
  if (socket.readyState === plugins.ws.WebSocket.CONNECTING) socket.terminate();
130
184
  else socket.close(1000);
131
185
  });
132
- await Promise.all([closeStore, this.#storage?.catch(() => undefined)]);
186
+ await Promise.allSettled([...this.#operations, ...[...this.#handlerOperations.values()].flatMap((operationsArg) => [...operationsArg])]);
187
+ const [result] = await storeResult;
188
+ if (result.status === 'rejected') throw new Error('webOS encrypted pairing cleanup failed; explicit retryCleanup is required.');
133
189
  }
134
190
 
135
191
  async #persist<TResult>(operationArg: () => Promise<TResult>): Promise<TResult> {
136
192
  if (this.#lifetime.signal.aborted) throw new Error('webOS persistence was aborted.');
137
- const operation = operationArg();
138
- this.#storage = operation;
139
- try { return await operation; }
193
+ try { return await operationArg(); }
140
194
  catch { throw new Error(this.#lifetime.signal.aborted ? 'webOS persistence was aborted.' : 'webOS encrypted pairing storage failed.'); }
141
- finally { if (this.#storage === operation) this.#storage = undefined; }
142
195
  }
143
196
 
144
197
  async #initialize(): Promise<IWebostvSnapshot> {
@@ -157,7 +210,8 @@ export class WebostvClient {
157
210
  const hello = await this.#request('hello', undefined, { appId: 'exchange.smarthome', appName: 'smarthome.exchange' });
158
211
  if (hello.deviceUUID !== this.#config.expectedDeviceUUID) throw new Error('webOS hello device UUID does not match the verified TV.');
159
212
  // New webOS requires this request before registration; permission denial fails setup explicitly.
160
- await this.#request('request', 'system/getSystemInfo', {});
213
+ const systemInfo = await this.#request('request', 'system/getSystemInfo', {});
214
+ const model = systemInfo.modelName === undefined ? undefined : webostvString(systemInfo.modelName, 'reported model');
161
215
  const registered = await this.#request('register', undefined, {
162
216
  pairingType: 'PROMPT', ...(clientKey ? { 'client-key': clientKey } : {}),
163
217
  manifest: { manifestVersion: 1, appVersion: '1.0', permissions },
@@ -178,7 +232,7 @@ export class WebostvClient {
178
232
  await this.#request('subscribe', uri, {}, (payloadArg) => update(field as keyof typeof endpoints, payloadArg));
179
233
  }
180
234
  if (initial.powerState === undefined || initial.volume === undefined || initial.muted === undefined || initial.currentAppId === undefined || initial.inputs === undefined) throw new Error('webOS did not supply a complete initial state.');
181
- this.#snapshot = { homeId: this.#config.homeId, deviceUUID: this.#config.expectedDeviceUUID, name: this.#config.name!, connected: true, powerState: initial.powerState, volume: initial.volume, muted: initial.muted, currentAppId: initial.currentAppId, inputs: initial.inputs, observedAt: new Date().toISOString() };
235
+ this.#snapshot = { homeId: this.#context.homeId, deviceUUID: this.#config.expectedDeviceUUID, name: this.#config.name!, model, authorizedCommands: this.#allowedCommands, connected: true, powerState: initial.powerState, volume: initial.volume, muted: initial.muted, currentAppId: initial.currentAppId, inputs: initial.inputs, observedAt: new Date().toISOString() };
182
236
  if (this.#lifetime.signal.aborted || this.#socket?.readyState !== plugins.ws.WebSocket.OPEN) throw new Error('webOS connection closed during setup.');
183
237
  this.#ready = true;
184
238
  this.#heartbeat = setInterval(() => {
@@ -288,13 +342,32 @@ export class WebostvClient {
288
342
  request.reject(request.type === 'request' ? new Error(`${errorArg.message} Effects already sent have an indeterminate outcome.`) : errorArg);
289
343
  }
290
344
  this.#pending.clear();
291
- void this.destroy();
345
+ void this.destroy().catch(() => undefined);
292
346
  }
293
347
 
294
348
  #emit(eventArg: TWebostvClientEvent): void {
295
349
  for (const handler of [...this.#handlers]) {
296
- try { handler(eventArg.type === 'state' ? { ...eventArg, snapshot: structuredClone(eventArg.snapshot) } : eventArg); }
297
- catch { this.#handlerCleanups.get(handler)?.(); }
350
+ const operations = this.#handlerOperations.get(handler) ?? new Set<Promise<void>>();
351
+ this.#handlerOperations.set(handler, operations);
352
+ const event = eventArg.type === 'state' ? { ...eventArg, snapshot: structuredClone(eventArg.snapshot) } : eventArg;
353
+ // Publish callback ownership before invoking user code, including close reentry.
354
+ const operation = Promise.resolve().then(() => {
355
+ const result = handler(event);
356
+ // A terminal callback may return the very close it triggered; never make close await itself.
357
+ return result === this.#destroy ? undefined : result;
358
+ }).catch(() => { this.#handlerCleanups.get(handler)?.(); });
359
+ operations.add(operation);
360
+ void operation.then(() => {
361
+ operations.delete(operation);
362
+ if (!operations.size) this.#handlerOperations.delete(handler);
363
+ });
298
364
  }
299
365
  }
366
+
367
+ #track<TResult>(operationArg: () => Promise<TResult>): Promise<TResult> {
368
+ const operation = Promise.resolve().then(operationArg);
369
+ this.#operations.add(operation);
370
+ void operation.then(() => this.#operations.delete(operation), () => this.#operations.delete(operation));
371
+ return operation;
372
+ }
300
373
  }
@@ -1,22 +1,42 @@
1
- import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
1
+ import { NativeIntegrationOwner, type INativeIntegrationOwnerOptions, type NativeIntegrationScope } from '../../core/classes.nativeintegrationowner.js';
2
+ import type { IConfigFlow, IConfigFlowStep, IDiscoveryCandidate, INativeConfigFlowContext } from '../../core/types.js';
2
3
  import { createWebostvDiscoveryDescriptor } from './webostv.discovery.js';
3
4
  import type { IWebostvConfig } from './webostv.types.js';
4
5
  import { normalizeWebostvConfig, webostvKeys, webostvObject } from './webostv.validation.js';
5
6
 
6
7
  export class WebostvConfigFlow implements IConfigFlow<IWebostvConfig> {
7
- public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IWebostvConfig>> {
8
- const result = await createWebostvDiscoveryDescriptor().getValidators()[0].validate(candidateArg, contextArg);
9
- if (!result.matched || !result.candidate) return { kind: 'error', error: result.reason };
10
- const candidate = result.candidate;
11
- return { kind: 'form', title: 'Pair verified LG webOS TV', description: 'The host must supply an encrypted database and external key provider. Prompt mode requires confirmation on the TV.',
12
- fields: [{ name: 'homeId', label: 'Home id', type: 'text', required: true }, { name: 'pairingMode', label: 'Pairing mode', type: 'select', required: true, options: [{ label: 'Use durable existing pairing', value: 'existing' }, { label: 'Confirm new pairing on TV', value: 'prompt' }] }, { name: 'allowUnencryptedConnection', label: 'Allow explicit cleartext local endpoint', type: 'boolean' }],
13
- submit: async (valuesArg) => {
14
- try {
15
- const values = webostvObject(valuesArg, 'webOS config flow values'); webostvKeys(values, ['homeId', 'pairingMode', 'allowUnencryptedConnection', 'certificateAuthority']);
16
- const config = normalizeWebostvConfig({ ...values, endpoint: candidate.metadata!.endpoint, expectedDeviceUUID: candidate.metadata!.expectedDeviceUUID, name: candidate.name } as IWebostvConfig);
17
- return { kind: 'done', config };
18
- } catch (errorArg) { return { kind: 'error', error: errorArg instanceof Error ? errorArg.message : 'Invalid webOS config.' }; }
19
- },
20
- };
8
+ readonly #owner: NativeIntegrationOwner;
9
+ readonly #scope: NativeIntegrationScope;
10
+ readonly #ownsOwner: boolean;
11
+
12
+ constructor(optionsArg: INativeIntegrationOwnerOptions = {}, ownerArg?: NativeIntegrationOwner) {
13
+ this.#owner = ownerArg ?? new NativeIntegrationOwner('webostv', optionsArg);
14
+ this.#ownsOwner = ownerArg === undefined;
15
+ this.#scope = this.#owner.createFlowScope();
16
+ }
17
+
18
+ public start(candidateArg: IDiscoveryCandidate, contextArg: INativeConfigFlowContext): Promise<IConfigFlowStep<IWebostvConfig>> {
19
+ const context = this.#scope.admit(contextArg);
20
+ return this.#scope.run(context, undefined, async () => {
21
+ const result = await createWebostvDiscoveryDescriptor().getValidators()[0].validate(candidateArg, context);
22
+ if (!result.matched || !result.candidate) return { kind: 'error', error: result.reason };
23
+ const candidate = result.candidate;
24
+ return { kind: 'form', title: 'Pair verified LG webOS TV', description: 'Pairing uses the selected home. Prompt mode requires confirmation on the TV.',
25
+ fields: [{ name: 'pairingMode', label: 'Pairing mode', type: 'select', required: true, options: [{ label: 'Use durable existing pairing', value: 'existing' }, { label: 'Confirm new pairing on TV', value: 'prompt' }] }, { name: 'allowUnencryptedConnection', label: 'Allow explicit cleartext local endpoint', type: 'boolean' }],
26
+ submit: async (valuesArg) => {
27
+ try {
28
+ return await this.#scope.run(context, context.homeId, async () => {
29
+ const values = webostvObject(valuesArg, 'webOS config flow values');
30
+ webostvKeys(values, ['pairingMode', 'allowUnencryptedConnection', 'certificateAuthority']);
31
+ const config = normalizeWebostvConfig({ ...values, homeId: context.homeId, endpoint: candidate.metadata!.endpoint, expectedDeviceUUID: candidate.metadata!.expectedDeviceUUID, name: candidate.name } as IWebostvConfig);
32
+ return { kind: 'done', config };
33
+ });
34
+ } catch (errorArg) { return { kind: 'error', error: errorArg instanceof Error ? errorArg.message : 'Invalid webOS config.' }; }
35
+ },
36
+ };
37
+ });
21
38
  }
39
+
40
+ public close(): Promise<void> { return this.#ownsOwner ? this.#owner.close() : this.#scope.close(); }
41
+ public retryCleanup(): Promise<void> { return this.#ownsOwner ? this.#owner.retryCleanup() : this.#scope.retryCleanup(); }
22
42
  }