@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,52 +1,81 @@
1
- import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
2
- import type { IModbusConfig } from './modbus.types.js';
3
-
4
- const defaultModbusTcpPort = 502;
1
+ import { NativeIntegrationOwner, type NativeIntegrationScope } from '../../core/classes.nativeintegrationowner.js';
2
+ import type { IConfigFlow, IConfigFlowStep, IDiscoveryCandidate, INativeConfigFlowContext } from '../../core/types.js';
3
+ import { ModbusClient } from './modbus.classes.client.js';
4
+ import { ModbusMapper } from './modbus.mapper.js';
5
+ import type { IModbusConfig, IModbusIntegrationOptions } from './modbus.types.js';
6
+ import { modbusRecord, normalizeModbusConfig } from './modbus.validation.js';
5
7
 
6
8
  export class ModbusConfigFlow implements IConfigFlow<IModbusConfig> {
7
- public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IModbusConfig>> {
8
- void contextArg;
9
+ readonly #scope: NativeIntegrationScope;
10
+
11
+ constructor(optionsArg: IModbusIntegrationOptions = {}, scopeArg?: NativeIntegrationScope) {
12
+ this.#scope = scopeArg ?? new NativeIntegrationOwner('modbus', optionsArg).createFlowScope();
13
+ }
14
+
15
+ public async start(candidateArg: IDiscoveryCandidate, contextArg: INativeConfigFlowContext): Promise<IConfigFlowStep<IModbusConfig>> {
16
+ const candidate = modbusRecord(candidateArg);
17
+ if (candidate.source !== 'manual' || candidate.integrationDomain !== undefined && candidate.integrationDomain !== 'modbus') {
18
+ throw new TypeError('Modbus config admission accepts explicit manual candidates only.');
19
+ }
20
+ const metadata = candidate.metadata === undefined ? {} : modbusRecord(candidate.metadata);
21
+ const context = this.#scope.admit(contextArg);
9
22
  return {
10
- kind: 'form',
11
- title: 'Connect Modbus TCP hub',
12
- description: 'Configure a known Modbus TCP endpoint. Serial RTU ports are intentionally not guessed or probed.',
23
+ kind: 'form', title: 'Select Modbus TCP device',
24
+ description: 'Select the private IPv4 endpoint, unit, vendor and product for the host-selected home. Supply an explicit JSON array of zero-based raw read mappings; writes require single-address permission. Identity strings are unauthenticated LAN observations.',
13
25
  fields: [
14
- { name: 'name', label: 'Hub name', type: 'text', required: false },
15
- { name: 'host', label: 'Host', type: 'text', required: true },
16
- { name: 'port', label: 'TCP port', type: 'number', required: false },
17
- { name: 'unitId', label: 'Default unit ID', type: 'number', required: false },
18
- { name: 'timeoutMs', label: 'Timeout milliseconds', type: 'number', required: false },
26
+ { name: 'address', label: 'Selected local IPv4 address', type: 'text', required: true },
27
+ { name: 'port', label: 'TCP port', type: 'number' },
28
+ { name: 'unitId', label: 'Selected unit ID', type: 'number', required: true },
29
+ { name: 'expectedVendorName', label: 'Exact vendor name', type: 'text', required: true },
30
+ { name: 'expectedProductCode', label: 'Exact product code', type: 'text', required: true },
31
+ { name: 'expectedRevision', label: 'Optional exact revision', type: 'text' },
32
+ { name: 'allowUnencryptedLan', label: 'Accept unencrypted LAN transport', type: 'boolean', required: true },
33
+ { name: 'mappings', label: 'Owner raw mappings JSON', type: 'text', required: true },
34
+ { name: 'name', label: 'Display name', type: 'text' },
19
35
  ],
20
- submit: async (valuesArg) => {
21
- const unitId = this.numberValue(valuesArg.unitId) || this.numberValue(candidateArg.metadata?.unitId) || 1;
22
- const port = this.numberValue(valuesArg.port) || candidateArg.port || defaultModbusTcpPort;
23
- return {
24
- kind: 'done',
25
- title: 'Modbus TCP hub configured',
26
- config: {
27
- hubs: [{
28
- id: candidateArg.id,
29
- name: this.stringValue(valuesArg.name) || candidateArg.name || 'modbus_hub',
30
- type: 'tcp',
31
- host: this.stringValue(valuesArg.host) || candidateArg.host || '',
32
- port,
33
- timeoutMs: this.numberValue(valuesArg.timeoutMs) || 5000,
34
- slave: unitId,
35
- manufacturer: candidateArg.manufacturer,
36
- model: candidateArg.model,
37
- serialNumber: candidateArg.serialNumber,
38
- }],
39
- },
40
- };
41
- },
36
+ submit: (valuesArg) => this.submit(valuesArg, candidate, metadata, context),
42
37
  };
43
38
  }
44
39
 
45
- private stringValue(valueArg: unknown): string | undefined {
46
- return typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim() : undefined;
47
- }
40
+ public destroy(): Promise<void> { return this.#scope.close(); }
41
+ public retryCleanup(): Promise<void> { return this.#scope.retryCleanup(); }
48
42
 
49
- private numberValue(valueArg: unknown): number | undefined {
50
- return typeof valueArg === 'number' && Number.isFinite(valueArg) ? valueArg : typeof valueArg === 'string' && valueArg.trim() && Number.isFinite(Number(valueArg)) ? Number(valueArg) : undefined;
43
+ private async submit(valuesArg: Record<string, unknown>, candidateArg: Record<string, unknown>, metadataArg: Record<string, unknown>,
44
+ contextArg: INativeConfigFlowContext): Promise<IConfigFlowStep<IModbusConfig>> {
45
+ try {
46
+ const values = modbusRecord(valuesArg, ['homeId', 'address', 'port', 'unitId', 'expectedVendorName', 'expectedProductCode', 'expectedRevision',
47
+ 'allowUnencryptedLan', 'mappings', 'name', 'localAddress', 'timeoutMs', 'maxQueuedOperations']);
48
+ if (values.homeId !== undefined && values.homeId !== contextArg.homeId) throw new Error('Modbus submitted home conflicts with the host-selected home.');
49
+ let mappings: unknown = values.mappings;
50
+ if (typeof mappings === 'string') {
51
+ if (mappings.length > 32768) throw new TypeError('Modbus mapping JSON exceeds its size bound.');
52
+ mappings = JSON.parse(mappings);
53
+ }
54
+ const config = normalizeModbusConfig({ ...values, homeId: contextArg.homeId, mappings,
55
+ address: values.address === undefined ? candidateArg.host : values.address,
56
+ port: values.port === undefined ? candidateArg.port : values.port,
57
+ unitId: values.unitId === undefined ? metadataArg.unitId : values.unitId,
58
+ expectedVendorName: values.expectedVendorName === undefined ? metadataArg.expectedVendorName : values.expectedVendorName,
59
+ expectedProductCode: values.expectedProductCode === undefined ? metadataArg.expectedProductCode : values.expectedProductCode,
60
+ expectedRevision: values.expectedRevision === undefined ? metadataArg.expectedRevision : values.expectedRevision });
61
+ return await this.#scope.run(contextArg, config.homeId, async (leaseArg) => {
62
+ const client = new ModbusClient(config, leaseArg.signal);
63
+ let cleanupAttempted = false;
64
+ leaseArg.own(() => {
65
+ if (cleanupAttempted) return client.retryCleanup();
66
+ cleanupAttempted = true;
67
+ return client.destroy();
68
+ });
69
+ await client.getSnapshot(leaseArg.signal);
70
+ leaseArg.assertOpen();
71
+ await client.destroy();
72
+ leaseArg.assertOpen();
73
+ if (leaseArg.context.configStore) await leaseArg.context.configStore.set(`device:${ModbusMapper.deviceId(config)}`, config);
74
+ leaseArg.assertOpen();
75
+ return { kind: 'done', title: 'Selected Modbus device and read mappings verified', config };
76
+ });
77
+ } catch (errorArg) {
78
+ return { kind: 'error', title: 'Modbus admission failed', error: errorArg instanceof Error ? errorArg.message : 'Modbus admission failed.' };
79
+ }
51
80
  }
52
81
  }
@@ -1,96 +1,151 @@
1
- import type * as shxInterfaces from '@smarthome.exchange/interfaces';
1
+ import * as plugins from '../../plugins.js';
2
2
  import { BaseIntegration } from '../../core/classes.baseintegration.js';
3
- import type { IIntegrationEntity, IIntegrationEvent, IIntegrationRuntime, IIntegrationSetupContext, IServiceCallRequest, IServiceCallResult } from '../../core/types.js';
3
+ import { NativeIntegrationOwner, type INativeSetupLease } from '../../core/classes.nativeintegrationowner.js';
4
+ import type { IDeviceFeatureWriteRequest, IIntegrationEntity, IIntegrationEvent, IIntegrationRuntime, INativeIntegrationSetupContext, IServiceCallRequest, IServiceCallResult } from '../../core/types.js';
4
5
  import { ModbusClient } from './modbus.classes.client.js';
5
6
  import { ModbusConfigFlow } from './modbus.classes.configflow.js';
6
7
  import { createModbusDiscoveryDescriptor } from './modbus.discovery.js';
7
8
  import { ModbusMapper } from './modbus.mapper.js';
8
- import type { IModbusConfig } from './modbus.types.js';
9
+ import type { IModbusConfig, IModbusIntegrationOptions, IModbusSnapshot, TModbusCommand } from './modbus.types.js';
10
+ import { normalizeModbusConfig } from './modbus.validation.js';
9
11
 
10
12
  export class ModbusIntegration extends BaseIntegration<IModbusConfig> {
11
13
  public readonly domain = 'modbus';
12
14
  public readonly displayName = 'Modbus';
13
15
  public readonly status = 'control-runtime' as const;
14
16
  public readonly discoveryDescriptor = createModbusDiscoveryDescriptor();
15
- public readonly configFlow = new ModbusConfigFlow();
17
+ public readonly configFlow: ModbusConfigFlow;
16
18
  public readonly metadata = {
17
- source: 'home-assistant/core',
18
- upstreamPath: 'homeassistant/components/modbus',
19
- upstreamDomain: 'modbus',
20
- iotClass: 'local_polling',
21
- requirements: ['pymodbus==3.11.2'],
22
- dependencies: [],
23
- afterDependencies: [],
24
- codeowners: [],
25
- documentation: 'https://www.home-assistant.io/integrations/modbus',
26
- configFlow: true,
27
- discovery: {
28
- manual: true,
29
- tcp: true,
30
- serial: false,
31
- note: 'Manual Modbus TCP setup is implemented. Serial RTU ports are not guessed or probed.',
32
- },
33
- runtime: {
34
- type: 'control-runtime',
35
- polling: 'local snapshot or explicit Modbus TCP requests',
36
- services: ['read_coil', 'read_register', 'write_coil', 'write_register', 'stop', 'restart', 'refresh'],
37
- },
19
+ source: 'native', upstreamDomain: 'modbus',
20
+ runtime: { type: 'control-runtime', polling: 'explicit owner reads', services: ['read', 'write_coil', 'write_register'] },
38
21
  localApi: {
39
- implemented: [
40
- 'manual Modbus TCP hub configuration',
41
- 'snapshot/manual configured registers and coils',
42
- 'Modbus TCP MBAP request shape for read coils, read discrete inputs, read holding/input registers, write coil(s), and write register(s)',
43
- 'register/coils mapping to sensor, binary_sensor, switch, and number entities',
44
- ],
45
- explicitUnsupported: [
46
- 'unsafe serial RTU discovery or serial port guessing',
47
- 'serial RTU runtime transport',
48
- 'RTU-over-TCP framer runtime transport',
49
- 'UDP runtime transport',
50
- 'climate, cover, fan, and light platform behavior beyond configured register/coil entities',
51
- ],
22
+ implemented: ['selected home/IPv4/unit/vendor/product', 'fresh basic identity on every operation', 'explicit raw bit/register read mappings',
23
+ 'default-deny single absolute FC5/6 writes', 'bounded devicemanager admission and terminal cleanup'],
24
+ explicitUnsupported: ['automatic discovery', 'serial RTU', 'UDP', 'RTU-over-TCP', 'register-map inference', 'scaling or semantic device capabilities',
25
+ 'multi-address writes', 'toggles', 'automatic retries or replay', 'cryptographic device authentication', 'physical effect confirmation'],
52
26
  },
53
27
  };
28
+ readonly #owner: NativeIntegrationOwner;
54
29
 
55
- public async setup(configArg: IModbusConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
56
- void contextArg;
57
- return new ModbusRuntime(new ModbusClient(configArg));
30
+ constructor(optionsArg: IModbusIntegrationOptions = {}) {
31
+ super();
32
+ this.#owner = new NativeIntegrationOwner(this.domain, optionsArg);
33
+ this.configFlow = new ModbusConfigFlow({}, this.#owner.createFlowScope());
58
34
  }
59
35
 
60
- public async destroy(): Promise<void> {}
36
+ public setup(configArg: IModbusConfig, contextArg: INativeIntegrationSetupContext): Promise<IIntegrationRuntime> {
37
+ try {
38
+ const config = normalizeModbusConfig(configArg);
39
+ return this.#owner.runSetup(contextArg, config.homeId, async (leaseArg) => {
40
+ const client = new ModbusClient(config, leaseArg.signal);
41
+ let cleanupAttempted = false;
42
+ leaseArg.own(() => {
43
+ if (cleanupAttempted) return client.retryCleanup();
44
+ cleanupAttempted = true;
45
+ return client.destroy();
46
+ });
47
+ const snapshot = await client.getSnapshot(leaseArg.signal);
48
+ leaseArg.assertOpen();
49
+ const runtime = new ModbusRuntime(client, snapshot, leaseArg);
50
+ leaseArg.promoteToRuntime();
51
+ return runtime;
52
+ });
53
+ } catch (errorArg) { return Promise.reject(errorArg); }
54
+ }
55
+
56
+ public destroy(): Promise<void> { return this.#owner.close(); }
57
+ public retryCleanup(): Promise<void> { return this.#owner.retryCleanup(); }
61
58
  }
62
59
 
63
60
  export class HomeAssistantModbusIntegration extends ModbusIntegration {}
64
61
 
65
62
  class ModbusRuntime implements IIntegrationRuntime {
66
- public domain = 'modbus';
67
-
68
- constructor(private readonly client: ModbusClient) {}
63
+ public readonly domain = 'modbus';
64
+ readonly #subscriptions = new Set<{ handler: (event: IIntegrationEvent) => void; release: () => void }>();
65
+ readonly #ownerAbort = (): void => { void this.destroy().then(() => undefined, () => undefined); };
66
+ #closed = false;
67
+ #closing?: Promise<void>;
69
68
 
70
- public async devices(): Promise<shxInterfaces.data.IDeviceDefinition[]> {
71
- return ModbusMapper.toDevices(await this.client.getSnapshot());
69
+ constructor(private readonly client: ModbusClient, private snapshot: IModbusSnapshot, private readonly lease: INativeSetupLease) {
70
+ lease.signal.addEventListener('abort', this.#ownerAbort, { once: true });
71
+ if (lease.signal.aborted) this.#ownerAbort();
72
72
  }
73
+ public isHealthy(): boolean { return !this.#closed && this.client.isHealthy(); }
73
74
 
74
- public async entities(): Promise<IIntegrationEntity[]> {
75
- return ModbusMapper.toEntities(await this.client.getSnapshot());
75
+ public async devices(signalArg?: AbortSignal): Promise<plugins.shxInterfaces.data.IDeviceDefinition[]> {
76
+ return ModbusMapper.toDevices(await this.refresh(signalArg));
76
77
  }
77
78
 
78
- public async subscribe(handlerArg: (eventArg: IIntegrationEvent) => void): Promise<() => Promise<void>> {
79
- const unsubscribe = this.client.onEvent((eventArg) => handlerArg(ModbusMapper.toIntegrationEvent(eventArg)));
80
- return async () => unsubscribe();
79
+ public async entities(): Promise<IIntegrationEntity[]> { return ModbusMapper.toEntities(await this.refresh()); }
80
+
81
+ /** Passive observations only: subscribing never starts reads or a polling timer. */
82
+ public async subscribe(handlerArg: (eventArg: IIntegrationEvent) => void, signalArg?: AbortSignal): Promise<() => Promise<void>> {
83
+ this.assertOpen();
84
+ if (typeof handlerArg !== 'function') throw new TypeError('Modbus subscription handler must be a function.');
85
+ if (signalArg !== undefined && !(signalArg instanceof AbortSignal)) throw new TypeError('Modbus subscription signal must be an AbortSignal.');
86
+ signalArg?.throwIfAborted();
87
+ const entry = { handler: handlerArg, release: (): void => { this.#subscriptions.delete(entry); signalArg?.removeEventListener('abort', entry.release); } };
88
+ this.#subscriptions.add(entry);
89
+ signalArg?.addEventListener('abort', entry.release, { once: true });
90
+ if (signalArg?.aborted) entry.release();
91
+ return async () => entry.release();
81
92
  }
82
93
 
83
94
  public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
84
- const snapshot = await this.client.getSnapshot();
85
- const command = ModbusMapper.commandForService(snapshot, requestArg);
86
- if (!command) {
87
- return { success: false, error: `Unsupported Modbus service: ${requestArg.domain}.${requestArg.service}` };
95
+ try { this.assertOpen(); return await this.execute(ModbusMapper.commandForService(this.client.config, requestArg)); }
96
+ catch (errorArg) { return this.failure(errorArg); }
97
+ }
98
+
99
+ public async writeFeature(requestArg: IDeviceFeatureWriteRequest, signalArg?: AbortSignal): Promise<IServiceCallResult> {
100
+ try { this.assertOpen(); return await this.execute(ModbusMapper.commandForFeature(this.client.config, requestArg), signalArg); }
101
+ catch (errorArg) { return this.failure(errorArg); }
102
+ }
103
+
104
+ public destroy(): Promise<void> {
105
+ if (this.#closing) return this.#closing;
106
+ this.#closed = true;
107
+ this.#closing = Promise.resolve().then(() => this.lease.close());
108
+ this.lease.signal.removeEventListener('abort', this.#ownerAbort);
109
+ for (const entry of [...this.#subscriptions]) entry.release();
110
+ return this.#closing;
111
+ }
112
+
113
+ private async refresh(signalArg?: AbortSignal): Promise<IModbusSnapshot> {
114
+ this.assertOpen();
115
+ const snapshot = await this.client.getSnapshot(signalArg);
116
+ this.assertOpen();
117
+ this.snapshot = snapshot;
118
+ for (const entity of ModbusMapper.toEntities(snapshot)) this.notify({ type: 'state_changed', integrationDomain: 'modbus',
119
+ deviceId: entity.deviceId, entityId: entity.id, timestamp: Date.parse(snapshot.observedAt), data: { entity } });
120
+ return snapshot;
121
+ }
122
+
123
+ private async execute(commandArg: TModbusCommand, signalArg?: AbortSignal): Promise<IServiceCallResult> {
124
+ const result = await this.client.sendCommand(commandArg, signalArg);
125
+ if ('mapping' in result) {
126
+ this.assertOpen();
127
+ const snapshot = { ...this.snapshot, identity: result.identity, observations: [result], observedAt: result.observedAt };
128
+ const entity = ModbusMapper.toEntities(snapshot)[0];
129
+ this.notify({ type: 'state_changed', integrationDomain: 'modbus', deviceId: entity.deviceId, entityId: entity.id,
130
+ timestamp: Date.parse(result.observedAt), data: { entity } });
88
131
  }
89
- const result = await this.client.sendCommand(command);
90
- return { success: result.success, error: result.error, data: result.data };
132
+ return { success: true, data: result };
91
133
  }
92
134
 
93
- public async destroy(): Promise<void> {
94
- await this.client.destroy();
135
+ private failure(errorArg: unknown): IServiceCallResult {
136
+ return { success: false, error: errorArg instanceof Error ? errorArg.message : 'Modbus operation failed.',
137
+ data: { physicalEffectConfirmed: false, effectOutcome: errorArg instanceof plugins.ecobridgeDeviceManager.ModbusProtocolError ? errorArg.effectOutcome : 'not-sent',
138
+ ...(errorArg instanceof plugins.ecobridgeDeviceManager.ModbusProtocolError ? { errorCode: errorArg.code } : {}) } };
139
+ }
140
+
141
+ private assertOpen(): void {
142
+ if (this.#closed) throw new Error('Modbus runtime is destroyed.');
143
+ this.lease.assertOpen();
144
+ }
145
+ private notify(eventArg: IIntegrationEvent): void {
146
+ for (const entry of [...this.#subscriptions]) {
147
+ if (this.#closed) break;
148
+ entry.handler(eventArg);
149
+ }
95
150
  }
96
151
  }
@@ -1,124 +1,56 @@
1
1
  import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
2
2
  import type { IDiscoveryCandidate, IDiscoveryContext, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
3
3
  import type { IModbusManualTcpEntry } from './modbus.types.js';
4
-
5
- const modbusDomain = 'modbus';
6
- const defaultModbusTcpPort = 502;
4
+ import { modbusAddress, modbusIdentityText, modbusInteger, modbusRecord, modbusText, modbusUnit } from './modbus.validation.js';
7
5
 
8
6
  export class ModbusManualTcpMatcher implements IDiscoveryMatcher<IModbusManualTcpEntry> {
9
- public id = 'modbus-manual-tcp-match';
10
- public source = 'manual' as const;
11
- public description = 'Recognize manual Modbus TCP setup entries without probing serial ports.';
7
+ public readonly id = 'modbus-manual-tcp-match';
8
+ public readonly source = 'manual' as const;
9
+ public readonly description = 'Accept explicit Modbus endpoint and family identity selections without network probes.';
12
10
 
13
11
  public async matches(inputArg: IModbusManualTcpEntry, contextArg: IDiscoveryContext): Promise<IDiscoveryMatch> {
14
- void contextArg;
15
- if (inputArg.type === 'serial' || this.looksLikeSerialPort(inputArg.host) || this.looksLikeSerialPort(String(inputArg.port || ''))) {
16
- return {
17
- matched: false,
18
- confidence: 'low',
19
- reason: 'Serial RTU setup is not auto-discovered by this integration.',
20
- };
21
- }
22
-
23
- const haystack = `${inputArg.name || ''} ${inputArg.protocol || ''} ${inputArg.manufacturer || ''} ${inputArg.model || ''}`.toLowerCase();
24
- const hasModbusHint = inputArg.type === 'tcp' || haystack.includes('modbus') || Boolean(inputArg.metadata?.modbus || inputArg.metadata?.modbusTcp);
25
- const matched = Boolean(inputArg.host && (hasModbusHint || inputArg.port === defaultModbusTcpPort));
26
- if (!matched) {
27
- return {
28
- matched: false,
29
- confidence: 'low',
30
- reason: 'Manual entry does not contain a Modbus TCP host or hint.',
31
- };
12
+ contextArg.abortSignal?.throwIfAborted();
13
+ try {
14
+ const input = modbusRecord(inputArg, ['integrationDomain', 'address', 'port', 'unitId', 'expectedVendorName', 'expectedProductCode', 'expectedRevision', 'name']);
15
+ if (input.integrationDomain !== 'modbus') throw new TypeError('Manual Modbus selection requires the exact integration domain.');
16
+ const address = modbusAddress(input.address);
17
+ const port = modbusInteger(input.port === undefined ? 502 : input.port, 1, 65535);
18
+ const unitId = modbusUnit(input.unitId);
19
+ const expectedVendorName = modbusIdentityText(input.expectedVendorName);
20
+ const expectedProductCode = modbusIdentityText(input.expectedProductCode);
21
+ const expectedRevision = input.expectedRevision === undefined ? undefined : modbusIdentityText(input.expectedRevision);
22
+ const id = `modbus:${address}:${port}:${unitId}`;
23
+ return { matched: true, confidence: 'low', reason: 'Explicit manual selection is structurally valid; no network identity verification has occurred.',
24
+ normalizedDeviceId: id, candidate: { source: 'manual', integrationDomain: 'modbus', id, host: address, port,
25
+ ...(input.name === undefined ? {} : { name: modbusText(input.name, 'name', 128) }),
26
+ metadata: { unitId, expectedVendorName, expectedProductCode, ...(expectedRevision === undefined ? {} : { expectedRevision }) } } };
27
+ } catch (errorArg) {
28
+ return { matched: false, confidence: 'low', reason: errorArg instanceof Error ? errorArg.message : 'Invalid explicit Modbus selection.' };
32
29
  }
33
-
34
- const id = this.normalizedId(inputArg);
35
- return {
36
- matched: true,
37
- confidence: hasModbusHint ? 'high' : 'medium',
38
- reason: 'Manual entry can be configured as a Modbus TCP hub.',
39
- normalizedDeviceId: id,
40
- candidate: {
41
- source: 'manual',
42
- integrationDomain: modbusDomain,
43
- id,
44
- host: inputArg.host,
45
- port: inputArg.port || defaultModbusTcpPort,
46
- name: inputArg.name,
47
- manufacturer: inputArg.manufacturer || 'Modbus',
48
- model: inputArg.model || 'TCP hub',
49
- serialNumber: inputArg.serialNumber,
50
- metadata: {
51
- ...inputArg.metadata,
52
- type: 'tcp',
53
- unitId: inputArg.unitId || inputArg.slave || 1,
54
- },
55
- },
56
- };
57
- }
58
-
59
- private normalizedId(inputArg: IModbusManualTcpEntry): string {
60
- return slug(inputArg.id || inputArg.serialNumber || `${inputArg.host || 'modbus'}_${inputArg.port || defaultModbusTcpPort}`);
61
- }
62
-
63
- private looksLikeSerialPort(valueArg: string | undefined): boolean {
64
- return Boolean(valueArg && (/^\/dev\//.test(valueArg) || /^com\d+$/i.test(valueArg)));
65
30
  }
66
31
  }
67
32
 
68
33
  export class ModbusCandidateValidator implements IDiscoveryValidator {
69
- public id = 'modbus-candidate-validator';
70
- public description = 'Validate that a candidate has enough Modbus TCP information for setup.';
34
+ public readonly id = 'modbus-candidate-validator';
35
+ public readonly description = 'Validate explicit manual selections; live admission belongs to the config flow.';
71
36
 
72
37
  public async validate(candidateArg: IDiscoveryCandidate, contextArg: IDiscoveryContext): Promise<IDiscoveryMatch> {
73
- void contextArg;
74
- const type = String(candidateArg.metadata?.type || '').toLowerCase();
75
- if (type === 'serial' || this.looksLikeSerialPort(candidateArg.host)) {
76
- return {
77
- matched: false,
78
- confidence: 'low',
79
- reason: 'Serial RTU candidates require explicit serial configuration and are not probed.',
80
- };
38
+ contextArg.abortSignal?.throwIfAborted();
39
+ try {
40
+ const candidate = modbusRecord(candidateArg);
41
+ if (candidate.source !== 'manual' || candidate.integrationDomain !== 'modbus') throw new TypeError('Modbus accepts explicit manual candidates only.');
42
+ const metadata = modbusRecord(candidate.metadata, ['unitId', 'expectedVendorName', 'expectedProductCode', 'expectedRevision']);
43
+ return new ModbusManualTcpMatcher().matches({ integrationDomain: 'modbus', address: modbusAddress(candidate.host),
44
+ port: candidate.port === undefined ? undefined : modbusInteger(candidate.port, 1, 65535), unitId: modbusUnit(metadata.unitId),
45
+ expectedVendorName: modbusIdentityText(metadata.expectedVendorName), expectedProductCode: modbusIdentityText(metadata.expectedProductCode),
46
+ ...(metadata.expectedRevision === undefined ? {} : { expectedRevision: modbusIdentityText(metadata.expectedRevision) }),
47
+ ...(candidate.name === undefined ? {} : { name: modbusText(candidate.name, 'name', 128) }) }, contextArg);
48
+ } catch (errorArg) {
49
+ return { matched: false, confidence: 'low', reason: errorArg instanceof Error ? errorArg.message : 'Invalid explicit Modbus candidate.' };
81
50
  }
82
-
83
- const haystack = `${candidateArg.name || ''} ${candidateArg.model || ''} ${candidateArg.manufacturer || ''}`.toLowerCase();
84
- const matched = Boolean(candidateArg.host && (
85
- candidateArg.integrationDomain === modbusDomain
86
- || candidateArg.port === defaultModbusTcpPort
87
- || type === 'tcp'
88
- || haystack.includes('modbus')
89
- || candidateArg.metadata?.modbus
90
- || candidateArg.metadata?.modbusTcp
91
- ));
92
- const id = slug(candidateArg.id || candidateArg.serialNumber || `${candidateArg.host || 'modbus'}_${candidateArg.port || defaultModbusTcpPort}`);
93
- return {
94
- matched,
95
- confidence: matched && candidateArg.integrationDomain === modbusDomain ? 'high' : matched ? 'medium' : 'low',
96
- reason: matched ? 'Candidate has Modbus TCP setup data.' : 'Candidate is not a Modbus TCP hub.',
97
- normalizedDeviceId: matched ? id : undefined,
98
- candidate: matched ? {
99
- ...candidateArg,
100
- integrationDomain: modbusDomain,
101
- id,
102
- port: candidateArg.port || defaultModbusTcpPort,
103
- manufacturer: candidateArg.manufacturer || 'Modbus',
104
- model: candidateArg.model || 'TCP hub',
105
- metadata: {
106
- ...candidateArg.metadata,
107
- type: 'tcp',
108
- },
109
- } : undefined,
110
- };
111
- }
112
-
113
- private looksLikeSerialPort(valueArg: string | undefined): boolean {
114
- return Boolean(valueArg && (/^\/dev\//.test(valueArg) || /^com\d+$/i.test(valueArg)));
115
51
  }
116
52
  }
117
53
 
118
- export const createModbusDiscoveryDescriptor = (): DiscoveryDescriptor => {
119
- return new DiscoveryDescriptor({ integrationDomain: modbusDomain, displayName: 'Modbus' })
120
- .addMatcher(new ModbusManualTcpMatcher())
121
- .addValidator(new ModbusCandidateValidator());
122
- };
123
-
124
- const slug = (valueArg: string): string => valueArg.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '') || modbusDomain;
54
+ export const createModbusDiscoveryDescriptor = (): DiscoveryDescriptor =>
55
+ new DiscoveryDescriptor({ integrationDomain: 'modbus', displayName: 'Modbus' })
56
+ .addMatcher(new ModbusManualTcpMatcher()).addValidator(new ModbusCandidateValidator());