@smarthome.exchange/integrations 1.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (529) hide show
  1. package/changelog.md +84 -0
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/core/classes.configstore.d.ts +2 -0
  4. package/dist_ts/core/classes.configstore.js +84 -27
  5. package/dist_ts/core/classes.nativehomeadmission.d.ts +19 -0
  6. package/dist_ts/core/classes.nativehomeadmission.js +84 -0
  7. package/dist_ts/core/classes.nativeintegrationowner.d.ts +81 -0
  8. package/dist_ts/core/classes.nativeintegrationowner.js +273 -0
  9. package/dist_ts/core/classes.runtimemanager.d.ts +2 -0
  10. package/dist_ts/core/classes.runtimemanager.js +11 -3
  11. package/dist_ts/core/index.d.ts +2 -0
  12. package/dist_ts/core/index.js +3 -1
  13. package/dist_ts/core/types.d.ts +16 -0
  14. package/dist_ts/integrations/braviatv/braviatv.classes.client.d.ts +23 -72
  15. package/dist_ts/integrations/braviatv/braviatv.classes.client.js +265 -625
  16. package/dist_ts/integrations/braviatv/braviatv.classes.configflow.d.ts +7 -7
  17. package/dist_ts/integrations/braviatv/braviatv.classes.configflow.js +51 -68
  18. package/dist_ts/integrations/braviatv/braviatv.classes.integration.d.ts +8 -11
  19. package/dist_ts/integrations/braviatv/braviatv.classes.integration.js +186 -135
  20. package/dist_ts/integrations/braviatv/braviatv.discovery.js +12 -11
  21. package/dist_ts/integrations/braviatv/braviatv.mapper.d.ts +6 -14
  22. package/dist_ts/integrations/braviatv/braviatv.mapper.js +37 -131
  23. package/dist_ts/integrations/braviatv/braviatv.types.d.ts +55 -133
  24. package/dist_ts/integrations/braviatv/braviatv.validation.d.ts +7 -0
  25. package/dist_ts/integrations/braviatv/braviatv.validation.js +72 -0
  26. package/dist_ts/integrations/broadlink/broadlink.actions.d.ts +2 -0
  27. package/dist_ts/integrations/broadlink/broadlink.actions.js +36 -0
  28. package/dist_ts/integrations/broadlink/broadlink.classes.client.d.ts +21 -19
  29. package/dist_ts/integrations/broadlink/broadlink.classes.client.js +185 -124
  30. package/dist_ts/integrations/broadlink/broadlink.classes.configflow.d.ts +5 -4
  31. package/dist_ts/integrations/broadlink/broadlink.classes.configflow.js +64 -83
  32. package/dist_ts/integrations/broadlink/broadlink.classes.integration.d.ts +16 -19
  33. package/dist_ts/integrations/broadlink/broadlink.classes.integration.js +114 -59
  34. package/dist_ts/integrations/broadlink/broadlink.mapper.d.ts +3 -2
  35. package/dist_ts/integrations/broadlink/broadlink.mapper.js +2 -1
  36. package/dist_ts/integrations/broadlink/broadlink.selected.mapper.d.ts +10 -0
  37. package/dist_ts/integrations/broadlink/broadlink.selected.mapper.js +41 -0
  38. package/dist_ts/integrations/broadlink/broadlink.types.d.ts +43 -7
  39. package/dist_ts/integrations/broadlink/broadlink.validation.d.ts +7 -0
  40. package/dist_ts/integrations/broadlink/broadlink.validation.js +63 -0
  41. package/dist_ts/integrations/daikin/daikin.classes.client.d.ts +10 -52
  42. package/dist_ts/integrations/daikin/daikin.classes.client.js +55 -614
  43. package/dist_ts/integrations/daikin/daikin.classes.configflow.d.ts +8 -12
  44. package/dist_ts/integrations/daikin/daikin.classes.configflow.js +77 -138
  45. package/dist_ts/integrations/daikin/daikin.classes.integration.d.ts +7 -24
  46. package/dist_ts/integrations/daikin/daikin.classes.integration.js +175 -67
  47. package/dist_ts/integrations/daikin/daikin.discovery.d.ts +11 -24
  48. package/dist_ts/integrations/daikin/daikin.discovery.js +98 -305
  49. package/dist_ts/integrations/daikin/daikin.mapper.d.ts +6 -7
  50. package/dist_ts/integrations/daikin/daikin.mapper.js +95 -155
  51. package/dist_ts/integrations/daikin/daikin.types.d.ts +24 -44
  52. package/dist_ts/integrations/daikin/daikin.types.js +1 -1
  53. package/dist_ts/integrations/daikin/daikin.validation.d.ts +5 -0
  54. package/dist_ts/integrations/daikin/daikin.validation.js +53 -0
  55. package/dist_ts/integrations/deconz/deconz.classes.client.d.ts +37 -29
  56. package/dist_ts/integrations/deconz/deconz.classes.client.js +521 -294
  57. package/dist_ts/integrations/deconz/deconz.classes.configflow.d.ts +6 -1
  58. package/dist_ts/integrations/deconz/deconz.classes.configflow.js +63 -33
  59. package/dist_ts/integrations/deconz/deconz.classes.integration.d.ts +4 -3
  60. package/dist_ts/integrations/deconz/deconz.classes.integration.js +85 -40
  61. package/dist_ts/integrations/deconz/deconz.mapper.d.ts +1 -0
  62. package/dist_ts/integrations/deconz/deconz.mapper.js +24 -6
  63. package/dist_ts/integrations/deconz/deconz.types.d.ts +43 -6
  64. package/dist_ts/integrations/deconz/deconz.validation.d.ts +7 -1
  65. package/dist_ts/integrations/deconz/deconz.validation.js +215 -4
  66. package/dist_ts/integrations/denonavr/denonavr.classes.client.d.ts +31 -40
  67. package/dist_ts/integrations/denonavr/denonavr.classes.client.js +149 -404
  68. package/dist_ts/integrations/denonavr/denonavr.classes.configflow.d.ts +7 -2
  69. package/dist_ts/integrations/denonavr/denonavr.classes.configflow.js +44 -36
  70. package/dist_ts/integrations/denonavr/denonavr.classes.integration.d.ts +6 -11
  71. package/dist_ts/integrations/denonavr/denonavr.classes.integration.js +192 -113
  72. package/dist_ts/integrations/denonavr/denonavr.discovery.d.ts +10 -18
  73. package/dist_ts/integrations/denonavr/denonavr.discovery.js +71 -193
  74. package/dist_ts/integrations/denonavr/denonavr.mapper.d.ts +5 -13
  75. package/dist_ts/integrations/denonavr/denonavr.mapper.js +35 -159
  76. package/dist_ts/integrations/denonavr/denonavr.types.d.ts +55 -90
  77. package/dist_ts/integrations/denonavr/denonavr.validation.d.ts +10 -0
  78. package/dist_ts/integrations/denonavr/denonavr.validation.js +86 -0
  79. package/dist_ts/integrations/esphome/esphome.classes.client.d.ts +19 -7
  80. package/dist_ts/integrations/esphome/esphome.classes.client.js +183 -89
  81. package/dist_ts/integrations/esphome/esphome.classes.configflow.d.ts +5 -2
  82. package/dist_ts/integrations/esphome/esphome.classes.configflow.js +43 -33
  83. package/dist_ts/integrations/esphome/esphome.classes.integration.d.ts +8 -6
  84. package/dist_ts/integrations/esphome/esphome.classes.integration.js +71 -43
  85. package/dist_ts/integrations/esphome/esphome.discovery.js +4 -12
  86. package/dist_ts/integrations/esphome/esphome.mapper.d.ts +2 -2
  87. package/dist_ts/integrations/esphome/esphome.mapper.js +6 -4
  88. package/dist_ts/integrations/esphome/esphome.types.d.ts +31 -6
  89. package/dist_ts/integrations/esphome/esphome.types.js +1 -1
  90. package/dist_ts/integrations/esphome/esphome.validation.d.ts +7 -0
  91. package/dist_ts/integrations/esphome/esphome.validation.js +66 -0
  92. package/dist_ts/integrations/homeassistant/homeassistant.classes.client.d.ts +32 -4
  93. package/dist_ts/integrations/homeassistant/homeassistant.classes.client.js +345 -116
  94. package/dist_ts/integrations/homeassistant/homeassistant.classes.integration.d.ts +8 -5
  95. package/dist_ts/integrations/homeassistant/homeassistant.classes.integration.js +34 -8
  96. package/dist_ts/integrations/homeassistant/homeassistant.classes.runtime.d.ts +7 -4
  97. package/dist_ts/integrations/homeassistant/homeassistant.classes.runtime.js +44 -56
  98. package/dist_ts/integrations/homeassistant/homeassistant.types.d.ts +12 -1
  99. package/dist_ts/integrations/hue/hue.classes.client.d.ts +6 -1
  100. package/dist_ts/integrations/hue/hue.classes.client.js +120 -45
  101. package/dist_ts/integrations/hue/hue.classes.configflow.js +16 -7
  102. package/dist_ts/integrations/hue/hue.classes.integration.d.ts +2 -0
  103. package/dist_ts/integrations/hue/hue.classes.integration.js +103 -30
  104. package/dist_ts/integrations/hue/hue.validation.d.ts +1 -0
  105. package/dist_ts/integrations/hue/hue.validation.js +14 -4
  106. package/dist_ts/integrations/kodi/kodi.classes.client.d.ts +11 -45
  107. package/dist_ts/integrations/kodi/kodi.classes.client.js +84 -310
  108. package/dist_ts/integrations/kodi/kodi.classes.configflow.d.ts +9 -8
  109. package/dist_ts/integrations/kodi/kodi.classes.configflow.js +53 -48
  110. package/dist_ts/integrations/kodi/kodi.classes.integration.d.ts +11 -11
  111. package/dist_ts/integrations/kodi/kodi.classes.integration.js +114 -171
  112. package/dist_ts/integrations/kodi/kodi.discovery.js +53 -29
  113. package/dist_ts/integrations/kodi/kodi.mapper.d.ts +7 -2
  114. package/dist_ts/integrations/kodi/kodi.mapper.js +48 -10
  115. package/dist_ts/integrations/kodi/kodi.types.d.ts +32 -28
  116. package/dist_ts/integrations/kodi/kodi.validation.d.ts +9 -0
  117. package/dist_ts/integrations/kodi/kodi.validation.js +62 -0
  118. package/dist_ts/integrations/modbus/modbus.classes.client.d.ts +15 -61
  119. package/dist_ts/integrations/modbus/modbus.classes.client.js +117 -776
  120. package/dist_ts/integrations/modbus/modbus.classes.configflow.d.ts +9 -5
  121. package/dist_ts/integrations/modbus/modbus.classes.configflow.js +70 -38
  122. package/dist_ts/integrations/modbus/modbus.classes.integration.d.ts +6 -17
  123. package/dist_ts/integrations/modbus/modbus.classes.integration.js +133 -57
  124. package/dist_ts/integrations/modbus/modbus.discovery.d.ts +5 -8
  125. package/dist_ts/integrations/modbus/modbus.discovery.js +38 -93
  126. package/dist_ts/integrations/modbus/modbus.mapper.d.ts +8 -32
  127. package/dist_ts/integrations/modbus/modbus.mapper.js +71 -431
  128. package/dist_ts/integrations/modbus/modbus.types.d.ts +82 -332
  129. package/dist_ts/integrations/modbus/modbus.validation.d.ts +10 -0
  130. package/dist_ts/integrations/modbus/modbus.validation.js +128 -0
  131. package/dist_ts/integrations/mqtt/mqtt.classes.client.d.ts +15 -2
  132. package/dist_ts/integrations/mqtt/mqtt.classes.client.js +173 -23
  133. package/dist_ts/integrations/mqtt/mqtt.classes.configflow.d.ts +5 -1
  134. package/dist_ts/integrations/mqtt/mqtt.classes.configflow.js +53 -33
  135. package/dist_ts/integrations/mqtt/mqtt.classes.integration.d.ts +4 -1
  136. package/dist_ts/integrations/mqtt/mqtt.classes.integration.js +127 -56
  137. package/dist_ts/integrations/mqtt/mqtt.classes.session.d.ts +13 -0
  138. package/dist_ts/integrations/mqtt/mqtt.classes.session.js +130 -0
  139. package/dist_ts/integrations/mqtt/mqtt.mapper.d.ts +3 -0
  140. package/dist_ts/integrations/mqtt/mqtt.mapper.js +44 -1
  141. package/dist_ts/integrations/mqtt/mqtt.types.d.ts +46 -0
  142. package/dist_ts/integrations/mqtt/mqtt.validation.d.ts +7 -0
  143. package/dist_ts/integrations/mqtt/mqtt.validation.js +180 -0
  144. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.client.d.ts +12 -34
  145. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.client.js +143 -231
  146. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.configflow.d.ts +6 -1
  147. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.configflow.js +105 -18
  148. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.integration.d.ts +8 -8
  149. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.integration.js +141 -155
  150. package/dist_ts/integrations/nanoleaf/nanoleaf.credentials.d.ts +9 -0
  151. package/dist_ts/integrations/nanoleaf/nanoleaf.credentials.js +35 -0
  152. package/dist_ts/integrations/nanoleaf/nanoleaf.mapper.d.ts +6 -18
  153. package/dist_ts/integrations/nanoleaf/nanoleaf.mapper.js +45 -220
  154. package/dist_ts/integrations/nanoleaf/nanoleaf.types.d.ts +33 -147
  155. package/dist_ts/integrations/nanoleaf/nanoleaf.validation.d.ts +6 -0
  156. package/dist_ts/integrations/nanoleaf/nanoleaf.validation.js +82 -0
  157. package/dist_ts/integrations/onkyo/onkyo.classes.client.d.ts +15 -20
  158. package/dist_ts/integrations/onkyo/onkyo.classes.client.js +161 -404
  159. package/dist_ts/integrations/onkyo/onkyo.classes.configflow.d.ts +9 -4
  160. package/dist_ts/integrations/onkyo/onkyo.classes.configflow.js +51 -6
  161. package/dist_ts/integrations/onkyo/onkyo.classes.integration.d.ts +22 -6
  162. package/dist_ts/integrations/onkyo/onkyo.classes.integration.js +144 -11
  163. package/dist_ts/integrations/onkyo/onkyo.discovery.d.ts +6 -1
  164. package/dist_ts/integrations/onkyo/onkyo.discovery.js +40 -4
  165. package/dist_ts/integrations/onkyo/onkyo.mapper.d.ts +7 -8
  166. package/dist_ts/integrations/onkyo/onkyo.mapper.js +45 -12
  167. package/dist_ts/integrations/onkyo/onkyo.types.d.ts +58 -8
  168. package/dist_ts/integrations/onkyo/onkyo.types.js +13 -102
  169. package/dist_ts/integrations/onkyo/onkyo.validation.d.ts +16 -0
  170. package/dist_ts/integrations/onkyo/onkyo.validation.js +77 -0
  171. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.client.d.ts +25 -25
  172. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.client.js +116 -360
  173. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.configflow.d.ts +11 -3
  174. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.configflow.js +76 -6
  175. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.integration.d.ts +26 -4
  176. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.integration.js +142 -9
  177. package/dist_ts/integrations/panasonic_viera/panasonic_viera.discovery.d.ts +2 -1
  178. package/dist_ts/integrations/panasonic_viera/panasonic_viera.discovery.js +31 -4
  179. package/dist_ts/integrations/panasonic_viera/panasonic_viera.mapper.d.ts +9 -9
  180. package/dist_ts/integrations/panasonic_viera/panasonic_viera.mapper.js +31 -149
  181. package/dist_ts/integrations/panasonic_viera/panasonic_viera.types.d.ts +51 -11
  182. package/dist_ts/integrations/panasonic_viera/panasonic_viera.types.js +22 -87
  183. package/dist_ts/integrations/panasonic_viera/panasonic_viera.validation.d.ts +6 -0
  184. package/dist_ts/integrations/panasonic_viera/panasonic_viera.validation.js +56 -0
  185. package/dist_ts/integrations/pioneer/pioneer.classes.client.d.ts +31 -16
  186. package/dist_ts/integrations/pioneer/pioneer.classes.client.js +167 -409
  187. package/dist_ts/integrations/pioneer/pioneer.classes.configflow.d.ts +10 -3
  188. package/dist_ts/integrations/pioneer/pioneer.classes.configflow.js +66 -6
  189. package/dist_ts/integrations/pioneer/pioneer.classes.integration.d.ts +13 -5
  190. package/dist_ts/integrations/pioneer/pioneer.classes.integration.js +239 -11
  191. package/dist_ts/integrations/pioneer/pioneer.discovery.d.ts +14 -1
  192. package/dist_ts/integrations/pioneer/pioneer.discovery.js +51 -4
  193. package/dist_ts/integrations/pioneer/pioneer.mapper.d.ts +14 -8
  194. package/dist_ts/integrations/pioneer/pioneer.mapper.js +54 -12
  195. package/dist_ts/integrations/pioneer/pioneer.types.d.ts +79 -12
  196. package/dist_ts/integrations/pioneer/pioneer.types.js +12 -76
  197. package/dist_ts/integrations/pioneer/pioneer.validation.d.ts +10 -0
  198. package/dist_ts/integrations/pioneer/pioneer.validation.js +85 -0
  199. package/dist_ts/integrations/samsungtv/samsungtv.classes.client.d.ts +4 -2
  200. package/dist_ts/integrations/samsungtv/samsungtv.classes.client.js +41 -16
  201. package/dist_ts/integrations/samsungtv/samsungtv.classes.configflow.d.ts +5 -0
  202. package/dist_ts/integrations/samsungtv/samsungtv.classes.configflow.js +20 -7
  203. package/dist_ts/integrations/samsungtv/samsungtv.classes.integration.d.ts +1 -0
  204. package/dist_ts/integrations/samsungtv/samsungtv.classes.integration.js +53 -50
  205. package/dist_ts/integrations/samsungtv/samsungtv.types.d.ts +4 -1
  206. package/dist_ts/integrations/shelly/shelly.classes.client.d.ts +26 -30
  207. package/dist_ts/integrations/shelly/shelly.classes.client.js +504 -276
  208. package/dist_ts/integrations/shelly/shelly.classes.configflow.d.ts +8 -3
  209. package/dist_ts/integrations/shelly/shelly.classes.configflow.js +43 -38
  210. package/dist_ts/integrations/shelly/shelly.classes.integration.d.ts +6 -6
  211. package/dist_ts/integrations/shelly/shelly.classes.integration.js +103 -101
  212. package/dist_ts/integrations/shelly/shelly.classes.subscription.d.ts +4 -0
  213. package/dist_ts/integrations/shelly/shelly.classes.subscription.js +24 -2
  214. package/dist_ts/integrations/shelly/shelly.mapper.d.ts +1 -2
  215. package/dist_ts/integrations/shelly/shelly.mapper.js +21 -20
  216. package/dist_ts/integrations/shelly/shelly.rpc.js +4 -1
  217. package/dist_ts/integrations/shelly/shelly.types.d.ts +42 -2
  218. package/dist_ts/integrations/shelly/shelly.types.js +11 -2
  219. package/dist_ts/integrations/shelly/shelly.validation.d.ts +7 -0
  220. package/dist_ts/integrations/shelly/shelly.validation.js +79 -0
  221. package/dist_ts/integrations/sonos/sonos.classes.client.d.ts +79 -22
  222. package/dist_ts/integrations/sonos/sonos.classes.client.js +427 -139
  223. package/dist_ts/integrations/sonos/sonos.classes.configflow.d.ts +9 -2
  224. package/dist_ts/integrations/sonos/sonos.classes.configflow.js +26 -28
  225. package/dist_ts/integrations/sonos/sonos.classes.integration.d.ts +7 -6
  226. package/dist_ts/integrations/sonos/sonos.classes.integration.js +289 -224
  227. package/dist_ts/integrations/sonos/sonos.config.d.ts +6 -1
  228. package/dist_ts/integrations/sonos/sonos.config.js +23 -2
  229. package/dist_ts/integrations/sonos/sonos.mapper.d.ts +1 -1
  230. package/dist_ts/integrations/sonos/sonos.mapper.js +14 -9
  231. package/dist_ts/integrations/sonos/sonos.matchers.js +3 -3
  232. package/dist_ts/integrations/sonos/sonos.types.d.ts +26 -1
  233. package/dist_ts/integrations/soundtouch/soundtouch.classes.client.d.ts +13 -36
  234. package/dist_ts/integrations/soundtouch/soundtouch.classes.client.js +179 -502
  235. package/dist_ts/integrations/soundtouch/soundtouch.classes.configflow.d.ts +6 -1
  236. package/dist_ts/integrations/soundtouch/soundtouch.classes.configflow.js +50 -40
  237. package/dist_ts/integrations/soundtouch/soundtouch.classes.integration.d.ts +14 -15
  238. package/dist_ts/integrations/soundtouch/soundtouch.classes.integration.js +108 -128
  239. package/dist_ts/integrations/soundtouch/soundtouch.discovery.d.ts +2 -20
  240. package/dist_ts/integrations/soundtouch/soundtouch.discovery.js +23 -113
  241. package/dist_ts/integrations/soundtouch/soundtouch.mapper.d.ts +3 -12
  242. package/dist_ts/integrations/soundtouch/soundtouch.mapper.js +35 -189
  243. package/dist_ts/integrations/soundtouch/soundtouch.types.d.ts +57 -142
  244. package/dist_ts/integrations/soundtouch/soundtouch.types.js +18 -3
  245. package/dist_ts/integrations/soundtouch/soundtouch.validation.d.ts +22 -0
  246. package/dist_ts/integrations/soundtouch/soundtouch.validation.js +65 -0
  247. package/dist_ts/integrations/tasmota/tasmota.classes.integration.d.ts +5 -0
  248. package/dist_ts/integrations/tasmota/tasmota.classes.integration.js +23 -7
  249. package/dist_ts/integrations/tasmota/tasmota.classes.runtime.d.ts +19 -5
  250. package/dist_ts/integrations/tasmota/tasmota.classes.runtime.js +115 -41
  251. package/dist_ts/integrations/tasmota/tasmota.config.js +23 -1
  252. package/dist_ts/integrations/tasmota/tasmota.credentials.d.ts +11 -0
  253. package/dist_ts/integrations/tasmota/tasmota.credentials.js +97 -0
  254. package/dist_ts/integrations/tasmota/tasmota.discovery.d.ts +7 -1
  255. package/dist_ts/integrations/tasmota/tasmota.discovery.js +108 -30
  256. package/dist_ts/integrations/tasmota/tasmota.mapper.d.ts +2 -2
  257. package/dist_ts/integrations/tasmota/tasmota.mapper.js +7 -7
  258. package/dist_ts/integrations/tasmota/tasmota.types.d.ts +41 -2
  259. package/dist_ts/integrations/tuya/tuya.classes.integration.d.ts +4 -1
  260. package/dist_ts/integrations/tuya/tuya.classes.integration.js +79 -110
  261. package/dist_ts/integrations/tuya/tuya.classes.runtime.d.ts +5 -2
  262. package/dist_ts/integrations/tuya/tuya.classes.runtime.js +73 -26
  263. package/dist_ts/integrations/tuya/tuya.mapper.js +2 -2
  264. package/dist_ts/integrations/tuya/tuya.types.d.ts +3 -0
  265. package/dist_ts/integrations/tuya/tuya.validation.d.ts +2 -2
  266. package/dist_ts/integrations/tuya/tuya.validation.js +6 -11
  267. package/dist_ts/integrations/webostv/webostv.classes.client.d.ts +6 -5
  268. package/dist_ts/integrations/webostv/webostv.classes.client.js +147 -76
  269. package/dist_ts/integrations/webostv/webostv.classes.configflow.d.ts +7 -2
  270. package/dist_ts/integrations/webostv/webostv.classes.configflow.js +36 -20
  271. package/dist_ts/integrations/webostv/webostv.classes.integration.d.ts +4 -4
  272. package/dist_ts/integrations/webostv/webostv.classes.integration.js +46 -54
  273. package/dist_ts/integrations/webostv/webostv.mapper.js +5 -5
  274. package/dist_ts/integrations/webostv/webostv.types.d.ts +16 -3
  275. package/dist_ts/integrations/webostv/webostv.types.js +1 -1
  276. package/dist_ts/integrations/webostv/webostv.validation.d.ts +3 -1
  277. package/dist_ts/integrations/webostv/webostv.validation.js +23 -1
  278. package/dist_ts/integrations/wiz/index.d.ts +1 -0
  279. package/dist_ts/integrations/wiz/index.js +2 -1
  280. package/dist_ts/integrations/wiz/wiz.classes.client.d.ts +14 -28
  281. package/dist_ts/integrations/wiz/wiz.classes.client.js +93 -229
  282. package/dist_ts/integrations/wiz/wiz.classes.configflow.d.ts +6 -4
  283. package/dist_ts/integrations/wiz/wiz.classes.configflow.js +38 -67
  284. package/dist_ts/integrations/wiz/wiz.classes.integration.d.ts +10 -21
  285. package/dist_ts/integrations/wiz/wiz.classes.integration.js +204 -44
  286. package/dist_ts/integrations/wiz/wiz.discovery.d.ts +10 -31
  287. package/dist_ts/integrations/wiz/wiz.discovery.js +48 -249
  288. package/dist_ts/integrations/wiz/wiz.mapper.d.ts +7 -51
  289. package/dist_ts/integrations/wiz/wiz.mapper.js +82 -668
  290. package/dist_ts/integrations/wiz/wiz.types.d.ts +34 -208
  291. package/dist_ts/integrations/wiz/wiz.validation.d.ts +8 -0
  292. package/dist_ts/integrations/wiz/wiz.validation.js +72 -0
  293. package/dist_ts/integrations/yamaha/index.d.ts +1 -0
  294. package/dist_ts/integrations/yamaha/index.js +2 -1
  295. package/dist_ts/integrations/yamaha/yamaha.classes.client.d.ts +30 -4
  296. package/dist_ts/integrations/yamaha/yamaha.classes.client.js +162 -6
  297. package/dist_ts/integrations/yamaha/yamaha.classes.configflow.d.ts +10 -3
  298. package/dist_ts/integrations/yamaha/yamaha.classes.configflow.js +106 -6
  299. package/dist_ts/integrations/yamaha/yamaha.classes.integration.d.ts +22 -4
  300. package/dist_ts/integrations/yamaha/yamaha.classes.integration.js +243 -8
  301. package/dist_ts/integrations/yamaha/yamaha.discovery.d.ts +13 -1
  302. package/dist_ts/integrations/yamaha/yamaha.discovery.js +57 -4
  303. package/dist_ts/integrations/yamaha/yamaha.mapper.d.ts +16 -8
  304. package/dist_ts/integrations/yamaha/yamaha.mapper.js +58 -12
  305. package/dist_ts/integrations/yamaha/yamaha.types.d.ts +73 -6
  306. package/dist_ts/integrations/yamaha/yamaha.types.js +8 -94
  307. package/dist_ts/integrations/yamaha/yamaha.validation.d.ts +12 -0
  308. package/dist_ts/integrations/yamaha/yamaha.validation.js +89 -0
  309. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.configflow.d.ts +6 -1
  310. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.configflow.js +18 -8
  311. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.integration.d.ts +4 -1
  312. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.integration.js +54 -6
  313. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.runtime.d.ts +12 -6
  314. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.runtime.js +73 -59
  315. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.mapper.d.ts +3 -2
  316. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.mapper.js +17 -12
  317. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.types.d.ts +31 -2
  318. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.validation.d.ts +9 -1
  319. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.validation.js +57 -7
  320. package/dist_ts/integrations/zwave_js/zwave_js.classes.client.d.ts +10 -4
  321. package/dist_ts/integrations/zwave_js/zwave_js.classes.client.js +167 -31
  322. package/dist_ts/integrations/zwave_js/zwave_js.classes.configflow.d.ts +6 -1
  323. package/dist_ts/integrations/zwave_js/zwave_js.classes.configflow.js +57 -35
  324. package/dist_ts/integrations/zwave_js/zwave_js.classes.integration.d.ts +3 -1
  325. package/dist_ts/integrations/zwave_js/zwave_js.classes.integration.js +59 -37
  326. package/dist_ts/integrations/zwave_js/zwave_js.discovery.js +3 -3
  327. package/dist_ts/integrations/zwave_js/zwave_js.mapper.js +41 -24
  328. package/dist_ts/integrations/zwave_js/zwave_js.types.d.ts +25 -3
  329. package/dist_ts/integrations/zwave_js/zwave_js.validation.d.ts +6 -2
  330. package/dist_ts/integrations/zwave_js/zwave_js.validation.js +52 -17
  331. package/dist_ts/plugins.d.ts +2 -1
  332. package/dist_ts/plugins.js +3 -2
  333. package/package.json +9 -9
  334. package/readme.md +539 -8
  335. package/ts/00_commitinfo_data.ts +1 -1
  336. package/ts/core/classes.configstore.ts +84 -27
  337. package/ts/core/classes.nativehomeadmission.ts +96 -0
  338. package/ts/core/classes.nativeintegrationowner.ts +302 -0
  339. package/ts/core/classes.runtimemanager.ts +11 -2
  340. package/ts/core/index.ts +2 -0
  341. package/ts/core/types.ts +18 -0
  342. package/ts/integrations/braviatv/braviatv.classes.client.ts +245 -709
  343. package/ts/integrations/braviatv/braviatv.classes.configflow.ts +47 -76
  344. package/ts/integrations/braviatv/braviatv.classes.integration.ts +167 -147
  345. package/ts/integrations/braviatv/braviatv.discovery.ts +11 -10
  346. package/ts/integrations/braviatv/braviatv.mapper.ts +39 -142
  347. package/ts/integrations/braviatv/braviatv.types.ts +53 -173
  348. package/ts/integrations/braviatv/braviatv.validation.ts +68 -0
  349. package/ts/integrations/broadlink/broadlink.actions.ts +31 -0
  350. package/ts/integrations/broadlink/broadlink.classes.client.ts +170 -144
  351. package/ts/integrations/broadlink/broadlink.classes.configflow.ts +59 -89
  352. package/ts/integrations/broadlink/broadlink.classes.integration.ts +110 -64
  353. package/ts/integrations/broadlink/broadlink.mapper.ts +5 -4
  354. package/ts/integrations/broadlink/broadlink.selected.mapper.ts +46 -0
  355. package/ts/integrations/broadlink/broadlink.types.ts +48 -8
  356. package/ts/integrations/broadlink/broadlink.validation.ts +66 -0
  357. package/ts/integrations/daikin/daikin.classes.client.ts +52 -661
  358. package/ts/integrations/daikin/daikin.classes.configflow.ts +72 -152
  359. package/ts/integrations/daikin/daikin.classes.integration.ts +166 -71
  360. package/ts/integrations/daikin/daikin.discovery.ts +94 -346
  361. package/ts/integrations/daikin/daikin.mapper.ts +83 -166
  362. package/ts/integrations/daikin/daikin.types.ts +27 -48
  363. package/ts/integrations/daikin/daikin.validation.ts +51 -0
  364. package/ts/integrations/daikin/readme.md +83 -0
  365. package/ts/integrations/deconz/deconz.classes.client.ts +348 -252
  366. package/ts/integrations/deconz/deconz.classes.configflow.ts +57 -29
  367. package/ts/integrations/deconz/deconz.classes.integration.ts +69 -37
  368. package/ts/integrations/deconz/deconz.mapper.ts +22 -5
  369. package/ts/integrations/deconz/deconz.types.ts +49 -6
  370. package/ts/integrations/deconz/deconz.validation.ts +177 -4
  371. package/ts/integrations/deconz/readme.md +15 -1
  372. package/ts/integrations/denonavr/denonavr.classes.client.ts +132 -421
  373. package/ts/integrations/denonavr/denonavr.classes.configflow.ts +44 -37
  374. package/ts/integrations/denonavr/denonavr.classes.integration.ts +171 -117
  375. package/ts/integrations/denonavr/denonavr.discovery.ts +67 -220
  376. package/ts/integrations/denonavr/denonavr.mapper.ts +36 -182
  377. package/ts/integrations/denonavr/denonavr.types.ts +48 -111
  378. package/ts/integrations/denonavr/denonavr.validation.ts +87 -0
  379. package/ts/integrations/esphome/esphome.classes.client.ts +179 -94
  380. package/ts/integrations/esphome/esphome.classes.configflow.ts +40 -33
  381. package/ts/integrations/esphome/esphome.classes.integration.ts +63 -36
  382. package/ts/integrations/esphome/esphome.discovery.ts +3 -12
  383. package/ts/integrations/esphome/esphome.mapper.ts +9 -7
  384. package/ts/integrations/esphome/esphome.types.ts +35 -6
  385. package/ts/integrations/esphome/esphome.validation.ts +66 -0
  386. package/ts/integrations/esphome/readme.md +20 -6
  387. package/ts/integrations/homeassistant/homeassistant.classes.client.ts +353 -125
  388. package/ts/integrations/homeassistant/homeassistant.classes.integration.ts +35 -18
  389. package/ts/integrations/homeassistant/homeassistant.classes.runtime.ts +50 -63
  390. package/ts/integrations/homeassistant/homeassistant.types.ts +17 -1
  391. package/ts/integrations/homeassistant/readme.md +22 -6
  392. package/ts/integrations/hue/hue.classes.client.ts +110 -41
  393. package/ts/integrations/hue/hue.classes.configflow.ts +12 -6
  394. package/ts/integrations/hue/hue.classes.integration.ts +92 -26
  395. package/ts/integrations/hue/hue.validation.ts +13 -3
  396. package/ts/integrations/hue/readme.md +3 -3
  397. package/ts/integrations/kodi/kodi.classes.client.ts +72 -352
  398. package/ts/integrations/kodi/kodi.classes.configflow.ts +52 -55
  399. package/ts/integrations/kodi/kodi.classes.integration.ts +97 -178
  400. package/ts/integrations/kodi/kodi.discovery.ts +49 -28
  401. package/ts/integrations/kodi/kodi.mapper.ts +52 -10
  402. package/ts/integrations/kodi/kodi.types.ts +33 -58
  403. package/ts/integrations/kodi/kodi.validation.ts +60 -0
  404. package/ts/integrations/modbus/modbus.classes.client.ts +100 -842
  405. package/ts/integrations/modbus/modbus.classes.configflow.ts +70 -41
  406. package/ts/integrations/modbus/modbus.classes.integration.ts +115 -60
  407. package/ts/integrations/modbus/modbus.discovery.ts +38 -106
  408. package/ts/integrations/modbus/modbus.mapper.ts +64 -467
  409. package/ts/integrations/modbus/modbus.types.ts +64 -381
  410. package/ts/integrations/modbus/modbus.validation.ts +119 -0
  411. package/ts/integrations/mqtt/mqtt.classes.client.ts +136 -19
  412. package/ts/integrations/mqtt/mqtt.classes.configflow.ts +45 -32
  413. package/ts/integrations/mqtt/mqtt.classes.integration.ts +99 -38
  414. package/ts/integrations/mqtt/mqtt.classes.session.ts +118 -0
  415. package/ts/integrations/mqtt/mqtt.mapper.ts +43 -1
  416. package/ts/integrations/mqtt/mqtt.types.ts +52 -0
  417. package/ts/integrations/mqtt/mqtt.validation.ts +157 -0
  418. package/ts/integrations/mqtt/readme.md +12 -3
  419. package/ts/integrations/nanoleaf/nanoleaf.classes.client.ts +115 -249
  420. package/ts/integrations/nanoleaf/nanoleaf.classes.configflow.ts +90 -19
  421. package/ts/integrations/nanoleaf/nanoleaf.classes.integration.ts +130 -181
  422. package/ts/integrations/nanoleaf/nanoleaf.credentials.ts +39 -0
  423. package/ts/integrations/nanoleaf/nanoleaf.mapper.ts +47 -260
  424. package/ts/integrations/nanoleaf/nanoleaf.types.ts +29 -147
  425. package/ts/integrations/nanoleaf/nanoleaf.validation.ts +77 -0
  426. package/ts/integrations/onkyo/onkyo.classes.client.ts +148 -457
  427. package/ts/integrations/onkyo/onkyo.classes.configflow.ts +52 -6
  428. package/ts/integrations/onkyo/onkyo.classes.integration.ts +121 -12
  429. package/ts/integrations/onkyo/onkyo.discovery.ts +41 -3
  430. package/ts/integrations/onkyo/onkyo.mapper.ts +46 -19
  431. package/ts/integrations/onkyo/onkyo.types.ts +55 -110
  432. package/ts/integrations/onkyo/onkyo.validation.ts +85 -0
  433. package/ts/integrations/onkyo/readme.md +126 -0
  434. package/ts/integrations/panasonic_viera/panasonic_viera.classes.client.ts +101 -398
  435. package/ts/integrations/panasonic_viera/panasonic_viera.classes.configflow.ts +69 -5
  436. package/ts/integrations/panasonic_viera/panasonic_viera.classes.integration.ts +141 -9
  437. package/ts/integrations/panasonic_viera/panasonic_viera.discovery.ts +32 -3
  438. package/ts/integrations/panasonic_viera/panasonic_viera.mapper.ts +34 -161
  439. package/ts/integrations/panasonic_viera/panasonic_viera.types.ts +52 -96
  440. package/ts/integrations/panasonic_viera/panasonic_viera.validation.ts +56 -0
  441. package/ts/integrations/pioneer/pioneer.classes.client.ts +132 -428
  442. package/ts/integrations/pioneer/pioneer.classes.configflow.ts +67 -5
  443. package/ts/integrations/pioneer/pioneer.classes.integration.ts +225 -11
  444. package/ts/integrations/pioneer/pioneer.discovery.ts +45 -3
  445. package/ts/integrations/pioneer/pioneer.mapper.ts +57 -13
  446. package/ts/integrations/pioneer/pioneer.types.ts +87 -85
  447. package/ts/integrations/pioneer/pioneer.validation.ts +84 -0
  448. package/ts/integrations/samsungtv/readme.md +11 -5
  449. package/ts/integrations/samsungtv/samsungtv.classes.client.ts +40 -15
  450. package/ts/integrations/samsungtv/samsungtv.classes.configflow.ts +21 -6
  451. package/ts/integrations/samsungtv/samsungtv.classes.integration.ts +53 -38
  452. package/ts/integrations/samsungtv/samsungtv.types.ts +4 -1
  453. package/ts/integrations/shelly/readme.md +22 -7
  454. package/ts/integrations/shelly/shelly.classes.client.ts +386 -269
  455. package/ts/integrations/shelly/shelly.classes.configflow.ts +43 -38
  456. package/ts/integrations/shelly/shelly.classes.integration.ts +92 -111
  457. package/ts/integrations/shelly/shelly.classes.subscription.ts +24 -1
  458. package/ts/integrations/shelly/shelly.mapper.ts +19 -20
  459. package/ts/integrations/shelly/shelly.rpc.ts +2 -0
  460. package/ts/integrations/shelly/shelly.types.ts +50 -2
  461. package/ts/integrations/shelly/shelly.validation.ts +76 -0
  462. package/ts/integrations/sonos/readme.md +117 -38
  463. package/ts/integrations/sonos/sonos.classes.client.ts +392 -123
  464. package/ts/integrations/sonos/sonos.classes.configflow.ts +25 -28
  465. package/ts/integrations/sonos/sonos.classes.integration.ts +211 -186
  466. package/ts/integrations/sonos/sonos.config.ts +24 -2
  467. package/ts/integrations/sonos/sonos.mapper.ts +13 -8
  468. package/ts/integrations/sonos/sonos.matchers.ts +2 -2
  469. package/ts/integrations/sonos/sonos.types.ts +30 -1
  470. package/ts/integrations/soundtouch/soundtouch.classes.client.ts +152 -571
  471. package/ts/integrations/soundtouch/soundtouch.classes.configflow.ts +47 -42
  472. package/ts/integrations/soundtouch/soundtouch.classes.integration.ts +95 -150
  473. package/ts/integrations/soundtouch/soundtouch.discovery.ts +21 -133
  474. package/ts/integrations/soundtouch/soundtouch.mapper.ts +36 -207
  475. package/ts/integrations/soundtouch/soundtouch.types.ts +50 -195
  476. package/ts/integrations/soundtouch/soundtouch.validation.ts +60 -0
  477. package/ts/integrations/tasmota/readme.md +16 -5
  478. package/ts/integrations/tasmota/tasmota.classes.integration.ts +24 -6
  479. package/ts/integrations/tasmota/tasmota.classes.runtime.ts +112 -43
  480. package/ts/integrations/tasmota/tasmota.config.ts +17 -0
  481. package/ts/integrations/tasmota/tasmota.credentials.ts +92 -0
  482. package/ts/integrations/tasmota/tasmota.discovery.ts +87 -24
  483. package/ts/integrations/tasmota/tasmota.mapper.ts +6 -6
  484. package/ts/integrations/tasmota/tasmota.types.ts +43 -2
  485. package/ts/integrations/tuya/readme.md +52 -10
  486. package/ts/integrations/tuya/tuya.classes.integration.ts +69 -98
  487. package/ts/integrations/tuya/tuya.classes.runtime.ts +70 -24
  488. package/ts/integrations/tuya/tuya.mapper.ts +1 -1
  489. package/ts/integrations/tuya/tuya.types.ts +4 -0
  490. package/ts/integrations/tuya/tuya.validation.ts +5 -8
  491. package/ts/integrations/webostv/readme.md +115 -22
  492. package/ts/integrations/webostv/webostv.classes.client.ts +123 -50
  493. package/ts/integrations/webostv/webostv.classes.configflow.ts +35 -15
  494. package/ts/integrations/webostv/webostv.classes.integration.ts +44 -37
  495. package/ts/integrations/webostv/webostv.mapper.ts +4 -4
  496. package/ts/integrations/webostv/webostv.types.ts +18 -3
  497. package/ts/integrations/webostv/webostv.validation.ts +22 -1
  498. package/ts/integrations/wiz/index.ts +1 -0
  499. package/ts/integrations/wiz/wiz.classes.client.ts +84 -254
  500. package/ts/integrations/wiz/wiz.classes.configflow.ts +39 -70
  501. package/ts/integrations/wiz/wiz.classes.integration.ts +189 -45
  502. package/ts/integrations/wiz/wiz.discovery.ts +48 -291
  503. package/ts/integrations/wiz/wiz.mapper.ts +81 -754
  504. package/ts/integrations/wiz/wiz.types.ts +24 -224
  505. package/ts/integrations/wiz/wiz.validation.ts +73 -0
  506. package/ts/integrations/yamaha/index.ts +1 -0
  507. package/ts/integrations/yamaha/yamaha.classes.client.ts +150 -6
  508. package/ts/integrations/yamaha/yamaha.classes.configflow.ts +100 -5
  509. package/ts/integrations/yamaha/yamaha.classes.integration.ts +236 -8
  510. package/ts/integrations/yamaha/yamaha.discovery.ts +52 -3
  511. package/ts/integrations/yamaha/yamaha.mapper.ts +65 -13
  512. package/ts/integrations/yamaha/yamaha.types.ts +77 -97
  513. package/ts/integrations/yamaha/yamaha.validation.ts +86 -0
  514. package/ts/integrations/zigbee2mqtt/readme.md +22 -27
  515. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.configflow.ts +16 -7
  516. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.integration.ts +53 -6
  517. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.runtime.ts +62 -49
  518. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.mapper.ts +17 -11
  519. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.types.ts +35 -2
  520. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.validation.ts +53 -7
  521. package/ts/integrations/zwave_js/readme.md +42 -9
  522. package/ts/integrations/zwave_js/zwave_js.classes.client.ts +155 -31
  523. package/ts/integrations/zwave_js/zwave_js.classes.configflow.ts +55 -31
  524. package/ts/integrations/zwave_js/zwave_js.classes.integration.ts +57 -35
  525. package/ts/integrations/zwave_js/zwave_js.discovery.ts +2 -2
  526. package/ts/integrations/zwave_js/zwave_js.mapper.ts +37 -23
  527. package/ts/integrations/zwave_js/zwave_js.types.ts +26 -3
  528. package/ts/integrations/zwave_js/zwave_js.validation.ts +52 -18
  529. package/ts/plugins.ts +2 -1
@@ -1,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());