@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,217 +1,46 @@
1
- import * as plugins from '../../plugins.js';
1
+ import type * as plugins from '../../plugins.js';
2
2
  import type { IIntegrationEntity } from '../../core/types.js';
3
- import type { ISoundtouchPreset, ISoundtouchSnapshot, ISoundtouchStatus } from './soundtouch.types.js';
3
+ import type { ISoundtouchSnapshot } from './soundtouch.types.js';
4
4
 
5
5
  export class SoundtouchMapper {
6
+ public static deviceId(snapshotArg: Pick<ISoundtouchSnapshot, 'homeId' | 'deviceId'>): string {
7
+ return `soundtouch:${encodeURIComponent(snapshotArg.homeId)}:${snapshotArg.deviceId}`;
8
+ }
9
+ public static mediaEntityId(snapshotArg: Pick<ISoundtouchSnapshot, 'homeId' | 'deviceId'>): string { return `${this.deviceId(snapshotArg)}:speaker`; }
6
10
  public static toDevices(snapshotArg: ISoundtouchSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
7
- const updatedAt = snapshotArg.lastUpdated || new Date().toISOString();
8
- const features: plugins.shxInterfaces.data.IDeviceFeature[] = [
9
- { id: 'playback', capability: 'media', name: 'Playback', readable: true, writable: true },
10
- { id: 'source', capability: 'media', name: 'Source', readable: true, writable: true },
11
- { id: 'volume', capability: 'media', name: 'Volume', readable: true, writable: true, unit: '%' },
12
- { id: 'muted', capability: 'media', name: 'Muted', readable: true, writable: true },
13
- { id: 'presets', capability: 'media', name: 'Presets', readable: true, writable: true },
14
- ];
15
- const state: plugins.shxInterfaces.data.IDeviceState[] = [
16
- { featureId: 'playback', value: this.mediaState(snapshotArg.status), updatedAt },
17
- { featureId: 'source', value: snapshotArg.status?.source || null, updatedAt },
18
- { featureId: 'volume', value: typeof snapshotArg.volume?.actual === 'number' ? snapshotArg.volume.actual : null, updatedAt },
19
- { featureId: 'muted', value: typeof snapshotArg.volume?.muted === 'boolean' ? snapshotArg.volume.muted : null, updatedAt },
20
- { featureId: 'presets', value: snapshotArg.presets?.length || 0, updatedAt },
21
- ];
22
-
23
- if (this.mediaTitle(snapshotArg.status)) {
24
- features.push({ id: 'current_title', capability: 'media', name: 'Current title', readable: true, writable: false });
25
- state.push({ featureId: 'current_title', value: this.mediaTitle(snapshotArg.status) || null, updatedAt });
26
- }
27
-
28
- return [{
29
- id: this.playerDeviceId(snapshotArg),
30
- integrationDomain: 'soundtouch',
31
- name: this.deviceName(snapshotArg),
32
- protocol: 'http',
33
- manufacturer: 'Bose Corporation',
34
- model: snapshotArg.config.type,
35
- online: snapshotArg.available !== false,
36
- features,
37
- state,
38
- metadata: {
39
- deviceId: snapshotArg.config.deviceId,
40
- host: snapshotArg.config.host,
41
- port: snapshotArg.config.port,
42
- macAddress: this.macAddress(snapshotArg),
43
- accountUuid: snapshotArg.config.accountUuid,
44
- moduleType: snapshotArg.config.moduleType,
45
- variant: snapshotArg.config.variant,
46
- softwareVersions: snapshotArg.config.components?.map((componentArg) => componentArg.softwareVersion).filter(Boolean),
47
- },
11
+ const settled = snapshotArg.targetVolume === snapshotArg.actualVolume;
12
+ const optionalCapabilities = snapshotArg.optionalCapabilities.map((capability) => ({ ...capability }));
13
+ return [{ id: this.deviceId(snapshotArg), integrationDomain: 'soundtouch', name: snapshotArg.displayName ?? snapshotArg.name, manufacturer: 'Bose',
14
+ protocol: 'http', online: snapshotArg.online,
15
+ features: [
16
+ { id: 'connectivity', capability: 'sensor', name: 'Connectivity', readable: true, writable: false },
17
+ { id: 'identity', capability: 'sensor', name: 'Speaker identity', readable: true, writable: false },
18
+ { id: 'optional_capabilities', capability: 'sensor', name: 'Declared optional capabilities', readable: true, writable: false },
19
+ { id: 'target_volume', capability: 'media', name: 'Observed target volume', readable: true, writable: false, unit: 'SoundTouch units' },
20
+ { id: 'volume', capability: 'media', name: 'Absolute volume', readable: true, writable: settled, unit: 'SoundTouch units' },
21
+ { id: 'mute', capability: 'media', name: 'Mute', readable: true,
22
+ writable: settled && snapshotArg.capabilities.mute && snapshotArg.actualVolume <= snapshotArg.volumeLimit },
23
+ ],
24
+ state: [
25
+ { featureId: 'connectivity', value: 'online', updatedAt: snapshotArg.observedAt },
26
+ { featureId: 'identity', value: { deviceId: snapshotArg.deviceId, type: snapshotArg.type }, updatedAt: snapshotArg.observedAt },
27
+ { featureId: 'optional_capabilities', value: { capabilities: optionalCapabilities }, updatedAt: snapshotArg.observedAt },
28
+ { featureId: 'target_volume', value: snapshotArg.targetVolume, updatedAt: snapshotArg.observedAt },
29
+ { featureId: 'volume', value: snapshotArg.actualVolume, updatedAt: snapshotArg.observedAt },
30
+ { featureId: 'mute', value: snapshotArg.muted, updatedAt: snapshotArg.observedAt },
31
+ ],
32
+ metadata: { homeId: snapshotArg.homeId, deviceId: snapshotArg.deviceId, type: snapshotArg.type, lanAuthenticated: false,
33
+ observedName: snapshotArg.name, volumeLimit: snapshotArg.volumeLimit, volumeRange: { minimum: 0, maximum: 100, step: 1 },
34
+ ownerWriteVolumeRange: { minimum: 0, maximum: snapshotArg.volumeLimit, step: 1 },
35
+ optionalCapabilities, observedAt: snapshotArg.observedAt, standbySupported: false },
48
36
  }];
49
37
  }
50
-
51
38
  public static toEntities(snapshotArg: ISoundtouchSnapshot): IIntegrationEntity[] {
52
- const base = this.entityBase(snapshotArg);
53
- const entities: IIntegrationEntity[] = [{
54
- id: `media_player.${base}`,
55
- uniqueId: `soundtouch_${this.uniqueBase(snapshotArg)}`,
56
- integrationDomain: 'soundtouch',
57
- deviceId: this.playerDeviceId(snapshotArg),
58
- platform: 'media_player',
59
- name: this.deviceName(snapshotArg),
60
- state: this.mediaState(snapshotArg.status),
61
- attributes: {
62
- deviceClass: 'speaker',
63
- deviceId: snapshotArg.config.deviceId,
64
- model: snapshotArg.config.type,
65
- macAddress: this.macAddress(snapshotArg),
66
- volumeLevel: typeof snapshotArg.volume?.actual === 'number' ? Math.max(0, Math.min(1, snapshotArg.volume.actual / 100)) : undefined,
67
- volume: snapshotArg.volume?.actual,
68
- targetVolume: snapshotArg.volume?.target,
69
- isVolumeMuted: snapshotArg.volume?.muted,
70
- source: snapshotArg.status?.source,
71
- sourceList: snapshotArg.sourceList || [],
72
- presetList: this.presetList(snapshotArg),
73
- mediaContentType: snapshotArg.status?.contentItem?.type || snapshotArg.status?.streamType,
74
- mediaContentId: snapshotArg.status?.contentItem?.location || snapshotArg.status?.trackId,
75
- mediaTitle: this.mediaTitle(snapshotArg.status),
76
- mediaArtist: snapshotArg.status?.artist,
77
- mediaTrack: snapshotArg.status?.track,
78
- mediaAlbumName: snapshotArg.status?.album,
79
- mediaImageUrl: snapshotArg.status?.image,
80
- mediaDuration: snapshotArg.status?.duration,
81
- mediaPosition: snapshotArg.status?.position,
82
- shuffle: snapshotArg.status?.shuffleSetting,
83
- repeat: snapshotArg.status?.repeatSetting,
84
- stationName: snapshotArg.status?.stationName,
85
- stationLocation: snapshotArg.status?.stationLocation,
86
- soundtouchZone: this.zoneAttribute(snapshotArg),
87
- soundtouchGroup: this.groupAttribute(snapshotArg),
88
- },
89
- available: snapshotArg.available !== false,
39
+ return [{ id: this.mediaEntityId(snapshotArg), uniqueId: this.mediaEntityId(snapshotArg), deviceId: this.deviceId(snapshotArg),
40
+ integrationDomain: 'soundtouch', platform: 'media_player', name: snapshotArg.displayName ?? snapshotArg.name, state: 'unknown', available: snapshotArg.online,
41
+ attributes: { type: snapshotArg.type, volume: snapshotArg.actualVolume, targetVolume: snapshotArg.targetVolume, muted: snapshotArg.muted,
42
+ volumeLimit: snapshotArg.volumeLimit, optionalCapabilities: snapshotArg.optionalCapabilities.map((capability) => ({ ...capability })),
43
+ observedAt: snapshotArg.observedAt, standbySupported: false },
90
44
  }];
91
-
92
- entities.push({
93
- id: `sensor.${base}_soundtouch_presets`,
94
- uniqueId: `soundtouch_${this.uniqueBase(snapshotArg)}_presets`,
95
- integrationDomain: 'soundtouch',
96
- deviceId: this.playerDeviceId(snapshotArg),
97
- platform: 'sensor',
98
- name: `${this.deviceName(snapshotArg)} SoundTouch Presets`,
99
- state: snapshotArg.presets?.length || 0,
100
- attributes: { presets: this.presetList(snapshotArg) },
101
- available: snapshotArg.available !== false,
102
- });
103
-
104
- entities.push({
105
- id: `sensor.${base}_soundtouch_sources`,
106
- uniqueId: `soundtouch_${this.uniqueBase(snapshotArg)}_sources`,
107
- integrationDomain: 'soundtouch',
108
- deviceId: this.playerDeviceId(snapshotArg),
109
- platform: 'sensor',
110
- name: `${this.deviceName(snapshotArg)} SoundTouch Sources`,
111
- state: snapshotArg.sourceList?.length || 0,
112
- attributes: { sourceList: snapshotArg.sourceList || [] },
113
- available: snapshotArg.available !== false,
114
- });
115
-
116
- entities.push({
117
- id: `sensor.${base}_soundtouch_media`,
118
- uniqueId: `soundtouch_${this.uniqueBase(snapshotArg)}_media`,
119
- integrationDomain: 'soundtouch',
120
- deviceId: this.playerDeviceId(snapshotArg),
121
- platform: 'sensor',
122
- name: `${this.deviceName(snapshotArg)} SoundTouch Media`,
123
- state: this.mediaTitle(snapshotArg.status) || snapshotArg.status?.source || 'None',
124
- attributes: { status: snapshotArg.status },
125
- available: snapshotArg.available !== false,
126
- });
127
-
128
- return entities;
129
- }
130
-
131
- public static playerDeviceId(snapshotArg: ISoundtouchSnapshot): string {
132
- return `soundtouch.player.${this.uniqueBase(snapshotArg)}`;
133
- }
134
-
135
- public static slug(valueArg: string | undefined): string {
136
- return (valueArg || 'soundtouch').toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '') || 'soundtouch';
137
- }
138
-
139
- private static mediaState(statusArg: ISoundtouchStatus | undefined): string {
140
- if (!statusArg || statusArg.source === 'STANDBY' || statusArg.playStatus === 'STOP_STATE') {
141
- return 'off';
142
- }
143
- if (statusArg.source === 'INVALID_SOURCE') {
144
- return 'unknown';
145
- }
146
- if (statusArg.playStatus === 'PLAY_STATE' || statusArg.playStatus === 'BUFFERING_STATE') {
147
- return 'playing';
148
- }
149
- if (statusArg.playStatus === 'PAUSE_STATE') {
150
- return 'paused';
151
- }
152
- return 'idle';
153
- }
154
-
155
- private static mediaTitle(statusArg: ISoundtouchStatus | undefined): string | undefined {
156
- if (!statusArg) {
157
- return undefined;
158
- }
159
- if (statusArg.stationName) {
160
- return statusArg.stationName;
161
- }
162
- if (statusArg.artist && statusArg.track) {
163
- return `${statusArg.artist} - ${statusArg.track}`;
164
- }
165
- return statusArg.track || statusArg.contentItem?.name || statusArg.description;
166
- }
167
-
168
- private static presetList(snapshotArg: ISoundtouchSnapshot): Array<Record<string, unknown>> {
169
- return (snapshotArg.presets || []).map((presetArg: ISoundtouchPreset) => ({
170
- presetId: presetArg.presetId,
171
- name: presetArg.name,
172
- source: presetArg.source,
173
- type: presetArg.type,
174
- location: presetArg.location,
175
- sourceAccount: presetArg.sourceAccount,
176
- isPresetable: presetArg.isPresetable,
177
- }));
178
- }
179
-
180
- private static zoneAttribute(snapshotArg: ISoundtouchSnapshot): Record<string, unknown> | undefined {
181
- if (!snapshotArg.zone) {
182
- return undefined;
183
- }
184
- return {
185
- master: snapshotArg.zone.masterId,
186
- masterIp: snapshotArg.zone.masterIp,
187
- isMaster: snapshotArg.zone.isMaster,
188
- slaves: snapshotArg.zone.slaves,
189
- };
190
- }
191
-
192
- private static groupAttribute(snapshotArg: ISoundtouchSnapshot): string[] | undefined {
193
- if (!snapshotArg.zone) {
194
- return undefined;
195
- }
196
- return [
197
- snapshotArg.zone.masterId || snapshotArg.zone.masterIp,
198
- ...snapshotArg.zone.slaves.map((slaveArg) => slaveArg.deviceId || slaveArg.ipAddress),
199
- ].filter((valueArg): valueArg is string => Boolean(valueArg));
200
- }
201
-
202
- private static deviceName(snapshotArg: ISoundtouchSnapshot): string {
203
- return snapshotArg.config.name || snapshotArg.config.type || 'Bose SoundTouch';
204
- }
205
-
206
- private static entityBase(snapshotArg: ISoundtouchSnapshot): string {
207
- return this.slug(this.deviceName(snapshotArg));
208
- }
209
-
210
- private static uniqueBase(snapshotArg: ISoundtouchSnapshot): string {
211
- return this.slug(snapshotArg.config.deviceId || this.macAddress(snapshotArg) || snapshotArg.config.host || this.deviceName(snapshotArg));
212
- }
213
-
214
- private static macAddress(snapshotArg: ISoundtouchSnapshot): string | undefined {
215
- return snapshotArg.config.networks?.find((networkArg) => networkArg.type === 'SMSC')?.macAddress || snapshotArg.config.networks?.[0]?.macAddress;
216
45
  }
217
46
  }
@@ -1,199 +1,54 @@
1
- export const soundtouchDefaultPort = 8090;
2
- export const soundtouchDefaultDlnaPort = 8091;
3
- export const soundtouchDefaultSourceList = ['AUX', 'BLUETOOTH'] as const;
4
-
5
- export type TSoundtouchPlayStatus = 'PLAY_STATE' | 'BUFFERING_STATE' | 'PAUSE_STATE' | 'STOP_STATE' | string;
6
-
7
- export type TSoundtouchSource =
8
- | 'SLAVE_SOURCE'
9
- | 'INTERNET_RADIO'
10
- | 'PANDORA'
11
- | 'AIRPLAY'
12
- | 'STORED_MUSIC'
13
- | 'AUX'
14
- | 'OFF_SOURCE'
15
- | 'CURRATED_RADIO'
16
- | 'STANDBY'
17
- | 'UPDATE'
18
- | 'DEEZER'
19
- | 'SPOTIFY'
20
- | 'IHEART'
21
- | 'LOCAL_MUSIC'
22
- | 'BLUETOOTH'
23
- | 'INVALID_SOURCE'
24
- | string;
25
-
26
- export type TSoundtouchContentType = 'uri' | 'track' | 'album' | 'playlist' | string;
27
-
28
- export type TSoundtouchCommand =
29
- | 'turn_on'
30
- | 'turn_off'
31
- | 'play'
32
- | 'pause'
33
- | 'play_pause'
34
- | 'previous_track'
35
- | 'next_track'
36
- | 'volume_up'
37
- | 'volume_down'
38
- | 'set_volume'
39
- | 'mute'
40
- | 'select_source'
41
- | 'play_media'
42
- | 'play_preset';
1
+ import type * as plugins from '../../plugins.js';
2
+ import type { INativeConfigFlowContext, INativeIntegrationSetupContext } from '../../core/types.js';
3
+ import type { INativeIntegrationOwnerOptions } from '../../core/classes.nativeintegrationowner.js';
43
4
 
5
+ export const soundtouchDomain = 'soundtouch';
6
+ export const soundtouchDefaultPort = 8090;
7
+ export const soundtouchDefaultTimeoutMs = 5000;
8
+ export interface ISoundtouchHomeOptions extends INativeIntegrationOwnerOptions {}
9
+ export interface ISoundtouchSetupContext extends INativeIntegrationSetupContext {}
10
+ export interface ISoundtouchConfigFlowContext extends INativeConfigFlowContext {}
44
11
  export interface ISoundtouchConfig {
45
- host?: string;
46
- port?: number;
47
- dlnaPort?: number;
48
- name?: string;
49
- deviceId?: string;
50
- model?: string;
51
- macAddress?: string;
52
- sourceList?: string[];
53
- timeoutMs?: number;
54
- snapshot?: ISoundtouchSnapshot;
55
- commandExecutor?: ISoundtouchCommandExecutor;
12
+ readonly homeId: string;
13
+ readonly host: string;
14
+ /** Exact uppercase unicast deviceID previously observed on the selected device. */
15
+ readonly deviceId: string;
16
+ /** Documented /info type; the protocol supplies no model field. */
17
+ readonly type: string;
18
+ readonly allowUnencryptedLan: true;
19
+ /** Owner's absolute integer volume ceiling, 0–100. */
20
+ readonly volumeLimit: number;
21
+ readonly port?: number;
22
+ /** Optional local display label; observed /info name remains in the snapshot. */
23
+ readonly name?: string;
24
+ readonly requestTimeoutMs?: number;
25
+ readonly maxPendingOperations?: number;
26
+ readonly maxResponseBytes?: number;
56
27
  }
57
-
58
28
  export interface IHomeAssistantSoundtouchConfig extends ISoundtouchConfig {}
59
-
60
- export interface ISoundtouchCommandExecutor {
61
- execute(requestArg: ISoundtouchRawCommandRequest): Promise<unknown>;
62
- }
63
-
64
- export interface ISoundtouchRawCommandRequest {
65
- command: TSoundtouchCommand | 'refresh';
66
- method: 'GET' | 'POST';
67
- path: string;
68
- url: string;
69
- host?: string;
70
- port: number;
71
- body?: string;
72
- headers?: Record<string, string>;
73
- }
74
-
75
- export interface ISoundtouchCommandRequest {
76
- command: TSoundtouchCommand;
77
- source?: string;
78
- presetId?: string;
79
- presetName?: string;
80
- mediaId?: string;
81
- mediaType?: string;
82
- url?: string;
83
- volumeLevel?: number;
84
- volume?: number;
85
- muted?: boolean;
86
- contentItem?: ISoundtouchContentItem;
87
- }
88
-
89
- export interface ISoundtouchSnapshot {
90
- config: ISoundtouchDeviceInfo;
91
- status?: ISoundtouchStatus;
92
- volume?: ISoundtouchVolume;
93
- presets?: ISoundtouchPreset[];
94
- zone?: ISoundtouchZoneStatus;
95
- sourceList?: string[];
96
- available?: boolean;
97
- lastUpdated?: string;
98
- error?: string;
99
- }
100
-
101
- export interface ISoundtouchDeviceInfo {
102
- deviceId?: string;
103
- name?: string;
104
- type?: string;
105
- accountUuid?: string;
106
- moduleType?: string;
107
- variant?: string;
108
- variantMode?: string;
109
- countryCode?: string;
110
- regionCode?: string;
111
- host?: string;
112
- port?: number;
113
- networks?: ISoundtouchNetworkInfo[];
114
- components?: ISoundtouchComponentInfo[];
115
- }
116
-
117
- export interface ISoundtouchNetworkInfo {
118
- type?: string;
119
- macAddress?: string;
120
- ipAddress?: string;
121
- }
122
-
123
- export interface ISoundtouchComponentInfo {
124
- category?: string;
125
- softwareVersion?: string;
126
- serialNumber?: string;
127
- }
128
-
129
- export interface ISoundtouchContentItem {
130
- name?: string;
131
- source?: TSoundtouchSource;
132
- type?: TSoundtouchContentType;
133
- location?: string;
134
- sourceAccount?: string;
135
- isPresetable?: boolean;
136
- }
137
-
138
- export interface ISoundtouchStatus {
139
- source?: TSoundtouchSource;
140
- contentItem?: ISoundtouchContentItem;
141
- track?: string;
142
- artist?: string;
143
- album?: string;
144
- image?: string;
145
- duration?: number;
146
- position?: number;
147
- playStatus?: TSoundtouchPlayStatus;
148
- shuffleSetting?: string;
149
- repeatSetting?: string;
150
- streamType?: string;
151
- trackId?: string;
152
- stationName?: string;
153
- description?: string;
154
- stationLocation?: string;
155
- }
156
-
157
- export interface ISoundtouchVolume {
158
- actual?: number;
159
- target?: number;
160
- muted?: boolean;
161
- }
162
-
163
- export interface ISoundtouchPreset extends ISoundtouchContentItem {
164
- presetId: string;
165
- sourceXml?: string;
166
- }
167
-
168
- export interface ISoundtouchZoneStatus {
169
- masterId?: string;
170
- masterIp?: string;
171
- isMaster?: boolean;
172
- slaves: ISoundtouchZoneMember[];
173
- }
174
-
175
- export interface ISoundtouchZoneMember {
176
- deviceId?: string;
177
- ipAddress?: string;
178
- role?: string;
179
- }
180
-
181
- export interface ISoundtouchMdnsRecord {
182
- name?: string;
183
- type?: string;
184
- host?: string;
185
- port?: number;
186
- txt?: Record<string, string | undefined>;
187
- }
188
-
189
- export interface ISoundtouchManualEntry {
190
- host?: string;
191
- port?: number;
192
- id?: string;
193
- name?: string;
194
- manufacturer?: string;
195
- model?: string;
196
- deviceId?: string;
197
- macAddress?: string;
198
- metadata?: Record<string, unknown>;
199
- }
29
+ export interface ISoundtouchSnapshot extends plugins.ecobridgeDeviceManager.IBoseSoundTouchState {
30
+ readonly homeId: string;
31
+ readonly displayName?: string;
32
+ readonly volumeLimit: number;
33
+ readonly online: true;
34
+ }
35
+ export type TSoundtouchRawData = ISoundtouchSnapshot;
36
+ export type TSoundtouchClientEvent =
37
+ | { readonly type: 'snapshot'; readonly snapshot: ISoundtouchSnapshot }
38
+ | { readonly type: 'control'; readonly observation: plugins.ecobridgeDeviceManager.IBoseSoundTouchControlResult }
39
+ | { readonly type: 'error'; readonly error: Error };
40
+ export const soundtouchProfile = {
41
+ domain: soundtouchDomain, displayName: 'Bose SoundTouch', status: 'control-runtime',
42
+ metadata: {
43
+ source: 'home-assistant/core', upstreamDomain: soundtouchDomain, configFlow: true,
44
+ localApi: {
45
+ status: 'Selected native SoundTouch HTTP/XML through devicemanager.',
46
+ implemented: ['exact selected home/private IPv4/deviceID/type binding', 'live sanitized identity, optional capability declarations and actual volume/mute',
47
+ 'fresh settled-volume admission and owner-bounded absolute volume/mute', 'status acknowledgment and independent state observation without causal claims',
48
+ 'caller-owned optional home/domain config store', 'explicit observation events and joined terminal cleanup'],
49
+ explicitUnsupported: ['automatic discovery', 'cloud access or fallback', 'injected clients, snapshots and executors',
50
+ 'power, standby, key/toggle/increment, source, preset, media and zone controls', 'defined command-success vocabulary',
51
+ 'automatic retry, replay, reconnect or push subscription', 'cryptographic LAN peer authentication'],
52
+ },
53
+ },
54
+ } as const;
@@ -0,0 +1,60 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import type { ISoundtouchConfig } from './soundtouch.types.js';
3
+ import { soundtouchDefaultPort, soundtouchDefaultTimeoutMs } from './soundtouch.types.js';
4
+
5
+ export function soundtouchRecord(valueArg: unknown, nameArg: string): Record<string, unknown> {
6
+ if (!valueArg || typeof valueArg !== 'object' || Array.isArray(valueArg)
7
+ || ![Object.prototype, null].includes(Object.getPrototypeOf(valueArg))) throw new TypeError(`SoundTouch ${nameArg} must be a plain data object.`);
8
+ const result: Record<string, unknown> = {};
9
+ for (const key of Reflect.ownKeys(valueArg)) {
10
+ const descriptor = Object.getOwnPropertyDescriptor(valueArg, key);
11
+ if (typeof key !== 'string' || !descriptor || !('value' in descriptor) || !descriptor.enumerable) {
12
+ throw new TypeError(`SoundTouch ${nameArg} may not contain symbols, accessors or hidden fields.`);
13
+ }
14
+ Object.defineProperty(result, key, { value: descriptor.value, enumerable: true });
15
+ }
16
+ return result;
17
+ }
18
+ export function soundtouchKeys(valueArg: Record<string, unknown>, keysArg: readonly string[]): void {
19
+ if (Object.keys(valueArg).some((key) => !keysArg.includes(key))) throw new TypeError('SoundTouch data contains unsupported fields.');
20
+ }
21
+ export function soundtouchText(valueArg: unknown, nameArg: string, maximumArg = 128): string {
22
+ if (typeof valueArg !== 'string' || !valueArg.trim() || valueArg.length > maximumArg || /[\p{Cc}\p{Cs}]/u.test(valueArg)) {
23
+ throw new TypeError(`SoundTouch ${nameArg} must be a non-empty string without control characters, at most ${maximumArg} characters.`);
24
+ }
25
+ return valueArg;
26
+ }
27
+ export function soundtouchInteger(valueArg: unknown, minimumArg: number, maximumArg: number, nameArg: string): number {
28
+ if (typeof valueArg !== 'number' || !Number.isSafeInteger(valueArg) || valueArg < minimumArg || valueArg > maximumArg) {
29
+ throw new RangeError(`SoundTouch ${nameArg} must be an integer from ${minimumArg} through ${maximumArg}.`);
30
+ }
31
+ return valueArg;
32
+ }
33
+ export function soundtouchBoolean(valueArg: unknown): boolean {
34
+ if (typeof valueArg !== 'boolean') throw new TypeError('SoundTouch mute requires an explicit boolean.');
35
+ return valueArg;
36
+ }
37
+ export function readSoundtouchEndpoint(hostArg: unknown, idArg: unknown, typeArg: unknown, portArg?: unknown): Pick<ISoundtouchConfig, 'host' | 'deviceId' | 'type'> & { port: number } {
38
+ if (typeof hostArg !== 'string' || plugins.net.isIP(hostArg) !== 4) throw new TypeError('SoundTouch host must be a selected private IPv4 address.');
39
+ const [first, second, , last] = hostArg.split('.').map(Number);
40
+ if (!(first === 10 || first === 127 || first === 192 && second === 168 || first === 172 && second >= 16 && second <= 31)
41
+ || last === 0 || last === 255) throw new TypeError('SoundTouch host must be a selected private or loopback unicast IPv4 address.');
42
+ if (typeof idArg !== 'string' || !/^[0-9A-F]{12}$/.test(idArg) || /^(0{12}|F{12})$/.test(idArg)
43
+ || (Number.parseInt(idArg.slice(0, 2), 16) & 1) !== 0) throw new TypeError('SoundTouch deviceId must be the exact uppercase unicast twelve-digit deviceID.');
44
+ return { host: hostArg, deviceId: idArg, type: soundtouchText(typeArg, 'selected type'),
45
+ port: soundtouchInteger(portArg === undefined ? soundtouchDefaultPort : portArg, 1, 65535, 'port') };
46
+ }
47
+ export function normalizeSoundtouchConfig(valueArg: unknown) {
48
+ const values = soundtouchRecord(valueArg, 'configuration');
49
+ soundtouchKeys(values, ['homeId', 'host', 'deviceId', 'type', 'allowUnencryptedLan', 'volumeLimit', 'port', 'name',
50
+ 'requestTimeoutMs', 'maxPendingOperations', 'maxResponseBytes']);
51
+ if (values.allowUnencryptedLan !== true) throw new TypeError('SoundTouch requires explicit allowUnencryptedLan: true admission.');
52
+ return Object.freeze({ homeId: soundtouchText(values.homeId, 'homeId', 256),
53
+ ...readSoundtouchEndpoint(values.host, values.deviceId, values.type, values.port), allowUnencryptedLan: true as const,
54
+ volumeLimit: soundtouchInteger(values.volumeLimit, 0, 100, 'volumeLimit'),
55
+ ...(values.name === undefined ? {} : { name: soundtouchText(values.name, 'name') }),
56
+ requestTimeoutMs: soundtouchInteger(values.requestTimeoutMs === undefined ? soundtouchDefaultTimeoutMs : values.requestTimeoutMs, 1, 30000, 'requestTimeoutMs'),
57
+ maxPendingOperations: soundtouchInteger(values.maxPendingOperations === undefined ? 16 : values.maxPendingOperations, 1, 128, 'maxPendingOperations'),
58
+ maxResponseBytes: soundtouchInteger(values.maxResponseBytes === undefined ? 16384 : values.maxResponseBytes, 256, 65536, 'maxResponseBytes'),
59
+ });
60
+ }
@@ -4,3 +4,4 @@ export * from './wiz.classes.integration.js';
4
4
  export * from './wiz.discovery.js';
5
5
  export * from './wiz.mapper.js';
6
6
  export * from './wiz.types.js';
7
+ export * from './wiz.validation.js';