@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,683 +1,74 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { NativeHomeAdmission } from '../../core/classes.nativehomeadmission.js';
3
+ import type { INativeIntegrationSetupContext } from '../../core/types.js';
1
4
  import { DaikinMapper } from './daikin.mapper.js';
2
- import type {
3
- IDaikinCommandRequest,
4
- IDaikinConfig,
5
- IDaikinControlSettings,
6
- IDaikinRawData,
7
- IDaikinRefreshResult,
8
- IDaikinSnapshot,
9
- IDaikinValueMap,
10
- TDaikinApiVariant,
11
- } from './daikin.types.js';
12
- import { daikinDefaultHttpPort, daikinDefaultHttpsPort, daikinDefaultTimeoutMs, daikinSkyFiPort } from './daikin.types.js';
13
-
14
- export class DaikinApiError extends Error {}
15
- export class DaikinApiConnectionError extends DaikinApiError {}
16
- export class DaikinApiAuthorizationError extends DaikinApiError {}
17
- export class DaikinUnsupportedCommandError extends DaikinApiError {}
18
-
19
- const legacyResources = [
20
- 'common/basic_info',
21
- 'aircon/get_sensor_info',
22
- 'aircon/get_model_info',
23
- 'aircon/get_control_info',
24
- 'aircon/get_target',
25
- 'common/get_holiday',
26
- 'common/get_notify',
27
- 'aircon/get_day_power_ex',
28
- 'aircon/get_week_power',
29
- 'aircon/get_year_power',
30
- ];
31
-
32
- const airbaseResources = [
33
- 'skyfi/common/basic_info',
34
- 'skyfi/aircon/get_control_info',
35
- 'skyfi/aircon/get_model_info',
36
- 'skyfi/aircon/get_sensor_info',
37
- 'skyfi/aircon/get_zone_setting',
38
- ];
39
-
40
- const resourceKeyMap: Record<string, keyof IDaikinRawData> = {
41
- 'common/basic_info': 'basicInfo',
42
- 'skyfi/common/basic_info': 'basicInfo',
43
- 'aircon/get_control_info': 'controlInfo',
44
- 'skyfi/aircon/get_control_info': 'controlInfo',
45
- 'aircon/get_sensor_info': 'sensorInfo',
46
- 'skyfi/aircon/get_sensor_info': 'sensorInfo',
47
- 'aircon/get_model_info': 'modelInfo',
48
- 'skyfi/aircon/get_model_info': 'modelInfo',
49
- 'common/get_holiday': 'holiday',
50
- 'common/get_notify': 'notify',
51
- 'aircon/get_day_power_ex': 'dayPowerEx',
52
- 'aircon/get_week_power': 'weekPower',
53
- 'aircon/get_year_power': 'yearPower',
54
- 'skyfi/aircon/get_zone_setting': 'zoneSetting',
55
- };
56
-
57
- const brp084ModeMap: Record<string, string> = {
58
- '0300': 'auto',
59
- '0200': 'cool',
60
- '0100': 'heat',
61
- '0000': 'fan',
62
- '0500': 'dry',
63
- };
64
-
65
- const brp084FanModeMap: Record<string, string> = {
66
- '0A00': 'auto',
67
- '0B00': 'quiet',
68
- '0300': '1',
69
- '0400': '2',
70
- '0500': '3',
71
- '0600': '4',
72
- '0700': '5',
73
- };
74
-
75
- const brp084Paths = {
76
- power: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_A002', 'p_01'],
77
- mode: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_3001', 'p_01'],
78
- indoorTemp: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_A00B', 'p_01'],
79
- indoorHumidity: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_A00B', 'p_02'],
80
- outdoorTemp: ['/dsiot/edge/adr_0200.dgc_status', 'dgc_status', 'e_1003', 'e_A00D', 'p_01'],
81
- mac: ['/dsiot/edge.adp_i', 'adp_i', 'mac'],
82
- coolTemp: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_3001', 'p_02'],
83
- heatTemp: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_3001', 'p_03'],
84
- autoTemp: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_3001', 'p_1D'],
85
- coolFan: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_3001', 'p_09'],
86
- heatFan: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_3001', 'p_0A'],
87
- autoFan: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_3001', 'p_26'],
88
- fanFan: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_3001', 'p_28'],
89
- todayRuntime: ['/dsiot/edge/adr_0100.i_power.week_power', 'week_power', 'today_runtime'],
90
- weeklyData: ['/dsiot/edge/adr_0100.i_power.week_power', 'week_power', 'datas'],
91
- };
5
+ import type { IDaikinNativeConfig, IDaikinSnapshot } from './daikin.types.js';
6
+ import { normalizeDaikinConfig } from './daikin.validation.js';
92
7
 
