@smarthome.exchange/integrations 1.0.0 → 2.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 (294) hide show
  1. package/changelog.md +49 -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 +79 -0
  8. package/dist_ts/core/classes.nativeintegrationowner.js +269 -0
  9. package/dist_ts/core/index.d.ts +2 -0
  10. package/dist_ts/core/index.js +3 -1
  11. package/dist_ts/core/types.d.ts +14 -0
  12. package/dist_ts/integrations/braviatv/braviatv.classes.client.d.ts +23 -72
  13. package/dist_ts/integrations/braviatv/braviatv.classes.client.js +265 -625
  14. package/dist_ts/integrations/braviatv/braviatv.classes.configflow.d.ts +7 -7
  15. package/dist_ts/integrations/braviatv/braviatv.classes.configflow.js +51 -68
  16. package/dist_ts/integrations/braviatv/braviatv.classes.integration.d.ts +8 -11
  17. package/dist_ts/integrations/braviatv/braviatv.classes.integration.js +186 -135
  18. package/dist_ts/integrations/braviatv/braviatv.discovery.js +12 -11
  19. package/dist_ts/integrations/braviatv/braviatv.mapper.d.ts +6 -14
  20. package/dist_ts/integrations/braviatv/braviatv.mapper.js +37 -131
  21. package/dist_ts/integrations/braviatv/braviatv.types.d.ts +55 -133
  22. package/dist_ts/integrations/braviatv/braviatv.validation.d.ts +7 -0
  23. package/dist_ts/integrations/braviatv/braviatv.validation.js +72 -0
  24. package/dist_ts/integrations/broadlink/broadlink.actions.d.ts +2 -0
  25. package/dist_ts/integrations/broadlink/broadlink.actions.js +36 -0
  26. package/dist_ts/integrations/broadlink/broadlink.classes.client.d.ts +21 -19
  27. package/dist_ts/integrations/broadlink/broadlink.classes.client.js +185 -124
  28. package/dist_ts/integrations/broadlink/broadlink.classes.configflow.d.ts +5 -4
  29. package/dist_ts/integrations/broadlink/broadlink.classes.configflow.js +64 -83
  30. package/dist_ts/integrations/broadlink/broadlink.classes.integration.d.ts +16 -19
  31. package/dist_ts/integrations/broadlink/broadlink.classes.integration.js +114 -59
  32. package/dist_ts/integrations/broadlink/broadlink.mapper.d.ts +3 -2
  33. package/dist_ts/integrations/broadlink/broadlink.mapper.js +2 -1
  34. package/dist_ts/integrations/broadlink/broadlink.selected.mapper.d.ts +10 -0
  35. package/dist_ts/integrations/broadlink/broadlink.selected.mapper.js +41 -0
  36. package/dist_ts/integrations/broadlink/broadlink.types.d.ts +43 -7
  37. package/dist_ts/integrations/broadlink/broadlink.validation.d.ts +7 -0
  38. package/dist_ts/integrations/broadlink/broadlink.validation.js +63 -0
  39. package/dist_ts/integrations/daikin/daikin.classes.client.d.ts +10 -52
  40. package/dist_ts/integrations/daikin/daikin.classes.client.js +55 -614
  41. package/dist_ts/integrations/daikin/daikin.classes.configflow.d.ts +8 -12
  42. package/dist_ts/integrations/daikin/daikin.classes.configflow.js +77 -138
  43. package/dist_ts/integrations/daikin/daikin.classes.integration.d.ts +7 -24
  44. package/dist_ts/integrations/daikin/daikin.classes.integration.js +175 -67
  45. package/dist_ts/integrations/daikin/daikin.discovery.d.ts +11 -24
  46. package/dist_ts/integrations/daikin/daikin.discovery.js +98 -305
  47. package/dist_ts/integrations/daikin/daikin.mapper.d.ts +6 -7
  48. package/dist_ts/integrations/daikin/daikin.mapper.js +95 -155
  49. package/dist_ts/integrations/daikin/daikin.types.d.ts +24 -44
  50. package/dist_ts/integrations/daikin/daikin.types.js +1 -1
  51. package/dist_ts/integrations/daikin/daikin.validation.d.ts +5 -0
  52. package/dist_ts/integrations/daikin/daikin.validation.js +53 -0
  53. package/dist_ts/integrations/denonavr/denonavr.classes.client.d.ts +31 -40
  54. package/dist_ts/integrations/denonavr/denonavr.classes.client.js +149 -404
  55. package/dist_ts/integrations/denonavr/denonavr.classes.configflow.d.ts +7 -2
  56. package/dist_ts/integrations/denonavr/denonavr.classes.configflow.js +44 -36
  57. package/dist_ts/integrations/denonavr/denonavr.classes.integration.d.ts +6 -11
  58. package/dist_ts/integrations/denonavr/denonavr.classes.integration.js +192 -113
  59. package/dist_ts/integrations/denonavr/denonavr.discovery.d.ts +10 -18
  60. package/dist_ts/integrations/denonavr/denonavr.discovery.js +71 -193
  61. package/dist_ts/integrations/denonavr/denonavr.mapper.d.ts +5 -13
  62. package/dist_ts/integrations/denonavr/denonavr.mapper.js +35 -159
  63. package/dist_ts/integrations/denonavr/denonavr.types.d.ts +55 -90
  64. package/dist_ts/integrations/denonavr/denonavr.validation.d.ts +10 -0
  65. package/dist_ts/integrations/denonavr/denonavr.validation.js +86 -0
  66. package/dist_ts/integrations/kodi/kodi.classes.client.d.ts +11 -45
  67. package/dist_ts/integrations/kodi/kodi.classes.client.js +84 -310
  68. package/dist_ts/integrations/kodi/kodi.classes.configflow.d.ts +9 -8
  69. package/dist_ts/integrations/kodi/kodi.classes.configflow.js +53 -48
  70. package/dist_ts/integrations/kodi/kodi.classes.integration.d.ts +11 -11
  71. package/dist_ts/integrations/kodi/kodi.classes.integration.js +114 -171
  72. package/dist_ts/integrations/kodi/kodi.discovery.js +53 -29
  73. package/dist_ts/integrations/kodi/kodi.mapper.d.ts +7 -2
  74. package/dist_ts/integrations/kodi/kodi.mapper.js +48 -10
  75. package/dist_ts/integrations/kodi/kodi.types.d.ts +32 -28
  76. package/dist_ts/integrations/kodi/kodi.validation.d.ts +9 -0
  77. package/dist_ts/integrations/kodi/kodi.validation.js +62 -0
  78. package/dist_ts/integrations/modbus/modbus.classes.client.d.ts +15 -61
  79. package/dist_ts/integrations/modbus/modbus.classes.client.js +117 -776
  80. package/dist_ts/integrations/modbus/modbus.classes.configflow.d.ts +9 -5
  81. package/dist_ts/integrations/modbus/modbus.classes.configflow.js +70 -38
  82. package/dist_ts/integrations/modbus/modbus.classes.integration.d.ts +6 -17
  83. package/dist_ts/integrations/modbus/modbus.classes.integration.js +133 -57
  84. package/dist_ts/integrations/modbus/modbus.discovery.d.ts +5 -8
  85. package/dist_ts/integrations/modbus/modbus.discovery.js +38 -93
  86. package/dist_ts/integrations/modbus/modbus.mapper.d.ts +8 -32
  87. package/dist_ts/integrations/modbus/modbus.mapper.js +71 -431
  88. package/dist_ts/integrations/modbus/modbus.types.d.ts +82 -332
  89. package/dist_ts/integrations/modbus/modbus.validation.d.ts +10 -0
  90. package/dist_ts/integrations/modbus/modbus.validation.js +128 -0
  91. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.client.d.ts +12 -34
  92. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.client.js +143 -231
  93. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.configflow.d.ts +6 -1
  94. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.configflow.js +105 -18
  95. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.integration.d.ts +8 -8
  96. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.integration.js +141 -155
  97. package/dist_ts/integrations/nanoleaf/nanoleaf.credentials.d.ts +9 -0
  98. package/dist_ts/integrations/nanoleaf/nanoleaf.credentials.js +35 -0
  99. package/dist_ts/integrations/nanoleaf/nanoleaf.mapper.d.ts +6 -18
  100. package/dist_ts/integrations/nanoleaf/nanoleaf.mapper.js +45 -220
  101. package/dist_ts/integrations/nanoleaf/nanoleaf.types.d.ts +33 -147
  102. package/dist_ts/integrations/nanoleaf/nanoleaf.validation.d.ts +6 -0
  103. package/dist_ts/integrations/nanoleaf/nanoleaf.validation.js +82 -0
  104. package/dist_ts/integrations/onkyo/onkyo.classes.client.d.ts +15 -20
  105. package/dist_ts/integrations/onkyo/onkyo.classes.client.js +161 -404
  106. package/dist_ts/integrations/onkyo/onkyo.classes.configflow.d.ts +9 -4
  107. package/dist_ts/integrations/onkyo/onkyo.classes.configflow.js +51 -6
  108. package/dist_ts/integrations/onkyo/onkyo.classes.integration.d.ts +22 -6
  109. package/dist_ts/integrations/onkyo/onkyo.classes.integration.js +144 -11
  110. package/dist_ts/integrations/onkyo/onkyo.discovery.d.ts +6 -1
  111. package/dist_ts/integrations/onkyo/onkyo.discovery.js +40 -4
  112. package/dist_ts/integrations/onkyo/onkyo.mapper.d.ts +7 -8
  113. package/dist_ts/integrations/onkyo/onkyo.mapper.js +45 -12
  114. package/dist_ts/integrations/onkyo/onkyo.types.d.ts +58 -8
  115. package/dist_ts/integrations/onkyo/onkyo.types.js +13 -102
  116. package/dist_ts/integrations/onkyo/onkyo.validation.d.ts +16 -0
  117. package/dist_ts/integrations/onkyo/onkyo.validation.js +77 -0
  118. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.client.d.ts +25 -25
  119. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.client.js +116 -360
  120. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.configflow.d.ts +11 -3
  121. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.configflow.js +76 -6
  122. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.integration.d.ts +26 -4
  123. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.integration.js +142 -9
  124. package/dist_ts/integrations/panasonic_viera/panasonic_viera.discovery.d.ts +2 -1
  125. package/dist_ts/integrations/panasonic_viera/panasonic_viera.discovery.js +31 -4
  126. package/dist_ts/integrations/panasonic_viera/panasonic_viera.mapper.d.ts +9 -9
  127. package/dist_ts/integrations/panasonic_viera/panasonic_viera.mapper.js +31 -149
  128. package/dist_ts/integrations/panasonic_viera/panasonic_viera.types.d.ts +51 -11
  129. package/dist_ts/integrations/panasonic_viera/panasonic_viera.types.js +22 -87
  130. package/dist_ts/integrations/panasonic_viera/panasonic_viera.validation.d.ts +6 -0
  131. package/dist_ts/integrations/panasonic_viera/panasonic_viera.validation.js +56 -0
  132. package/dist_ts/integrations/pioneer/pioneer.classes.client.d.ts +31 -16
  133. package/dist_ts/integrations/pioneer/pioneer.classes.client.js +167 -409
  134. package/dist_ts/integrations/pioneer/pioneer.classes.configflow.d.ts +10 -3
  135. package/dist_ts/integrations/pioneer/pioneer.classes.configflow.js +66 -6
  136. package/dist_ts/integrations/pioneer/pioneer.classes.integration.d.ts +13 -5
  137. package/dist_ts/integrations/pioneer/pioneer.classes.integration.js +239 -11
  138. package/dist_ts/integrations/pioneer/pioneer.discovery.d.ts +14 -1
  139. package/dist_ts/integrations/pioneer/pioneer.discovery.js +51 -4
  140. package/dist_ts/integrations/pioneer/pioneer.mapper.d.ts +14 -8
  141. package/dist_ts/integrations/pioneer/pioneer.mapper.js +54 -12
  142. package/dist_ts/integrations/pioneer/pioneer.types.d.ts +79 -12
  143. package/dist_ts/integrations/pioneer/pioneer.types.js +12 -76
  144. package/dist_ts/integrations/pioneer/pioneer.validation.d.ts +10 -0
  145. package/dist_ts/integrations/pioneer/pioneer.validation.js +85 -0
  146. package/dist_ts/integrations/soundtouch/soundtouch.classes.client.d.ts +13 -36
  147. package/dist_ts/integrations/soundtouch/soundtouch.classes.client.js +179 -502
  148. package/dist_ts/integrations/soundtouch/soundtouch.classes.configflow.d.ts +6 -1
  149. package/dist_ts/integrations/soundtouch/soundtouch.classes.configflow.js +50 -40
  150. package/dist_ts/integrations/soundtouch/soundtouch.classes.integration.d.ts +14 -15
  151. package/dist_ts/integrations/soundtouch/soundtouch.classes.integration.js +108 -128
  152. package/dist_ts/integrations/soundtouch/soundtouch.discovery.d.ts +2 -20
  153. package/dist_ts/integrations/soundtouch/soundtouch.discovery.js +23 -113
  154. package/dist_ts/integrations/soundtouch/soundtouch.mapper.d.ts +3 -12
  155. package/dist_ts/integrations/soundtouch/soundtouch.mapper.js +35 -189
  156. package/dist_ts/integrations/soundtouch/soundtouch.types.d.ts +57 -142
  157. package/dist_ts/integrations/soundtouch/soundtouch.types.js +18 -3
  158. package/dist_ts/integrations/soundtouch/soundtouch.validation.d.ts +22 -0
  159. package/dist_ts/integrations/soundtouch/soundtouch.validation.js +65 -0
  160. package/dist_ts/integrations/wiz/index.d.ts +1 -0
  161. package/dist_ts/integrations/wiz/index.js +2 -1
  162. package/dist_ts/integrations/wiz/wiz.classes.client.d.ts +14 -28
  163. package/dist_ts/integrations/wiz/wiz.classes.client.js +93 -229
  164. package/dist_ts/integrations/wiz/wiz.classes.configflow.d.ts +6 -4
  165. package/dist_ts/integrations/wiz/wiz.classes.configflow.js +38 -67
  166. package/dist_ts/integrations/wiz/wiz.classes.integration.d.ts +10 -21
  167. package/dist_ts/integrations/wiz/wiz.classes.integration.js +204 -44
  168. package/dist_ts/integrations/wiz/wiz.discovery.d.ts +10 -31
  169. package/dist_ts/integrations/wiz/wiz.discovery.js +48 -249
  170. package/dist_ts/integrations/wiz/wiz.mapper.d.ts +7 -51
  171. package/dist_ts/integrations/wiz/wiz.mapper.js +82 -668
  172. package/dist_ts/integrations/wiz/wiz.types.d.ts +34 -208
  173. package/dist_ts/integrations/wiz/wiz.validation.d.ts +8 -0
  174. package/dist_ts/integrations/wiz/wiz.validation.js +72 -0
  175. package/dist_ts/integrations/yamaha/index.d.ts +1 -0
  176. package/dist_ts/integrations/yamaha/index.js +2 -1
  177. package/dist_ts/integrations/yamaha/yamaha.classes.client.d.ts +30 -4
  178. package/dist_ts/integrations/yamaha/yamaha.classes.client.js +162 -6
  179. package/dist_ts/integrations/yamaha/yamaha.classes.configflow.d.ts +10 -3
  180. package/dist_ts/integrations/yamaha/yamaha.classes.configflow.js +106 -6
  181. package/dist_ts/integrations/yamaha/yamaha.classes.integration.d.ts +22 -4
  182. package/dist_ts/integrations/yamaha/yamaha.classes.integration.js +243 -8
  183. package/dist_ts/integrations/yamaha/yamaha.discovery.d.ts +13 -1
  184. package/dist_ts/integrations/yamaha/yamaha.discovery.js +57 -4
  185. package/dist_ts/integrations/yamaha/yamaha.mapper.d.ts +16 -8
  186. package/dist_ts/integrations/yamaha/yamaha.mapper.js +58 -12
  187. package/dist_ts/integrations/yamaha/yamaha.types.d.ts +73 -6
  188. package/dist_ts/integrations/yamaha/yamaha.types.js +8 -94
  189. package/dist_ts/integrations/yamaha/yamaha.validation.d.ts +12 -0
  190. package/dist_ts/integrations/yamaha/yamaha.validation.js +89 -0
  191. package/package.json +3 -3
  192. package/readme.md +514 -2
  193. package/ts/00_commitinfo_data.ts +1 -1
  194. package/ts/core/classes.configstore.ts +84 -27
  195. package/ts/core/classes.nativehomeadmission.ts +96 -0
  196. package/ts/core/classes.nativeintegrationowner.ts +297 -0
  197. package/ts/core/index.ts +2 -0
  198. package/ts/core/types.ts +16 -0
  199. package/ts/integrations/braviatv/braviatv.classes.client.ts +245 -709
  200. package/ts/integrations/braviatv/braviatv.classes.configflow.ts +47 -76
  201. package/ts/integrations/braviatv/braviatv.classes.integration.ts +167 -147
  202. package/ts/integrations/braviatv/braviatv.discovery.ts +11 -10
  203. package/ts/integrations/braviatv/braviatv.mapper.ts +39 -142
  204. package/ts/integrations/braviatv/braviatv.types.ts +53 -173
  205. package/ts/integrations/braviatv/braviatv.validation.ts +68 -0
  206. package/ts/integrations/broadlink/broadlink.actions.ts +31 -0
  207. package/ts/integrations/broadlink/broadlink.classes.client.ts +170 -144
  208. package/ts/integrations/broadlink/broadlink.classes.configflow.ts +59 -89
  209. package/ts/integrations/broadlink/broadlink.classes.integration.ts +110 -64
  210. package/ts/integrations/broadlink/broadlink.mapper.ts +5 -4
  211. package/ts/integrations/broadlink/broadlink.selected.mapper.ts +46 -0
  212. package/ts/integrations/broadlink/broadlink.types.ts +48 -8
  213. package/ts/integrations/broadlink/broadlink.validation.ts +66 -0
  214. package/ts/integrations/daikin/daikin.classes.client.ts +52 -661
  215. package/ts/integrations/daikin/daikin.classes.configflow.ts +72 -152
  216. package/ts/integrations/daikin/daikin.classes.integration.ts +166 -71
  217. package/ts/integrations/daikin/daikin.discovery.ts +94 -346
  218. package/ts/integrations/daikin/daikin.mapper.ts +83 -166
  219. package/ts/integrations/daikin/daikin.types.ts +27 -48
  220. package/ts/integrations/daikin/daikin.validation.ts +51 -0
  221. package/ts/integrations/daikin/readme.md +83 -0
  222. package/ts/integrations/denonavr/denonavr.classes.client.ts +132 -421
  223. package/ts/integrations/denonavr/denonavr.classes.configflow.ts +44 -37
  224. package/ts/integrations/denonavr/denonavr.classes.integration.ts +171 -117
  225. package/ts/integrations/denonavr/denonavr.discovery.ts +67 -220
  226. package/ts/integrations/denonavr/denonavr.mapper.ts +36 -182
  227. package/ts/integrations/denonavr/denonavr.types.ts +48 -111
  228. package/ts/integrations/denonavr/denonavr.validation.ts +87 -0
  229. package/ts/integrations/kodi/kodi.classes.client.ts +72 -352
  230. package/ts/integrations/kodi/kodi.classes.configflow.ts +52 -55
  231. package/ts/integrations/kodi/kodi.classes.integration.ts +97 -178
  232. package/ts/integrations/kodi/kodi.discovery.ts +49 -28
  233. package/ts/integrations/kodi/kodi.mapper.ts +52 -10
  234. package/ts/integrations/kodi/kodi.types.ts +33 -58
  235. package/ts/integrations/kodi/kodi.validation.ts +60 -0
  236. package/ts/integrations/modbus/modbus.classes.client.ts +100 -842
  237. package/ts/integrations/modbus/modbus.classes.configflow.ts +70 -41
  238. package/ts/integrations/modbus/modbus.classes.integration.ts +115 -60
  239. package/ts/integrations/modbus/modbus.discovery.ts +38 -106
  240. package/ts/integrations/modbus/modbus.mapper.ts +64 -467
  241. package/ts/integrations/modbus/modbus.types.ts +64 -381
  242. package/ts/integrations/modbus/modbus.validation.ts +119 -0
  243. package/ts/integrations/nanoleaf/nanoleaf.classes.client.ts +115 -249
  244. package/ts/integrations/nanoleaf/nanoleaf.classes.configflow.ts +90 -19
  245. package/ts/integrations/nanoleaf/nanoleaf.classes.integration.ts +130 -181
  246. package/ts/integrations/nanoleaf/nanoleaf.credentials.ts +39 -0
  247. package/ts/integrations/nanoleaf/nanoleaf.mapper.ts +47 -260
  248. package/ts/integrations/nanoleaf/nanoleaf.types.ts +29 -147
  249. package/ts/integrations/nanoleaf/nanoleaf.validation.ts +77 -0
  250. package/ts/integrations/onkyo/onkyo.classes.client.ts +148 -457
  251. package/ts/integrations/onkyo/onkyo.classes.configflow.ts +52 -6
  252. package/ts/integrations/onkyo/onkyo.classes.integration.ts +121 -12
  253. package/ts/integrations/onkyo/onkyo.discovery.ts +41 -3
  254. package/ts/integrations/onkyo/onkyo.mapper.ts +46 -19
  255. package/ts/integrations/onkyo/onkyo.types.ts +55 -110
  256. package/ts/integrations/onkyo/onkyo.validation.ts +85 -0
  257. package/ts/integrations/onkyo/readme.md +126 -0
  258. package/ts/integrations/panasonic_viera/panasonic_viera.classes.client.ts +101 -398
  259. package/ts/integrations/panasonic_viera/panasonic_viera.classes.configflow.ts +69 -5
  260. package/ts/integrations/panasonic_viera/panasonic_viera.classes.integration.ts +141 -9
  261. package/ts/integrations/panasonic_viera/panasonic_viera.discovery.ts +32 -3
  262. package/ts/integrations/panasonic_viera/panasonic_viera.mapper.ts +34 -161
  263. package/ts/integrations/panasonic_viera/panasonic_viera.types.ts +52 -96
  264. package/ts/integrations/panasonic_viera/panasonic_viera.validation.ts +56 -0
  265. package/ts/integrations/pioneer/pioneer.classes.client.ts +132 -428
  266. package/ts/integrations/pioneer/pioneer.classes.configflow.ts +67 -5
  267. package/ts/integrations/pioneer/pioneer.classes.integration.ts +225 -11
  268. package/ts/integrations/pioneer/pioneer.discovery.ts +45 -3
  269. package/ts/integrations/pioneer/pioneer.mapper.ts +57 -13
  270. package/ts/integrations/pioneer/pioneer.types.ts +87 -85
  271. package/ts/integrations/pioneer/pioneer.validation.ts +84 -0
  272. package/ts/integrations/soundtouch/soundtouch.classes.client.ts +152 -571
  273. package/ts/integrations/soundtouch/soundtouch.classes.configflow.ts +47 -42
  274. package/ts/integrations/soundtouch/soundtouch.classes.integration.ts +95 -150
  275. package/ts/integrations/soundtouch/soundtouch.discovery.ts +21 -133
  276. package/ts/integrations/soundtouch/soundtouch.mapper.ts +36 -207
  277. package/ts/integrations/soundtouch/soundtouch.types.ts +50 -195
  278. package/ts/integrations/soundtouch/soundtouch.validation.ts +60 -0
  279. package/ts/integrations/wiz/index.ts +1 -0
  280. package/ts/integrations/wiz/wiz.classes.client.ts +84 -254
  281. package/ts/integrations/wiz/wiz.classes.configflow.ts +39 -70
  282. package/ts/integrations/wiz/wiz.classes.integration.ts +189 -45
  283. package/ts/integrations/wiz/wiz.discovery.ts +48 -291
  284. package/ts/integrations/wiz/wiz.mapper.ts +81 -754
  285. package/ts/integrations/wiz/wiz.types.ts +24 -224
  286. package/ts/integrations/wiz/wiz.validation.ts +73 -0
  287. package/ts/integrations/yamaha/index.ts +1 -0
  288. package/ts/integrations/yamaha/yamaha.classes.client.ts +150 -6
  289. package/ts/integrations/yamaha/yamaha.classes.configflow.ts +100 -5
  290. package/ts/integrations/yamaha/yamaha.classes.integration.ts +236 -8
  291. package/ts/integrations/yamaha/yamaha.discovery.ts +52 -3
  292. package/ts/integrations/yamaha/yamaha.mapper.ts +65 -13
  293. package/ts/integrations/yamaha/yamaha.types.ts +77 -97
  294. package/ts/integrations/yamaha/yamaha.validation.ts +86 -0
@@ -1,168 +1,88 @@
1
- import * as plugins from '../../plugins.js';
2
1
  import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
3
- import type { IDaikinConfig, IDaikinRawData, IDaikinSnapshot, IDaikinValueMap, TDaikinApiVariant } from './daikin.types.js';
4
- import { daikinDefaultHttpPort, daikinDefaultHttpsPort, daikinDefaultTimeoutMs, daikinSkyFiPort } from './daikin.types.js';
2
+ import { NativeIntegrationOwner, type NativeIntegrationScope } from '../../core/classes.nativeintegrationowner.js';
3
+ import { DaikinClient } from './daikin.classes.client.js';
4
+ import type { IDaikinIntegrationOptions, IDaikinNativeConfig } from './daikin.types.js';
5
+ import { daikinKeys, daikinObject, normalizeDaikinConfig } from './daikin.validation.js';
5
6
 
6
- export class DaikinConfigFlow implements IConfigFlow<IDaikinConfig> {
7
- public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IDaikinConfig>> {
8
- void contextArg;
7
+ export class DaikinConfigFlow implements IConfigFlow<IDaikinNativeConfig> {
8
+ readonly #scope: NativeIntegrationScope;
9
+
10
+ constructor(optionsArg: IDaikinIntegrationOptions = {}, scopeArg?: NativeIntegrationScope) {
11
+ this.#scope = scopeArg ?? new NativeIntegrationOwner('daikin', optionsArg).createFlowScope();
12
+ }
13
+
14
+ public destroy(): Promise<void> { return this.#scope.close(); }
15
+ public retryCleanup(): Promise<void> { return this.#scope.retryCleanup(); }
16
+
17
+ public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IDaikinNativeConfig>> {
18
+ const context = this.#scope.admit(contextArg);
19
+ const candidate = daikinObject(candidateArg, 'Daikin candidate');
20
+ const metadata = candidate.metadata === undefined ? {} : daikinObject(candidate.metadata, 'Daikin candidate metadata');
21
+ const host = typeof candidate.host === 'string' ? candidate.host : undefined;
22
+ const expectedMac = typeof candidate.macAddress === 'string' ? candidate.macAddress.replace(/:/g, '') : typeof metadata.expectedMac === 'string' ? metadata.expectedMac : undefined;
9
23
  return {
10
- kind: 'form',
11
- title: 'Configure Daikin AC',
12
- description: 'Configure a local Daikin endpoint, or use snapshot/manual data from the discovery candidate. Use either API key or SkyFi password, not both.',
24
+ kind: 'form', title: 'Connect selected Daikin BRP069',
25
+ description: 'Select a private IPv4 address and exact 12-digit adapter MAC for the host-selected home. Setup verifies basic, model and current control observations. BRP069 LAN HTTP is unauthenticated. No credentials are accepted.',
13
26
  fields: [
14
- { name: 'host', label: 'Host', type: 'text' },
15
- { name: 'port', label: 'Port', type: 'number' },
16
- { name: 'apiVariant', label: 'API variant', type: 'select', options: [
17
- { label: 'Auto / BRP069 local HTTP', value: 'auto' },
18
- { label: 'BRP069 local HTTP', value: 'brp069' },
19
- { label: 'BRP072C HTTPS with API key', value: 'brp072c' },
20
- { label: 'AirBase / skyfi path prefix', value: 'airbase' },
21
- { label: 'SkyFi password API', value: 'skyfi' },
22
- { label: 'BRP084 dsiot read-only', value: 'brp084' },
23
- ] },
24
- { name: 'ssl', label: 'Use HTTPS', type: 'boolean' },
25
- { name: 'verifySsl', label: 'Verify TLS certificate', type: 'boolean' },
26
- { name: 'apiKey', label: 'API key', type: 'password' },
27
- { name: 'uuid', label: 'Terminal UUID', type: 'text' },
28
- { name: 'password', label: 'SkyFi password', type: 'password' },
27
+ { name: 'host', label: 'Selected private IPv4', type: 'text', required: true },
28
+ { name: 'expectedMac', label: 'Exact adapter MAC (12 hex digits)', type: 'text', required: true },
29
+ { name: 'apiVariant', label: 'Controller family', type: 'select', required: true, options: [{ label: 'Selected BRP069', value: 'brp069' }] },
30
+ { name: 'allowPlaintext', label: 'Allow unauthenticated plaintext LAN HTTP', type: 'boolean', required: true },
31
+ { name: 'port', label: 'HTTP port (80)', type: 'number' },
29
32
  { name: 'name', label: 'Name', type: 'text' },
30
33
  ],
31
- submit: async (valuesArg) => this.submit(candidateArg, valuesArg),
34
+ submit: async (valuesArg) => {
35
+ let config: Readonly<IDaikinNativeConfig>;
36
+ try {
37
+ const values = daikinObject(valuesArg, 'Daikin configuration form');
38
+ daikinKeys(values, ['homeId', 'host', 'expectedMac', 'apiVariant', 'allowPlaintext', 'port', 'name']);
39
+ if (values.homeId !== undefined && values.homeId !== context.homeId) throw new TypeError('Daikin submitted home must match the independently selected host home.');
40
+ config = normalizeDaikinConfig({
41
+ homeId: context.homeId,
42
+ host: formText(values.host, host),
43
+ expectedMac: formText(values.expectedMac, expectedMac),
44
+ apiVariant: selectedVariant(values.apiVariant), allowPlaintext: selectedPlaintext(values.allowPlaintext), port: formPort(values.port, candidate.port),
45
+ name: values.name === undefined ? undefined : formText(values.name),
46
+ });
47
+ this.#scope.admit(context, config.homeId);
48
+ } catch (errorArg) {
49
+ return { kind: 'error', title: 'Daikin verification failed', error: errorArg instanceof Error ? errorArg.message : 'Daikin configuration is invalid.' };
50
+ }
51
+ return this.#scope.run(context, config.homeId, async (leaseArg): Promise<IConfigFlowStep<IDaikinNativeConfig>> => {
52
+ const client = new DaikinClient(config, leaseArg.context);
53
+ leaseArg.own(() => client.destroy());
54
+ try {
55
+ await client.getSnapshot(leaseArg.signal);
56
+ leaseArg.assertOpen();
57
+ return { kind: 'done', title: 'Verified selected Daikin BRP069', config };
58
+ } catch (errorArg) {
59
+ return { kind: 'error', title: 'Daikin verification failed', error: errorArg instanceof Error ? errorArg.message : 'Daikin verification failed.' };
60
+ }
61
+ });
62
+ },
32
63
  };
33
64
  }
34
-
35
- private async submit(candidateArg: IDiscoveryCandidate, valuesArg: Record<string, unknown>): Promise<IConfigFlowStep<IDaikinConfig>> {
36
- const metadata = candidateArg.metadata || {};
37
- const parsed = parseEndpoint(this.stringValue(valuesArg.host) || this.stringValue(metadata.url));
38
- const snapshot = snapshotValue(metadata.snapshot);
39
- const rawData = rawDataValue(metadata.rawData);
40
- const apiKey = this.stringValue(valuesArg.apiKey) || this.stringMetadata(metadata, 'apiKey');
41
- const password = this.stringValue(valuesArg.password) || this.stringMetadata(metadata, 'password');
42
- const apiVariant = this.apiVariant(this.stringValue(valuesArg.apiVariant) || this.stringMetadata(metadata, 'apiVariant'), apiKey, password);
43
- const host = parsed?.host || this.stringValue(valuesArg.host) || candidateArg.host || snapshot?.device.host;
44
- const ssl = this.booleanValue(valuesArg.ssl) ?? this.booleanMetadata(metadata, 'ssl') ?? parsed?.ssl ?? snapshot?.device.ssl ?? apiVariant === 'brp072c';
45
- const port = this.numberValue(valuesArg.port) || candidateArg.port || parsed?.port || snapshot?.device.port || this.defaultPort(apiVariant, ssl);
46
- const hasManualData = Boolean(snapshot || rawData || metadata.values || metadata.client);
47
-
48
- if (apiKey && password) {
49
- return { kind: 'error', title: 'Daikin setup failed', error: 'Invalid authentication: use either API key or password, not both.' };
50
- }
51
- if (!host && !hasManualData) {
52
- return { kind: 'error', title: 'Daikin setup failed', error: 'Daikin host, injected client, snapshot, or manual data is required.' };
53
- }
54
- if (!this.validPort(port)) {
55
- return { kind: 'error', title: 'Daikin setup failed', error: 'Daikin port must be between 1 and 65535.' };
56
- }
57
-
58
- const config: IDaikinConfig = {
59
- host,
60
- port,
61
- ssl,
62
- verifySsl: this.booleanValue(valuesArg.verifySsl) ?? this.booleanMetadata(metadata, 'verifySsl') ?? false,
63
- apiKey,
64
- uuid: this.stringValue(valuesArg.uuid) || this.stringMetadata(metadata, 'uuid') || (apiKey ? plugins.crypto.randomUUID().replace(/-/g, '') : undefined),
65
- password,
66
- apiVariant,
67
- timeoutMs: daikinDefaultTimeoutMs,
68
- name: this.stringValue(valuesArg.name) || candidateArg.name || snapshot?.device.name || this.stringMetadata(metadata, 'name'),
69
- manufacturer: candidateArg.manufacturer || snapshot?.device.manufacturer || 'Daikin',
70
- model: candidateArg.model || snapshot?.device.model,
71
- uniqueId: candidateArg.id || snapshot?.device.id || snapshot?.device.macAddress || (host ? `${host}:${port}` : undefined),
72
- macAddress: candidateArg.macAddress || snapshot?.device.macAddress,
73
- snapshot,
74
- rawData,
75
- values: valueMapValue(metadata.values),
76
- client: metadata.client as IDaikinConfig['client'],
77
- commandExecutor: metadata.commandExecutor as IDaikinConfig['commandExecutor'],
78
- };
79
-
80
- return { kind: 'done', title: 'Daikin configured', config };
81
- }
82
-
83
- private apiVariant(valueArg: string | undefined, apiKeyArg: string | undefined, passwordArg: string | undefined): TDaikinApiVariant {
84
- if (passwordArg) {
85
- return 'skyfi';
86
- }
87
- if (apiKeyArg) {
88
- return 'brp072c';
89
- }
90
- const allowed: TDaikinApiVariant[] = ['auto', 'brp069', 'brp072c', 'airbase', 'skyfi', 'brp084'];
91
- return allowed.includes(valueArg as TDaikinApiVariant) ? valueArg as TDaikinApiVariant : 'auto';
92
- }
93
-
94
- private defaultPort(apiVariantArg: TDaikinApiVariant, sslArg: boolean): number {
95
- if (apiVariantArg === 'skyfi') {
96
- return daikinSkyFiPort;
97
- }
98
- return sslArg ? daikinDefaultHttpsPort : daikinDefaultHttpPort;
99
- }
100
-
101
- private stringValue(valueArg: unknown): string | undefined {
102
- return typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim().replace(/\/$/, '') : undefined;
103
- }
104
-
105
- private numberValue(valueArg: unknown): number | undefined {
106
- if (typeof valueArg === 'number' && Number.isFinite(valueArg)) {
107
- return Math.round(valueArg);
108
- }
109
- if (typeof valueArg === 'string' && valueArg.trim()) {
110
- const parsed = Number(valueArg);
111
- return Number.isFinite(parsed) ? Math.round(parsed) : undefined;
112
- }
113
- return undefined;
114
- }
115
-
116
- private booleanValue(valueArg: unknown): boolean | undefined {
117
- if (typeof valueArg === 'boolean') {
118
- return valueArg;
119
- }
120
- if (typeof valueArg === 'string') {
121
- const normalized = valueArg.trim().toLowerCase();
122
- if (['true', 'yes', 'on', '1'].includes(normalized)) {
123
- return true;
124
- }
125
- if (['false', 'no', 'off', '0'].includes(normalized)) {
126
- return false;
127
- }
128
- }
129
- return undefined;
130
- }
131
-
132
- private stringMetadata(metadataArg: Record<string, unknown>, keyArg: string): string | undefined {
133
- return this.stringValue(metadataArg[keyArg]);
134
- }
135
-
136
- private booleanMetadata(metadataArg: Record<string, unknown>, keyArg: string): boolean | undefined {
137
- const value = metadataArg[keyArg];
138
- return typeof value === 'boolean' ? value : undefined;
139
- }
140
-
141
- private validPort(valueArg: number): boolean {
142
- return Number.isInteger(valueArg) && valueArg > 0 && valueArg <= 65535;
143
- }
144
65
  }
145
66
 
146
- const parseEndpoint = (valueArg: string | undefined): { host: string; port?: number; ssl: boolean } | undefined => {
147
- if (!valueArg || !/^[a-z][a-z0-9+.-]*:\/\//i.test(valueArg)) {
148
- return undefined;
149
- }
150
- try {
151
- const url = new URL(valueArg);
152
- return { host: url.hostname, port: url.port ? Number(url.port) : undefined, ssl: url.protocol === 'https:' };
153
- } catch {
154
- return undefined;
155
- }
67
+ const selectedVariant = (valueArg: unknown): 'brp069' => {
68
+ if (valueArg !== 'brp069') throw new TypeError('Select the BRP069 controller family explicitly.');
69
+ return valueArg;
156
70
  };
157
-
158
- const snapshotValue = (valueArg: unknown): IDaikinSnapshot | undefined => {
159
- return valueArg && typeof valueArg === 'object' && 'device' in valueArg && 'climate' in valueArg ? valueArg as IDaikinSnapshot : undefined;
71
+ const formPort = (valueArg: unknown, hintArg: unknown): number | undefined => {
72
+ const value = valueArg === undefined || valueArg === '' ? hintArg : valueArg;
73
+ if (value === undefined) return undefined;
74
+ if (typeof value === 'number') return value;
75
+ if (typeof value === 'string' && /^\d{1,5}$/.test(value)) return Number(value);
76
+ throw new TypeError('Daikin port must be an integer.');
160
77
  };
161
78
 
162
- const rawDataValue = (valueArg: unknown): Partial<IDaikinRawData> | undefined => {
163
- return valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) ? valueArg as Partial<IDaikinRawData> : undefined;
79
+ const selectedPlaintext = (valueArg: unknown): true => {
80
+ if (valueArg !== true) throw new TypeError('Explicitly allow unauthenticated plaintext LAN HTTP.');
81
+ return valueArg;
164
82
  };
165
83
 
166
- const valueMapValue = (valueArg: unknown): IDaikinValueMap | undefined => {
167
- return valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) ? valueArg as IDaikinValueMap : undefined;
84
+ const formText = (valueArg: unknown, hintArg?: string): string => {
85
+ if (valueArg === undefined) return hintArg ?? '';
86
+ if (typeof valueArg !== 'string') throw new TypeError('Daikin text fields must be strings.');
87
+ return valueArg;
168
88
  };
@@ -1,107 +1,202 @@
1
- import type * as shxInterfaces from '@smarthome.exchange/interfaces';
1
+ import * as plugins from '../../plugins.js';
2
2
  import { BaseIntegration } from '../../core/classes.baseintegration.js';
3
- import type { IIntegrationEntity, IIntegrationRuntime, IIntegrationSetupContext, IServiceCallRequest, IServiceCallResult } from '../../core/types.js';
3
+ import { NativeIntegrationOwner, type INativeSetupLease } from '../../core/classes.nativeintegrationowner.js';
4
+ import type { IDeviceFeatureWriteRequest, IIntegrationEntity, IIntegrationEvent, IIntegrationRuntime, IIntegrationSetupContext, ILogger, IServiceCallRequest, IServiceCallResult } from '../../core/types.js';
4
5
  import { DaikinClient } from './daikin.classes.client.js';
5
6
  import { DaikinConfigFlow } from './daikin.classes.configflow.js';
6
7
  import { createDaikinDiscoveryDescriptor } from './daikin.discovery.js';
7
8
  import { DaikinMapper } from './daikin.mapper.js';
8
- import type { IDaikinConfig } from './daikin.types.js';
9
- import { daikinDisplayName, daikinDomain } from './daikin.types.js';
9
+ import type { IDaikinIntegrationOptions, IDaikinNativeConfig, IDaikinSnapshot } from './daikin.types.js';
10
+ import { normalizeDaikinConfig } from './daikin.validation.js';
10
11
 
11
- export class DaikinIntegration extends BaseIntegration<IDaikinConfig> {
12
- public readonly domain = daikinDomain;
13
- public readonly displayName = daikinDisplayName;
12
+ const failure = (errorArg: unknown): IServiceCallResult => ({
13
+ success: false, error: errorArg instanceof Error ? errorArg.message : 'Daikin operation failed.',
14
+ data: errorArg instanceof plugins.ecobridgeDeviceManager.DaikinProtocolError
15
+ ? { code: errorArg.code, effectOutcome: errorArg.effectOutcome }
16
+ : { effectOutcome: 'not-sent' },
17
+ });
18
+
19
+ export class DaikinIntegration extends BaseIntegration<IDaikinNativeConfig> {
20
+ public readonly domain = 'daikin';
21
+ public readonly displayName = 'Daikin BRP069';
14
22
  public readonly status = 'control-runtime' as const;
15
23
  public readonly discoveryDescriptor = createDaikinDiscoveryDescriptor();
16
- public readonly configFlow = new DaikinConfigFlow();
24
+ public readonly configFlow: DaikinConfigFlow;
17
25
  public readonly metadata = {
18
- source: 'home-assistant/core',
19
- upstreamPath: 'homeassistant/components/daikin',
20
- upstreamDomain: daikinDomain,
21
- integrationType: 'device',
22
- iotClass: 'local_polling',
23
- requirements: ['pydaikin==2.17.2'],
24
- dependencies: [] as string[],
25
- afterDependencies: [] as string[],
26
- codeowners: ['@fredrike'],
27
- configFlow: true,
28
- documentation: 'https://www.home-assistant.io/integrations/daikin',
29
- zeroconf: ['_dkapi._tcp.local.'],
30
- discovery: {
31
- zeroconf: '_dkapi._tcp.local.',
32
- udp: 'DAIKIN_UDP/common/basic_info broadcast on UDP 30050 from source port 30000.',
33
- http: 'Manual HTTP candidates for /common/basic_info, /aircon/*, /skyfi/*, and /dsiot/* are recognized.',
34
- manual: true,
35
- },
36
- runtime: {
37
- type: 'control-runtime',
38
- polling: 'local Daikin HTTP key/value APIs plus SkyFi and BRP084 dsiot read modeling',
39
- services: ['snapshot', 'status', 'refresh', 'climate controls', 'power switch', 'zone switch', 'streamer', 'holiday/advanced modes when represented by local endpoints'],
40
- controls: true,
41
- },
26
+ source: '@ecobridge.xyz/devicemanager', upstreamDomain: 'daikin', integrationType: 'device', iotClass: 'local_polling',
42
27
  localApi: {
43
- implemented: [
44
- 'BRP069-style key/value local HTTP reads and set_control_info writes',
45
- 'BRP072C register_terminal/API-key request modeling when HTTPS is usable by the runtime',
46
- 'AirBase skyfi-prefixed reads plus zone setting writes',
47
- 'SkyFi password API reads and set.cgi/setzone.cgi writes',
48
- 'BRP084 dsiot snapshot reads',
49
- 'snapshot/manual-data and injected client/executor operation',
50
- ],
51
- explicitUnsupported: [
52
- 'BRP084 dsiot control writes without an injected commandExecutor',
53
- 'faking command success for snapshot/manual-only configurations',
54
- 'forcing TLS certificate bypass for BRP072C self-signed devices through Node fetch',
55
- ],
28
+ implemented: ['selected BRP069 fresh identity/model/control observations', 'explicit power', 'integer target in observed cool or heat mode', 'independent readback'],
29
+ explicitUnsupported: ['automatic family selection', 'mode/humidity/fan/direction changes', 'other controller-family effects', 'cloud', 'retries', 'optimistic state', 'injected effect executors'],
56
30
  },
57
31
  };
32
+ readonly #owner: NativeIntegrationOwner;
33
+
34
+ constructor(optionsArg: IDaikinIntegrationOptions = {}) {
35
+ super();
36
+ this.#owner = new NativeIntegrationOwner(this.domain, optionsArg);
37
+ this.configFlow = new DaikinConfigFlow({}, this.#owner.createFlowScope());
38
+ }
58
39
 
59
- public async setup(configArg: IDaikinConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
60
- void contextArg;
61
- return new DaikinRuntime(new DaikinClient(configArg));
40
+ public async setup(configArg: IDaikinNativeConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
41
+ const config = normalizeDaikinConfig(configArg);
42
+ return this.#owner.runSetup(contextArg, config.homeId, async (leaseArg) => {
43
+ const client = new DaikinClient(config, leaseArg.context);
44
+ let runtime: DaikinRuntime | undefined;
45
+ leaseArg.own(() => runtime ? runtime.cleanupOwnedResource() : client.destroy());
46
+ const identity = await client.getSnapshot(leaseArg.signal);
47
+ leaseArg.assertOpen();
48
+ runtime = new DaikinRuntime(client, identity, leaseArg, leaseArg.context.logger);
49
+ leaseArg.promoteToRuntime();
50
+ return runtime;
51
+ });
62
52
  }
63
53
 
64
- public async destroy(): Promise<void> {}
54
+ public destroy(): Promise<void> { return this.#owner.close(); }
55
+ public retryCleanup(): Promise<void> { return this.#owner.retryCleanup(); }
65
56
  }
66
57
 
67
58
  export class HomeAssistantDaikinIntegration extends DaikinIntegration {}
68
59
 
60
+ interface IDaikinSubscriber {
61
+ handler: (eventArg: IIntegrationEvent) => void;
62
+ cleanup: () => void;
63
+ }
64
+
69
65
  class DaikinRuntime implements IIntegrationRuntime {
70
- public domain = daikinDomain;
66
+ public readonly domain = 'daikin';
67
+ readonly #identity: IDaikinSnapshot;
68
+ readonly #subscriptions = new Set<IDaikinSubscriber>();
69
+ #entities: IIntegrationEntity[];
70
+ #available = true;
71
+ #destroy?: Promise<void>;
72
+ #removeAbort: () => void = () => undefined;
71
73
 
72
- constructor(private readonly client: DaikinClient) {}
74
+ constructor(private readonly client: DaikinClient, identityArg: IDaikinSnapshot, private readonly lease: INativeSetupLease,
75
+ private readonly logger?: ILogger) {
76
+ this.#identity = structuredClone(identityArg);
77
+ this.#entities = DaikinMapper.toEntities(identityArg);
78
+ // The lease retains failed cleanup; destroy() and the owner expose its stable rejection.
79
+ const aborted = (): void => { void this.destroy().catch(() => undefined); };
80
+ lease.signal.addEventListener('abort', aborted, { once: true });
81
+ this.#removeAbort = () => lease.signal.removeEventListener('abort', aborted);
82
+ if (lease.signal.aborted) aborted();
83
+ }
73
84
 
74
- public async devices(): Promise<shxInterfaces.data.IDeviceDefinition[]> {
75
- return DaikinMapper.toDevices(await this.client.getSnapshot());
85
+ public isHealthy(): boolean { return !this.#destroy && this.client.isHealthy(); }
86
+ public async devices(signalArg?: AbortSignal): Promise<plugins.shxInterfaces.data.IDeviceDefinition[]> {
87
+ return DaikinMapper.toDevices(await this.#snapshot(signalArg));
76
88
  }
89
+ public async entities(): Promise<IIntegrationEntity[]> { return DaikinMapper.toEntities(await this.#snapshot()); }
77
90
 
78
- public async entities(): Promise<IIntegrationEntity[]> {
79
- return DaikinMapper.toEntities(await this.client.getSnapshot());
91
+ public async subscribe(handlerArg: (eventArg: IIntegrationEvent) => void, signalArg?: AbortSignal): Promise<() => Promise<void>> {
92
+ this.#assertOpen();
93
+ if (typeof handlerArg !== 'function') throw new TypeError('Daikin subscriber must be a function.');
94
+ if (signalArg !== undefined && !(signalArg instanceof AbortSignal)) throw new TypeError('Daikin subscriber signal must be an AbortSignal.');
95
+ signalArg?.throwIfAborted();
96
+ if (this.#subscriptions.size >= 128) throw new Error('Daikin subscriber capacity is exhausted.');
97
+ const subscriber: IDaikinSubscriber = { handler: handlerArg, cleanup: () => {
98
+ this.#subscriptions.delete(subscriber);
99
+ signalArg?.removeEventListener('abort', subscriber.cleanup);
100
+ } };
101
+ this.#subscriptions.add(subscriber);
102
+ signalArg?.addEventListener('abort', subscriber.cleanup, { once: true });
103
+ if (signalArg?.aborted) subscriber.cleanup();
104
+ return async () => subscriber.cleanup();
80
105
  }
81
106
 
82
107
  public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
83
108
  try {
84
- if (requestArg.domain === daikinDomain && (requestArg.service === 'snapshot' || requestArg.service === 'status')) {
85
- return { success: true, data: await this.client.getSnapshot() };
86
- }
87
- if (requestArg.domain === daikinDomain && requestArg.service === 'refresh') {
88
- const result = await this.client.refresh();
89
- return { success: result.success, error: result.error, data: result.snapshot || result.data };
109
+ this.#assertOpen();
110
+ const control = DaikinMapper.controlForService(this.#identity, requestArg);
111
+ if (control === 'snapshot') {
112
+ const snapshot = await this.client.getSnapshot();
113
+ this.#observe(snapshot);
114
+ return { success: true, data: snapshot };
90
115
  }
116
+ return this.#result(await this.client.setControl(control));
117
+ } catch (errorArg) { this.#report(errorArg); return failure(errorArg); }
118
+ }
119
+
120
+ public async writeFeature(requestArg: IDeviceFeatureWriteRequest, signalArg?: AbortSignal): Promise<IServiceCallResult> {
121
+ try {
122
+ this.#assertOpen();
123
+ return this.#result(await this.client.setControl(DaikinMapper.controlForFeature(this.#identity, requestArg), signalArg));
124
+ } catch (errorArg) { this.#report(errorArg); return failure(errorArg); }
125
+ }
126
+
127
+ public destroy(): Promise<void> {
128
+ this.#destroy ??= Promise.resolve().then(() => this.lease.close());
129
+ return this.#destroy;
130
+ }
91
131
 
92
- const snapshot = await this.client.getSnapshot();
93
- const command = DaikinMapper.commandForService(snapshot, requestArg);
94
- if (!command) {
95
- return { success: false, error: `Unsupported Daikin service: ${requestArg.domain}.${requestArg.service}` };
132
+ /** One adapter resource groups its client and subscriptions; every sibling must settle. */
133
+ public async cleanupOwnedResource(): Promise<void> {
134
+ const results = await Promise.allSettled([
135
+ Promise.resolve().then(() => this.#removeAbort()),
136
+ ...[...this.#subscriptions].map((subscriberArg) => Promise.resolve().then(() => subscriberArg.cleanup())),
137
+ Promise.resolve().then(() => this.client.destroy()),
138
+ ]);
139
+ const errors = results.filter((resultArg): resultArg is PromiseRejectedResult => resultArg.status === 'rejected').map((resultArg) => resultArg.reason as unknown);
140
+ if (errors.length) throw new AggregateError(errors, 'Daikin runtime resource cleanup failed.');
141
+ }
142
+
143
+ async #snapshot(signalArg?: AbortSignal): Promise<IDaikinSnapshot> {
144
+ this.#assertOpen();
145
+ try {
146
+ const snapshot = await this.client.getSnapshot(signalArg);
147
+ this.#observe(snapshot);
148
+ return snapshot;
149
+ } catch (errorArg) { this.#report(errorArg); throw errorArg; }
150
+ }
151
+
152
+ #result(dataArg: plugins.ecobridgeDeviceManager.IDaikinControlResult): IServiceCallResult {
153
+ this.#observe(DaikinMapper.fromState(this.client.config, dataArg.observed));
154
+ return { success: dataArg.effectOutcome === 'confirmed', data: dataArg,
155
+ error: dataArg.effectOutcome === 'confirmed' ? undefined : 'Daikin setter was accepted but requested state was not confirmed by observation.' };
156
+ }
157
+
158
+ #observe(snapshotArg: IDaikinSnapshot): void {
159
+ const entities = DaikinMapper.toEntities(snapshotArg);
160
+ if (!this.#available) this.#emit({ type: 'availability_changed', data: { available: true } });
161
+ this.#available = true;
162
+ for (const entity of entities) {
163
+ const previous = this.#entities.find((entryArg) => entryArg.id === entity.id);
164
+ if (!previous || JSON.stringify([previous.state, previous.attributes]) !== JSON.stringify([entity.state, entity.attributes])) {
165
+ this.#emit({ type: 'state_changed', entityId: entity.id, data: entity });
96
166
  }
97
- const data = await this.client.execute(command);
98
- return { success: true, data: data ?? command };
99
- } catch (errorArg) {
100
- return { success: false, error: errorArg instanceof Error ? errorArg.message : String(errorArg) };
101
167
  }
168
+ this.#entities = entities;
169
+ }
170
+
171
+ #report(errorArg: unknown): void {
172
+ if (!this.client.isHealthy() && this.#available) {
173
+ this.#available = false;
174
+ this.#emit({ type: 'availability_changed', data: { available: false } });
175
+ }
176
+ this.#emit({ type: 'error', data: failure(errorArg) });
177
+ }
178
+
179
+ #emit(eventArg: Pick<IIntegrationEvent, 'type' | 'data'> & { entityId?: string }): void {
180
+ if (this.#destroy) return;
181
+ const event: IIntegrationEvent = { ...eventArg, integrationDomain: this.domain,
182
+ deviceId: DaikinMapper.deviceId(this.#identity), timestamp: Date.now() };
183
+ for (const subscriber of [...this.#subscriptions]) {
184
+ if (!this.#subscriptions.has(subscriber)) continue;
185
+ try {
186
+ const returned: unknown = subscriber.handler(structuredClone(event));
187
+ if (returned instanceof Promise) void returned.catch(() => this.#subscriberFailed(subscriber));
188
+ } catch { this.#subscriberFailed(subscriber); }
189
+ }
190
+ }
191
+
192
+ #subscriberFailed(subscriberArg: IDaikinSubscriber): void {
193
+ subscriberArg.cleanup();
194
+ this.logger?.log('error', 'Daikin event subscriber failed.');
195
+ this.#emit({ type: 'error', data: { error: 'Daikin event subscriber failed.' } });
102
196
  }
103
197
 
104
- public async destroy(): Promise<void> {
105
- await this.client.destroy();
198
+ #assertOpen(): void {
199
+ if (this.#destroy) throw new Error('Daikin runtime is destroyed.');
200
+ this.lease.assertOpen();
106
201
  }
107
202
  }