8
+ /** Selected BRP069 facade. The public protocol owns validation, preflight, HTTP, queueing and drain. */
93
9
  export class DaikinClient {
94
- private currentSnapshot?: IDaikinSnapshot;
95
-
96
- constructor(private readonly config: IDaikinConfig) {}
97
-
98
- public async getSnapshot(forceRefreshArg = false): Promise<IDaikinSnapshot> {
99
- if (!forceRefreshArg && this.currentSnapshot) {
100
- return this.cloneSnapshot(this.currentSnapshot);
101
- }
102
-
103
- if (!forceRefreshArg && this.config.snapshot) {
104
- this.currentSnapshot = DaikinMapper.toSnapshot({ config: this.config, source: 'snapshot', online: this.config.snapshot.online });
105
- return this.cloneSnapshot(this.currentSnapshot);
106
- }
107
-
108
- if (this.config.client) {
109
- try {
110
- this.currentSnapshot = await this.snapshotFromClient();
111
- } catch (errorArg) {
112
- this.currentSnapshot = this.offlineSnapshot(this.errorMessage(errorArg));
113
- }
114
- return this.cloneSnapshot(this.currentSnapshot);
115
- }
116
-
117
- if (!forceRefreshArg && this.hasManualData()) {
118
- this.currentSnapshot = DaikinMapper.toSnapshot({ config: this.config, online: this.config.online ?? true, source: 'manual' });
119
- return this.cloneSnapshot(this.currentSnapshot);
120
- }
121
-
122
- if (this.config.host || this.config.url) {
123
- try {
124
- this.currentSnapshot = await this.fetchSnapshot();
125
- } catch (errorArg) {
126
- this.currentSnapshot = this.offlineSnapshot(this.errorMessage(errorArg));
127
- }
128
- return this.cloneSnapshot(this.currentSnapshot);
129
- }
130
-
131
- this.currentSnapshot = this.offlineSnapshot('No Daikin local HTTP endpoint, client, snapshot, or manual data is configured.');
132
- return this.cloneSnapshot(this.currentSnapshot);
133
- }
134
-
135
- public async refresh(): Promise<IDaikinRefreshResult> {
136
- try {
137
- this.currentSnapshot = undefined;
138
- const snapshot = await this.getSnapshot(Boolean(this.config.host || this.config.url || this.config.client));
139
- const success = snapshot.online && !snapshot.error;
140
- return { success, snapshot, error: success ? undefined : snapshot.error, data: { source: snapshot.source } };
141
- } catch (errorArg) {
142
- const error = this.errorMessage(errorArg);
143
- const snapshot = this.offlineSnapshot(error);
144
- this.currentSnapshot = snapshot;
145
- return { success: false, snapshot: this.cloneSnapshot(snapshot), error };
146
- }
147
- }
148
-
149
- public async fetchSnapshot(): Promise<IDaikinSnapshot> {
150
- const variant = this.config.apiVariant || 'auto';
151
- if (variant === 'skyfi' || this.config.password) {
152
- return this.snapshotFromRawData(await this.fetchSkyFiRawData(), 'http');
153
- }
154
- if (variant === 'airbase') {
155
- return this.snapshotFromRawData(await this.fetchLegacyRawData('airbase'), 'http');
156
- }
157
- if (variant === 'brp084') {
158
- return this.snapshotFromRawData(await this.fetchBrp084RawData(), 'http');
159
- }
160
- if (variant === 'brp072c' || this.config.apiKey) {
161
- return this.snapshotFromRawData(await this.fetchLegacyRawData('brp072c'), 'http');
162
- }
163
-
164
- const legacy = await this.fetchLegacyRawData('brp069');
165
- const legacyValues = DaikinMapper.valuesFromRawData(legacy);
166
- if (legacyValues.mode || legacyValues.pow || legacyValues.htemp || legacyValues.mac) {
167
- return this.snapshotFromRawData(legacy, 'http');
168
- }
169
-
170
- return this.snapshotFromRawData(await this.fetchLegacyRawData('airbase'), 'http');
171
- }
172
-
173
- public async execute(commandArg: IDaikinCommandRequest): Promise<unknown> {
174
- if (this.config.commandExecutor) {
175
- return this.config.commandExecutor.execute(commandArg);
176
- }
177
- if (this.config.client?.execute) {
178
- return this.config.client.execute(commandArg);
179
- }
180
- if (commandArg.action === 'refresh') {
181
- return (await this.refresh()).snapshot;
182
- }
183
- if (!this.config.host && !this.config.url) {
184
- throw new DaikinApiConnectionError('Daikin commands require config.host/config.url, an injected client.execute, or commandExecutor. Static snapshots/manual data are read-only.');
185
- }
186
- if (commandArg.path && commandArg.method) {
187
- return this.requestKeyValue(commandArg.path, commandArg.query, this.requestVariant(commandArg.protocol), true);
188
- }
189
-
190
- switch (commandArg.action) {
191
- case 'set_control':
192
- return this.setControl(commandArg.settings || {}, this.requestVariant(commandArg.protocol));
193
- case 'set_holiday':
194
- return this.setHoliday(Boolean(commandArg.enabled), this.requestVariant(commandArg.protocol));
195
- case 'set_advanced_mode':
196
- return this.setAdvancedMode(commandArg.mode || 'powerful', Boolean(commandArg.enabled), this.requestVariant(commandArg.protocol));
197
- case 'set_streamer':
198
- return this.setStreamer(Boolean(commandArg.enabled), this.requestVariant(commandArg.protocol));
199
- case 'set_zone':
200
- return this.setZone(commandArg.zoneId, commandArg.zoneKey || 'zone_onoff', commandArg.zoneValue ?? (commandArg.enabled ? '1' : '0'), this.requestVariant(commandArg.protocol));
201
- default:
202
- throw new DaikinUnsupportedCommandError(`Unsupported Daikin command: ${commandArg.action}`);
203
- }
204
- }
205
-
206
- public async destroy(): Promise<void> {
207
- await this.config.client?.destroy?.();
208
- }
209
-
210
- private async snapshotFromClient(): Promise<IDaikinSnapshot> {
211
- const client = this.config.client;
212
- if (!client) {
213
- throw new DaikinApiConnectionError('No Daikin client configured.');
214
- }
215
- if (client.getSnapshot) {
216
- const result = await client.getSnapshot();
217
- if (this.isSnapshot(result)) {
218
- return DaikinMapper.toSnapshot({ config: { ...this.config, snapshot: result }, source: 'client', online: result.online });
219
- }
220
- return DaikinMapper.toSnapshot({ config: this.config, rawData: result, online: true, source: 'client' });
221
- }
222
- if (client.getRawData) {
223
- return DaikinMapper.toSnapshot({ config: this.config, rawData: await client.getRawData(), online: true, source: 'client' });
224
- }
225
- throw new DaikinApiConnectionError('Daikin client must expose getSnapshot() or getRawData().');
226
- }
227
-
228
- private snapshotFromRawData(rawDataArg: Partial<IDaikinRawData>, sourceArg: 'http' | 'manual' | 'client'): IDaikinSnapshot {
229
- return DaikinMapper.toSnapshot({ config: this.config, rawData: rawDataArg, online: true, source: sourceArg });
230
- }
231
-
232
- private async fetchLegacyRawData(variantArg: TDaikinApiVariant): Promise<Partial<IDaikinRawData>> {
233
- const resources = variantArg === 'airbase' ? airbaseResources : legacyResources;
234
- if (variantArg === 'brp072c' && this.config.apiKey) {
235
- await this.requestKeyValue('common/register_terminal', { key: this.config.apiKey }, variantArg, true);
236
- }
237
-
238
- const rawData: Partial<IDaikinRawData> = { variant: variantArg, resources: {}, fetchedAt: new Date().toISOString() };
239
- for (const resource of resources) {
240
- const data = await this.requestKeyValue(resource, undefined, variantArg, true).catch((errorArg) => {
241
- if (resource === resources[0]) {
242
- throw errorArg;
243
- }
244
- return {};
245
- });
246
- rawData.resources![resource] = data;
247
- const key = resourceKeyMap[resource];
248
- if (key) {
249
- (rawData as Record<string, unknown>)[key] = data;
250
- }
251
- }
252
- return rawData;
253
- }
254
-
255
- private async fetchSkyFiRawData(): Promise<Partial<IDaikinRawData>> {
256
- const ac = await this.requestSkyFi('ac.cgi');
257
- const zones = await this.requestSkyFi('zones.cgi').catch(() => ({}));
258
- return {
259
- variant: 'skyfi',
260
- skyfiAc: ac,
261
- skyfiZones: zones,
262
- values: { ...ac, ...zones },
263
- fetchedAt: new Date().toISOString(),
264
- };
265
- }
266
-
267
- private async fetchBrp084RawData(): Promise<Partial<IDaikinRawData>> {
268
- const payload = {
269
- requests: [
270
- { op: 2, to: '/dsiot/edge/adr_0100.dgc_status?filter=pv,pt,md' },
271
- { op: 2, to: '/dsiot/edge/adr_0200.dgc_status?filter=pv,pt,md' },
272
- { op: 2, to: '/dsiot/edge/adr_0100.i_power.week_power?filter=pv,pt,md' },
273
- { op: 2, to: '/dsiot/edge.adp_i' },
274
- ],
275
- };
276
- const response = await this.requestJson<Record<string, unknown>>('/dsiot/multireq', payload, 'brp084');
277
- const modeValue = this.findBrp084Value(response, brp084Paths.mode);
278
- const mode = brp084ModeMap[modeValue] || modeValue;
279
- const isOff = this.findBrp084Value(response, brp084Paths.power) === '00';
280
- const tempPath = mode === 'cool' ? brp084Paths.coolTemp : mode === 'heat' ? brp084Paths.heatTemp : brp084Paths.autoTemp;
281
- const fanPath = mode === 'cool' ? brp084Paths.coolFan : mode === 'heat' ? brp084Paths.heatFan : mode === 'fan' ? brp084Paths.fanFan : brp084Paths.autoFan;
282
- const values: IDaikinValueMap = {
283
- mac: this.findBrp084Value(response, brp084Paths.mac),
284
- pow: isOff ? '0' : '1',
285
- mode: isOff ? 'off' : mode,
286
- otemp: this.hexTemp(this.findBrp084Value(response, brp084Paths.outdoorTemp)),
287
- htemp: this.hexTemp(this.findBrp084Value(response, brp084Paths.indoorTemp), 1),
288
- hhum: this.hexInt(this.findBrp084Value(response, brp084Paths.indoorHumidity)),
289
- stemp: this.hexTemp(this.findBrp084Value(response, tempPath)),
290
- f_rate: brp084FanModeMap[this.findBrp084Value(response, fanPath)] || 'auto',
291
- datas: this.brp084WeeklyData(response),
292
- };
293
- return { variant: 'brp084', brp084: response, values, fetchedAt: new Date().toISOString() };
294
- }
295
-
296
- private async setControl(settingsArg: Partial<IDaikinControlSettings>, variantArg: TDaikinApiVariant): Promise<unknown> {
297
- if (variantArg === 'skyfi') {
298
- return this.setSkyFiControl(settingsArg);
299
- }
300
- if (variantArg === 'brp084') {
301
- throw new DaikinUnsupportedCommandError('Daikin BRP084 dsiot control writes are not implemented by this native port. Provide commandExecutor for BRP084 controls.');
302
- }
303
- return this.setLegacyControl(settingsArg, variantArg === 'airbase' ? 'airbase' : 'brp069');
304
- }
305
-
306
- private async setLegacyControl(settingsArg: Partial<IDaikinControlSettings>, variantArg: 'brp069' | 'airbase'): Promise<unknown> {
307
- const prefix = variantArg === 'airbase' ? 'skyfi/' : '';
308
- const current = await this.requestKeyValue(`${prefix}aircon/get_control_info`, undefined, variantArg, true);
309
- const values: IDaikinValueMap = { ...current };
310
- this.applyControlSettings(values, settingsArg, variantArg);
311
-
312
- if (settingsArg.mode === 'off') {
313
- values.pow = '0';
314
- values.mode = current.mode || values.mode;
315
- } else if ('mode' in settingsArg || Object.keys(settingsArg).length === 0) {
316
- values.pow = '1';
317
- }
318
-
319
- this.fillModeDefaults(values, current);
320
- const query = variantArg === 'airbase'
321
- ? {
322
- f_airside: values.f_airside || 0,
323
- f_auto: values.f_auto || '0',
324
- f_dir: values.f_dir || '0',
325
- f_rate: (values.f_rate || 'A')[0],
326
- lpw: '',
327
- mode: values.mode || current.mode || '0',
328
- pow: values.pow || current.pow || '1',
329
- shum: values.shum || current.shum || '--',
330
- stemp: values.stemp || current.stemp || '--',
331
- }
332
- : this.cleanQuery({
333
- mode: values.mode || current.mode || '0',
334
- pow: values.pow || current.pow || '1',
335
- shum: values.shum || current.shum || '--',
336
- stemp: values.stemp || current.stemp || '--',
337
- f_rate: values.f_rate !== undefined || current.f_rate !== undefined ? values.f_rate || current.f_rate : undefined,
338
- f_dir: values.f_dir !== undefined || current.f_dir !== undefined ? values.f_dir || current.f_dir : undefined,
339
- f_dir_ud: current.f_dir_ud !== undefined ? this.swingAxis(values.f_dir, 'ud') : undefined,
340
- f_dir_lr: current.f_dir_lr !== undefined ? this.swingAxis(values.f_dir, 'lr') : undefined,
341
- });
342
- return this.requestKeyValue(`${prefix}aircon/set_control_info`, query, variantArg, true);
343
- }
344
-
345
- private async setSkyFiControl(settingsArg: Partial<IDaikinControlSettings>): Promise<unknown> {
346
- const current = await this.requestSkyFi('ac.cgi');
347
- const values: IDaikinValueMap = { ...current };
348
- this.applyControlSettings(values, settingsArg, 'skyfi');
349
- if (settingsArg.mode === 'off') {
350
- return this.requestSkyFi('set.cgi', { p: '0' });
351
- }
352
- if ('mode' in settingsArg || Object.keys(settingsArg).length === 0) {
353
- values.opmode = '1';
354
- values.pow = '1';
355
- }
356
- return this.requestSkyFi('set.cgi', {
357
- p: values.opmode || values.pow || '1',
358
- t: values.settemp || values.stemp || current.settemp || current.stemp,
359
- f: values.fanspeed || values.f_rate || current.fanspeed || current.f_rate,
360
- m: values.acmode || values.mode || current.acmode || current.mode,
10
+ readonly #config: Readonly<IDaikinNativeConfig>;
11
+ readonly #protocol: plugins.ecobridgeDeviceManager.DaikinProtocol;
12
+ readonly #lifetime = new AbortController();
13
+ #healthy = false;
14
+ #destroy?: Promise<void>;
15
+ #removeAbort: () => void = () => undefined;
16
+
17
+ constructor(configArg: IDaikinNativeConfig, contextArg: INativeIntegrationSetupContext) {
18
+ const config = normalizeDaikinConfig(configArg);
19
+ const context = new NativeHomeAdmission('daikin').admit(contextArg, config.homeId);
20
+ const signalArg = context.abortSignal;
21
+ this.#config = Object.freeze({ ...config, homeId: context.homeId });
22
+ this.#protocol = new plugins.ecobridgeDeviceManager.DaikinProtocol({
23
+ address: this.#config.host, expectedMac: this.#config.expectedMac, port: this.#config.port,
24
+ timeoutMs: this.#config.timeoutMs, maxResponseBytes: this.#config.maxResponseBytes,
25
+ maxPendingOperations: this.#config.maxPendingOperations,
361
26
  });
362
- }
363
-
364
- private async setHoliday(enabledArg: boolean, variantArg: TDaikinApiVariant): Promise<unknown> {
365
- if (variantArg === 'skyfi' || variantArg === 'airbase' || variantArg === 'brp084') {
366
- throw new DaikinUnsupportedCommandError(`Daikin ${variantArg} holiday mode is not represented by a supported local request in this port.`);
367
- }
368
- return this.requestKeyValue('common/set_holiday', { en_hol: enabledArg ? '1' : '0' }, variantArg, true);
369
- }
370
-
371
- private async setAdvancedMode(modeArg: string, enabledArg: boolean, variantArg: TDaikinApiVariant): Promise<unknown> {
372
- if (variantArg === 'skyfi' || variantArg === 'brp084') {
373
- throw new DaikinUnsupportedCommandError(`Daikin ${variantArg} advanced modes are not represented by a supported local request in this port.`);
27
+ if (signalArg) {
28
+ // Preserve cleanup failure in destroy() while an event listener has no promise consumer.
29
+ const aborted = (): void => { void this.destroy().catch(() => undefined); };
30
+ signalArg.addEventListener('abort', aborted, { once: true });
31
+ this.#removeAbort = () => signalArg.removeEventListener('abort', aborted);
32
+ if (signalArg.aborted) aborted();
374
33
  }
375
- const mode = DaikinMapper.humanToDaikin('spmode_kind', modeArg, variantArg) || modeArg;
376
- return this.requestKeyValue(`${variantArg === 'airbase' ? 'skyfi/' : ''}aircon/set_special_mode`, { spmode_kind: mode, set_spmode: enabledArg ? '1' : '0' }, variantArg, true);
377
34
  }
378
35
 
379
- private async setStreamer(enabledArg: boolean, variantArg: TDaikinApiVariant): Promise<unknown> {
380
- if (variantArg === 'skyfi' || variantArg === 'brp084') {
381
- throw new DaikinUnsupportedCommandError(`Daikin ${variantArg} streamer mode is not represented by a supported local request in this port.`);
382
- }
383
- return this.requestKeyValue(`${variantArg === 'airbase' ? 'skyfi/' : ''}aircon/set_special_mode`, { en_streamer: enabledArg ? '1' : '0' }, variantArg, true);
384
- }
36
+ public get config(): Readonly<IDaikinNativeConfig> { return this.#config; }
37
+ public isHealthy(): boolean { return this.#healthy && !this.#lifetime.signal.aborted; }
385
38
 
386
- private async setZone(zoneIdArg: number | undefined, keyArg: string, valueArg: string, variantArg: TDaikinApiVariant): Promise<unknown> {
387
- if (zoneIdArg === undefined || !Number.isInteger(zoneIdArg) || zoneIdArg < 0) {
388
- throw new DaikinUnsupportedCommandError('Daikin zone commands require a non-negative zoneId.');
389
- }
390
- if (variantArg === 'skyfi') {
391
- if (keyArg !== 'zone_onoff') {
392
- throw new DaikinUnsupportedCommandError('Daikin SkyFi only supports zone_onoff through setzone.cgi.');
393
- }
394
- return this.requestSkyFi('setzone.cgi', { z: zoneIdArg + 1, s: valueArg });
395
- }
396
- if (variantArg !== 'airbase') {
397
- throw new DaikinUnsupportedCommandError(`Daikin ${variantArg} zone commands are not represented by a supported local request in this port.`);
398
- }
399
-
400
- const current = await this.requestKeyValue('skyfi/aircon/get_zone_setting', undefined, variantArg, true);
401
- if (!current[keyArg]) {
402
- throw new DaikinUnsupportedCommandError(`Daikin AirBase zone setting ${keyArg} is not available on this device.`);
403
- }
404
- const group = this.listValue(current[keyArg]);
405
- if (zoneIdArg >= group.length) {
406
- throw new DaikinUnsupportedCommandError(`Daikin AirBase zone ${zoneIdArg} is outside the available range 0-${Math.max(0, group.length - 1)}.`);
407
- }
408
- group[zoneIdArg] = valueArg;
409
- const values = { ...current, [keyArg]: encodeURIComponent(group.join(';')).toLowerCase() };
410
- const query = [`zone_name=${values.zone_name || ''}`, `zone_onoff=${values.zone_onoff || ''}`];
411
- if (values.lztemp_c !== undefined && values.lztemp_h !== undefined) {
412
- query.push(`lztemp_c=${values.lztemp_c}`, `lztemp_h=${values.lztemp_h}`);
413
- }
414
- return this.requestKeyValue(`skyfi/aircon/set_zone_setting?${query.join('&')}`, undefined, variantArg, true);
415
- }
416
-
417
- private applyControlSettings(valuesArg: IDaikinValueMap, settingsArg: Partial<IDaikinControlSettings>, variantArg: TDaikinApiVariant): void {
418
- for (const [key, value] of Object.entries(settingsArg)) {
419
- if (value === undefined) {
420
- continue;
421
- }
422
- if (key === 'stemp') {
423
- valuesArg.stemp = DaikinMapper.formatTargetTemperature(Number(value));
424
- valuesArg.settemp = valuesArg.stemp;
425
- } else if (key === 'shum') {
426
- valuesArg.shum = String(value);
427
- } else if (key === 'mode') {
428
- const daikinMode = DaikinMapper.humanToDaikin('mode', String(value), variantArg);
429
- valuesArg.mode = daikinMode;
430
- valuesArg.acmode = daikinMode;
431
- } else if (key === 'f_rate') {
432
- const fanRate = DaikinMapper.humanToDaikin('f_rate', String(value), variantArg);
433
- valuesArg.f_rate = fanRate;
434
- valuesArg.fanspeed = fanRate;
435
- } else if (key === 'f_dir') {
436
- valuesArg.f_dir = DaikinMapper.humanToDaikin('f_dir', String(value), variantArg);
437
- } else {
438
- valuesArg[key] = String(value);
439
- }
440
- }
441
- }
442
-
443
- private fillModeDefaults(valuesArg: IDaikinValueMap, currentArg: IDaikinValueMap): void {
444
- const mode = valuesArg.mode || currentArg.mode;
445
- if (!mode) {
446
- return;
447
- }
448
- const defaults: Record<string, string> = { stemp: `dt${mode}`, shum: `dh${mode}`, f_rate: `dfr${mode}` };
449
- for (const [target, source] of Object.entries(defaults)) {
450
- if (valuesArg[target] === undefined && currentArg[source] !== undefined) {
451
- valuesArg[target] = currentArg[source];
452
- }
453
- }
454
- }
455
-
456
- private async requestSkyFi(pathArg: string, queryArg: Record<string, string | number | boolean | undefined> = {}): Promise<IDaikinValueMap> {
457
- const query = { pass: this.config.password || '', ...queryArg };
458
- const response = await this.fetchText(pathArg, query, 'skyfi', true);
459
- return DaikinMapper.parseSkyFiResponse(response);
460
- }
461
-
462
- private async requestKeyValue(pathArg: string, queryArg: Record<string, string | number | boolean | undefined> | undefined, variantArg: TDaikinApiVariant, allowEmptyArg = false): Promise<IDaikinValueMap> {
463
- if (this.config.client?.getResource && !this.config.host && !this.config.url) {
464
- return this.config.client.getResource(pathArg, queryArg);
465
- }
466
- const text = await this.fetchText(pathArg, queryArg, variantArg, allowEmptyArg);
467
- if (!text.trim()) {
468
- return {};
469
- }
39
+ public async getSnapshot(signalArg?: AbortSignal): Promise<IDaikinSnapshot> {
470
40
  try {
471
- return DaikinMapper.parseKeyValueResponse(text);
472
- } catch (errorArg) {
473
- if (allowEmptyArg) {
474
- return {};
475
- }
476
- throw errorArg;
477
- }
41
+ const state = await this.#protocol.readState({ signal: this.#signal(signalArg) });
42
+ this.#healthy = true;
43
+ return DaikinMapper.fromState(this.#config, state);
44
+ } catch (errorArg) { this.#healthy = false; throw errorArg; }
478
45
  }
479
46
 
480
- private async requestJson<TValue>(pathArg: string, bodyArg: unknown, variantArg: TDaikinApiVariant): Promise<TValue> {
481
- const url = this.url(pathArg, undefined, variantArg);
482
- let response: Response;
47
+ public async setControl(controlArg: plugins.ecobridgeDeviceManager.IDaikinControl, signalArg?: AbortSignal): Promise<plugins.ecobridgeDeviceManager.IDaikinControlResult> {
483
48
  try {
484
- response = await globalThis.fetch(url, {
485
- method: 'POST',
486
- headers: this.headers(variantArg, { 'content-type': 'application/json', accept: 'application/json' }),
487
- body: JSON.stringify(bodyArg),
488
- signal: AbortSignal.timeout(this.config.timeoutMs || daikinDefaultTimeoutMs),
489
- });
49
+ const result = await this.#protocol.setControl(controlArg, { signal: this.#signal(signalArg) });
50
+ this.#healthy = true;
51
+ return result;
490
52
  } catch (errorArg) {
491
- throw new DaikinApiConnectionError(`Connection to ${url} failed: ${this.errorMessage(errorArg)}`);
53
+ if (!(errorArg instanceof plugins.ecobridgeDeviceManager.DaikinProtocolError) || !['unsupported', 'invalid-options', 'device-error', 'capacity'].includes(errorArg.code)) this.#healthy = false;
54
+ throw errorArg;
492
55
  }
493
- await this.assertOk(response, url, false);
494
- return await response.json() as TValue;
495
56
  }
496
57
 
497
- private async fetchText(pathArg: string, queryArg: Record<string, string | number | boolean | undefined> | undefined, variantArg: TDaikinApiVariant, allowEmptyArg: boolean): Promise<string> {
498
- const url = this.url(pathArg, queryArg, variantArg);
499
- let response: Response;
500
- try {
501
- response = await globalThis.fetch(url, {
502
- method: 'GET',
503
- headers: this.headers(variantArg),
504
- signal: AbortSignal.timeout(this.config.timeoutMs || daikinDefaultTimeoutMs),
58
+ public destroy(): Promise<void> {
59
+ if (!this.#destroy) {
60
+ this.#healthy = false;
61
+ this.#destroy = Promise.resolve().then(async () => {
62
+ this.#removeAbort();
63
+ this.#lifetime.abort(new Error('Daikin client is destroyed.'));
64
+ await this.#protocol.close();
505
65
  });
506
- } catch (errorArg) {
507
- throw new DaikinApiConnectionError(`Connection to ${url} failed: ${this.errorMessage(errorArg)}`);
508
- }
509
- if (response.status === 404 && allowEmptyArg) {
510
- await response.arrayBuffer().catch(() => undefined);
511
- return '';
512
- }
513
- await this.assertOk(response, url, allowEmptyArg);
514
- return await response.text();
515
- }
516
-
517
- private async assertOk(responseArg: Response, urlArg: string, allowEmptyArg: boolean): Promise<void> {
518
- if (responseArg.status === 403 || responseArg.status === 401) {
519
- throw new DaikinApiAuthorizationError('Daikin authentication failed. Check API key, UUID, or SkyFi password.');
520
- }
521
- if (!responseArg.ok) {
522
- const text = await responseArg.text().catch(() => '');
523
- if (allowEmptyArg && responseArg.status === 404) {
524
- return;
525
- }
526
- throw new DaikinApiConnectionError(`Daikin endpoint ${urlArg} failed with HTTP ${responseArg.status}${text ? `: ${text}` : ''}`);
527
- }
528
- }
529
-
530
- private url(pathArg: string, queryArg: Record<string, string | number | boolean | undefined> | undefined, variantArg: TDaikinApiVariant): string {
531
- if (/^https?:\/\//i.test(pathArg)) {
532
- return pathArg;
533
- }
534
- const base = this.baseUrl(variantArg);
535
- const url = new URL(pathArg.startsWith('/') ? pathArg : `/${pathArg}`, base.endsWith('/') ? base : `${base}/`);
536
- for (const [key, value] of Object.entries(queryArg || {})) {
537
- if (value !== undefined) {
538
- url.searchParams.set(key, String(value));
539
- }
540
- }
541
- return url.toString();
542
- }
543
-
544
- private baseUrl(variantArg: TDaikinApiVariant): string {
545
- const configured = this.config.url || this.config.host || 'localhost';
546
- if (/^https?:\/\//i.test(configured)) {
547
- const url = new URL(configured);
548
- return `${url.protocol}//${url.host}`;
549
- }
550
- const ssl = this.config.ssl ?? this.config.tls ?? variantArg === 'brp072c';
551
- const protocol = ssl ? 'https' : 'http';
552
- const defaultPort = variantArg === 'skyfi' ? daikinSkyFiPort : ssl ? daikinDefaultHttpsPort : daikinDefaultHttpPort;
553
- const port = this.config.port && this.config.port !== defaultPort ? `:${this.config.port}` : '';
554
- return `${protocol}://${this.hostForUrl(configured)}${port}`;
555
- }
556
-
557
- private headers(variantArg: TDaikinApiVariant, extraArg: Record<string, string> = {}): Record<string, string> {
558
- const headers = { ...extraArg };
559
- if (variantArg === 'brp072c' && this.config.uuid) {
560
- headers['x-daikin-uuid'] = this.config.uuid.replace(/-/g, '');
561
66
  }
562
- return headers;
563
- }
564
-
565
- private requestVariant(valueArg: IDaikinCommandRequest['protocol']): TDaikinApiVariant {
566
- if (valueArg && valueArg !== 'legacy' && valueArg !== 'auto') {
567
- return valueArg;
568
- }
569
- if (this.config.apiVariant && this.config.apiVariant !== 'auto') {
570
- return this.config.apiVariant;
571
- }
572
- if (this.config.password) {
573
- return 'skyfi';
574
- }
575
- if (this.config.apiKey) {
576
- return 'brp072c';
577
- }
578
- return 'brp069';
579
- }
580
-
581
- private findBrp084Value(dataArg: Record<string, unknown>, pathArg: string[]): string {
582
- const responses = Array.isArray(dataArg.responses) ? dataArg.responses as Array<Record<string, unknown>> : [];
583
- const from = pathArg[0];
584
- let nodes = responses.filter((responseArg) => responseArg.fr === from || responseArg.to === from).map((responseArg) => responseArg.pc as Record<string, unknown>).filter(Boolean);
585
- for (const key of pathArg.slice(1)) {
586
- let found: Record<string, unknown> | undefined;
587
- for (const node of nodes) {
588
- if (node.pn === key) {
589
- found = node;
590
- break;
591
- }
592
- }
593
- if (!found) {
594
- throw new DaikinApiConnectionError(`Daikin BRP084 response missing key ${key}.`);
595
- }
596
- if (pathArg[pathArg.length - 1] === key) {
597
- return String(found.pv ?? '');
598
- }
599
- nodes = Array.isArray(found.pch) ? found.pch as Array<Record<string, unknown>> : [];
600
- }
601
- return '';
602
- }
603
-
604
- private brp084WeeklyData(dataArg: Record<string, unknown>): string | undefined {
605
- try {
606
- const value = this.findBrp084Value(dataArg, brp084Paths.weeklyData);
607
- return Array.isArray(value) ? value.join('/') : String(value);
608
- } catch {
609
- return undefined;
610
- }
611
- }
612
-
613
- private hexTemp(valueArg: string, divisorArg = 2): string | undefined {
614
- if (!valueArg) {
615
- return undefined;
616
- }
617
- const parsed = Number.parseInt(valueArg.slice(0, 2), 16);
618
- return Number.isFinite(parsed) ? String(parsed / divisorArg) : undefined;
619
- }
620
-
621
- private hexInt(valueArg: string): string | undefined {
622
- if (!valueArg) {
623
- return undefined;
624
- }
625
- const parsed = Number.parseInt(valueArg, 16);
626
- return Number.isFinite(parsed) ? String(parsed) : undefined;
627
- }
628
-
629
- private swingAxis(valueArg: string | undefined, axisArg: 'ud' | 'lr'): string {
630
- const value = valueArg || '0';
631
- if (axisArg === 'ud') {
632
- return value === '1' || value === '3' ? 'S' : '0';
633
- }
634
- return value === '2' || value === '3' ? 'S' : '0';
635
- }
636
-
637
- private listValue(valueArg?: string): string[] {
638
- if (!valueArg) {
639
- return [];
640
- }
641
- let decoded = valueArg;
642
- try {
643
- decoded = decodeURIComponent(valueArg.replace(/\+/g, '%20'));
644
- } catch {
645
- decoded = valueArg;
646
- }
647
- return decoded.includes(';') ? decoded.split(';') : decoded.split('');
648
- }
649
-
650
- private cleanQuery(queryArg: Record<string, string | number | boolean | undefined>): Record<string, string | number | boolean | undefined> {
651
- const cleaned: Record<string, string | number | boolean | undefined> = {};
652
- for (const [key, value] of Object.entries(queryArg)) {
653
- if (value !== undefined) {
654
- cleaned[key] = value;
655
- }
656
- }
657
- return cleaned;
658
- }
659
-
660
- private offlineSnapshot(errorArg: string): IDaikinSnapshot {
661
- return DaikinMapper.toSnapshot({ config: this.config, online: false, source: 'runtime', error: errorArg });
662
- }
663
-
664
- private hasManualData(): boolean {
665
- return Boolean(this.config.snapshot || this.config.rawData || this.config.values);
666
- }
667
-
668
- private isSnapshot(valueArg: unknown): valueArg is IDaikinSnapshot {
669
- return Boolean(valueArg && typeof valueArg === 'object' && 'device' in valueArg && 'climate' in valueArg);
670
- }
671
-
672
- private hostForUrl(hostArg: string): string {
673
- return hostArg.includes(':') && !hostArg.startsWith('[') ? `[${hostArg}]` : hostArg;
674
- }
675
-
676
- private errorMessage(errorArg: unknown): string {
677
- return errorArg instanceof Error ? errorArg.message : String(errorArg);
67
+ return this.#destroy;
678
68
  }
679
69
 
680
- private cloneSnapshot(snapshotArg: IDaikinSnapshot): IDaikinSnapshot {
681
- return JSON.parse(JSON.stringify(snapshotArg)) as IDaikinSnapshot;
70
+ #signal(signalArg?: AbortSignal): AbortSignal {
71
+ if (this.#destroy) throw new plugins.ecobridgeDeviceManager.DaikinProtocolError('Daikin client is destroyed.', 'closed');
72
+ return signalArg ? AbortSignal.any([signalArg, this.#lifetime.signal]) : this.#lifetime.signal;
682
73
  }
683
74
  }