@switchbot/homebridge-switchbot 5.0.0-beta.9 → 5.0.0-beta.91

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 (448) hide show
  1. package/.github/ISSUE_TEMPLATE/e2e-verification.md +36 -0
  2. package/.github/workflows/ci.yml +32 -0
  3. package/.github/workflows/manual-e2e.yml +115 -0
  4. package/.github/workflows/release.yml +0 -4
  5. package/CHANGELOG.md +35 -0
  6. package/E2E-VERIFICATION.md +121 -0
  7. package/MIGRATION.md +44 -0
  8. package/README.md +56 -3
  9. package/config.schema.json +91 -14787
  10. package/dist/deviceFactory.d.ts +13 -0
  11. package/dist/deviceFactory.d.ts.map +1 -0
  12. package/dist/deviceFactory.js +81 -0
  13. package/dist/deviceFactory.js.map +1 -0
  14. package/dist/devices/deviceBase.d.ts +50 -0
  15. package/dist/devices/deviceBase.d.ts.map +1 -0
  16. package/dist/devices/deviceBase.js +119 -0
  17. package/dist/devices/deviceBase.js.map +1 -0
  18. package/dist/devices/genericDevice.d.ts +283 -0
  19. package/dist/devices/genericDevice.d.ts.map +1 -0
  20. package/dist/devices/genericDevice.js +1035 -0
  21. package/dist/devices/genericDevice.js.map +1 -0
  22. package/dist/homebridge-ui/public/index.html +630 -246
  23. package/dist/homebridge-ui/server.d.ts +3 -1
  24. package/dist/homebridge-ui/server.d.ts.map +1 -1
  25. package/dist/homebridge-ui/server.js +367 -36
  26. package/dist/homebridge-ui/server.js.map +1 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +4 -32
  29. package/dist/index.js.map +1 -1
  30. package/dist/platform.d.ts +35 -0
  31. package/dist/platform.d.ts.map +1 -0
  32. package/dist/platform.js +514 -0
  33. package/dist/platform.js.map +1 -0
  34. package/dist/settings.d.ts +10 -249
  35. package/dist/settings.d.ts.map +1 -1
  36. package/dist/settings.js +5 -30
  37. package/dist/settings.js.map +1 -1
  38. package/dist/switchbotClient.d.ts +32 -0
  39. package/dist/switchbotClient.d.ts.map +1 -0
  40. package/dist/switchbotClient.js +194 -0
  41. package/dist/switchbotClient.js.map +1 -0
  42. package/dist/utils.d.ts +39 -50
  43. package/dist/utils.d.ts.map +1 -1
  44. package/dist/utils.js +39 -688
  45. package/dist/utils.js.map +1 -1
  46. package/docs/assets/highlight.css +14 -0
  47. package/docs/assets/icons.js +1 -1
  48. package/docs/assets/icons.svg +1 -1
  49. package/docs/assets/main.js +2 -2
  50. package/docs/assets/style.css +3 -3
  51. package/docs/index.html +77 -13
  52. package/docs/variables/default.html +1 -1
  53. package/eslint.config.js +2 -8
  54. package/package.json +21 -28
  55. package/scripts/e2e/README.md +25 -0
  56. package/scripts/e2e/curtain-e2e.sh +70 -0
  57. package/scripts/e2e/fan-e2e.sh +75 -0
  58. package/scripts/e2e/light-advanced-e2e.sh +97 -0
  59. package/scripts/e2e/light-e2e.sh +75 -0
  60. package/scripts/e2e/list-accessories.sh +19 -0
  61. package/scripts/e2e/lock-e2e.sh +65 -0
  62. package/scripts/generate-matter-maps.js +60 -0
  63. package/scripts/run-e2e-local.sh +14 -0
  64. package/src/deviceFactory.ts +122 -0
  65. package/src/devices/deviceBase.ts +141 -0
  66. package/src/devices/genericDevice.ts +965 -0
  67. package/src/homebridge-ui/public/index.html +630 -246
  68. package/src/homebridge-ui/server.ts +434 -41
  69. package/src/index.ts +4 -33
  70. package/src/platform.ts +515 -0
  71. package/src/settings.ts +12 -277
  72. package/src/switchbotClient.ts +203 -0
  73. package/src/utils.ts +45 -713
  74. package/test/accessory-restore.spec.ts +73 -0
  75. package/test/device-mapping.spec.ts +37 -0
  76. package/test/deviceFactory.spec.ts +18 -0
  77. package/test/e2e/run-e2e.spec.ts +50 -0
  78. package/test/fan-swing.spec.ts +29 -0
  79. package/test/helpers/matter-harness.ts +53 -0
  80. package/test/lock-users.spec.ts +44 -0
  81. package/test/matter-childbridge.spec.ts +55 -0
  82. package/test/matter-descriptors.spec.ts +97 -0
  83. package/test/matter-device-state.spec.ts +101 -0
  84. package/test/matter-integration.spec.ts +70 -0
  85. package/test/platform.integration.spec.ts +55 -0
  86. package/test/switchbot-client-debounce.spec.ts +131 -0
  87. package/test/switchbot-client-openapi.spec.ts +56 -0
  88. package/test/switchbotClient.spec.ts +10 -0
  89. package/test/utils.spec.ts +20 -0
  90. package/vitest.config.ts +7 -0
  91. package/coverage/base.css +0 -224
  92. package/coverage/block-navigation.js +0 -87
  93. package/coverage/clover.xml +0 -15847
  94. package/coverage/coverage-final.json +0 -42
  95. package/coverage/docs/assets/dmt/dmt-component-data.js.html +0 -85
  96. package/coverage/docs/assets/dmt/dmt-components.js.html +0 -286
  97. package/coverage/docs/assets/dmt/index.html +0 -131
  98. package/coverage/docs/assets/hierarchy.js.html +0 -85
  99. package/coverage/docs/assets/icons.js.html +0 -136
  100. package/coverage/docs/assets/index.html +0 -146
  101. package/coverage/docs/assets/main.js.html +0 -265
  102. package/coverage/favicon.png +0 -0
  103. package/coverage/index.html +0 -191
  104. package/coverage/prettify.css +0 -1
  105. package/coverage/prettify.js +0 -2
  106. package/coverage/sort-arrow-sprite.png +0 -0
  107. package/coverage/sorter.js +0 -196
  108. package/coverage/src/device/blindtilt.ts.html +0 -3238
  109. package/coverage/src/device/bot.ts.html +0 -2803
  110. package/coverage/src/device/ceilinglight.ts.html +0 -2338
  111. package/coverage/src/device/colorbulb.ts.html +0 -2824
  112. package/coverage/src/device/contact.ts.html +0 -1465
  113. package/coverage/src/device/curtain.ts.html +0 -2869
  114. package/coverage/src/device/device.ts.html +0 -2500
  115. package/coverage/src/device/fan.ts.html +0 -2242
  116. package/coverage/src/device/hub.ts.html +0 -1408
  117. package/coverage/src/device/humidifier.ts.html +0 -2116
  118. package/coverage/src/device/index.html +0 -416
  119. package/coverage/src/device/iosensor.ts.html +0 -1375
  120. package/coverage/src/device/lightstrip.ts.html +0 -2617
  121. package/coverage/src/device/lock.ts.html +0 -1963
  122. package/coverage/src/device/meter.ts.html +0 -1372
  123. package/coverage/src/device/meterplus.ts.html +0 -1384
  124. package/coverage/src/device/meterpro.ts.html +0 -1618
  125. package/coverage/src/device/motion.ts.html +0 -1264
  126. package/coverage/src/device/plug.ts.html +0 -1372
  127. package/coverage/src/device/relayswitch.ts.html +0 -2284
  128. package/coverage/src/device/robotvacuumcleaner.ts.html +0 -1810
  129. package/coverage/src/device/waterdetector.ts.html +0 -1294
  130. package/coverage/src/homebridge-ui/index.html +0 -116
  131. package/coverage/src/homebridge-ui/server.ts.html +0 -229
  132. package/coverage/src/index.html +0 -161
  133. package/coverage/src/index.ts.html +0 -124
  134. package/coverage/src/irdevice/airconditioner.ts.html +0 -1687
  135. package/coverage/src/irdevice/airpurifier.ts.html +0 -844
  136. package/coverage/src/irdevice/camera.ts.html +0 -475
  137. package/coverage/src/irdevice/fan.ts.html +0 -766
  138. package/coverage/src/irdevice/index.html +0 -251
  139. package/coverage/src/irdevice/irdevice.ts.html +0 -1117
  140. package/coverage/src/irdevice/light.ts.html +0 -826
  141. package/coverage/src/irdevice/other.ts.html +0 -2458
  142. package/coverage/src/irdevice/tv.ts.html +0 -1222
  143. package/coverage/src/irdevice/vacuumcleaner.ts.html +0 -466
  144. package/coverage/src/irdevice/waterheater.ts.html +0 -469
  145. package/coverage/src/platform.ts.html +0 -8776
  146. package/coverage/src/settings.ts.html +0 -934
  147. package/coverage/src/utils.ts.html +0 -2092
  148. package/dist/devices-hap/airpurifier.d.ts +0 -54
  149. package/dist/devices-hap/airpurifier.d.ts.map +0 -1
  150. package/dist/devices-hap/airpurifier.js +0 -527
  151. package/dist/devices-hap/airpurifier.js.map +0 -1
  152. package/dist/devices-hap/blindtilt.d.ts +0 -90
  153. package/dist/devices-hap/blindtilt.d.ts.map +0 -1
  154. package/dist/devices-hap/blindtilt.js +0 -974
  155. package/dist/devices-hap/blindtilt.js.map +0 -1
  156. package/dist/devices-hap/bot.d.ts +0 -102
  157. package/dist/devices-hap/bot.d.ts.map +0 -1
  158. package/dist/devices-hap/bot.js +0 -811
  159. package/dist/devices-hap/bot.js.map +0 -1
  160. package/dist/devices-hap/ceilinglight.d.ts +0 -85
  161. package/dist/devices-hap/ceilinglight.d.ts.map +0 -1
  162. package/dist/devices-hap/ceilinglight.js +0 -701
  163. package/dist/devices-hap/ceilinglight.js.map +0 -1
  164. package/dist/devices-hap/colorbulb.d.ts +0 -88
  165. package/dist/devices-hap/colorbulb.d.ts.map +0 -1
  166. package/dist/devices-hap/colorbulb.js +0 -881
  167. package/dist/devices-hap/colorbulb.js.map +0 -1
  168. package/dist/devices-hap/contact.d.ts +0 -44
  169. package/dist/devices-hap/contact.d.ts.map +0 -1
  170. package/dist/devices-hap/contact.js +0 -409
  171. package/dist/devices-hap/contact.js.map +0 -1
  172. package/dist/devices-hap/curtain.d.ts +0 -73
  173. package/dist/devices-hap/curtain.d.ts.map +0 -1
  174. package/dist/devices-hap/curtain.js +0 -869
  175. package/dist/devices-hap/curtain.js.map +0 -1
  176. package/dist/devices-hap/device.d.ts +0 -98
  177. package/dist/devices-hap/device.d.ts.map +0 -1
  178. package/dist/devices-hap/device.js +0 -749
  179. package/dist/devices-hap/device.js.map +0 -1
  180. package/dist/devices-hap/fan.d.ts +0 -69
  181. package/dist/devices-hap/fan.d.ts.map +0 -1
  182. package/dist/devices-hap/fan.js +0 -649
  183. package/dist/devices-hap/fan.js.map +0 -1
  184. package/dist/devices-hap/hub.d.ts +0 -37
  185. package/dist/devices-hap/hub.d.ts.map +0 -1
  186. package/dist/devices-hap/hub.js +0 -392
  187. package/dist/devices-hap/hub.js.map +0 -1
  188. package/dist/devices-hap/humidifier.d.ts +0 -68
  189. package/dist/devices-hap/humidifier.d.ts.map +0 -1
  190. package/dist/devices-hap/humidifier.js +0 -628
  191. package/dist/devices-hap/humidifier.js.map +0 -1
  192. package/dist/devices-hap/iosensor.d.ts +0 -42
  193. package/dist/devices-hap/iosensor.d.ts.map +0 -1
  194. package/dist/devices-hap/iosensor.js +0 -382
  195. package/dist/devices-hap/iosensor.js.map +0 -1
  196. package/dist/devices-hap/lightstrip.d.ts +0 -79
  197. package/dist/devices-hap/lightstrip.d.ts.map +0 -1
  198. package/dist/devices-hap/lightstrip.js +0 -797
  199. package/dist/devices-hap/lightstrip.js.map +0 -1
  200. package/dist/devices-hap/lock.d.ts +0 -53
  201. package/dist/devices-hap/lock.d.ts.map +0 -1
  202. package/dist/devices-hap/lock.js +0 -561
  203. package/dist/devices-hap/lock.js.map +0 -1
  204. package/dist/devices-hap/meter.d.ts +0 -37
  205. package/dist/devices-hap/meter.d.ts.map +0 -1
  206. package/dist/devices-hap/meter.js +0 -379
  207. package/dist/devices-hap/meter.js.map +0 -1
  208. package/dist/devices-hap/meterplus.d.ts +0 -42
  209. package/dist/devices-hap/meterplus.d.ts.map +0 -1
  210. package/dist/devices-hap/meterplus.js +0 -384
  211. package/dist/devices-hap/meterplus.js.map +0 -1
  212. package/dist/devices-hap/meterpro.d.ts +0 -43
  213. package/dist/devices-hap/meterpro.d.ts.map +0 -1
  214. package/dist/devices-hap/meterpro.js +0 -468
  215. package/dist/devices-hap/meterpro.js.map +0 -1
  216. package/dist/devices-hap/motion.d.ts +0 -42
  217. package/dist/devices-hap/motion.d.ts.map +0 -1
  218. package/dist/devices-hap/motion.js +0 -345
  219. package/dist/devices-hap/motion.js.map +0 -1
  220. package/dist/devices-hap/plug.d.ts +0 -49
  221. package/dist/devices-hap/plug.d.ts.map +0 -1
  222. package/dist/devices-hap/plug.js +0 -395
  223. package/dist/devices-hap/plug.js.map +0 -1
  224. package/dist/devices-hap/relayswitch.d.ts +0 -96
  225. package/dist/devices-hap/relayswitch.d.ts.map +0 -1
  226. package/dist/devices-hap/relayswitch.js +0 -642
  227. package/dist/devices-hap/relayswitch.js.map +0 -1
  228. package/dist/devices-hap/robotvacuumcleaner.d.ts +0 -54
  229. package/dist/devices-hap/robotvacuumcleaner.d.ts.map +0 -1
  230. package/dist/devices-hap/robotvacuumcleaner.js +0 -523
  231. package/dist/devices-hap/robotvacuumcleaner.js.map +0 -1
  232. package/dist/devices-hap/waterdetector.d.ts +0 -41
  233. package/dist/devices-hap/waterdetector.d.ts.map +0 -1
  234. package/dist/devices-hap/waterdetector.js +0 -356
  235. package/dist/devices-hap/waterdetector.js.map +0 -1
  236. package/dist/devices-matter/BaseMatterAccessory.d.ts +0 -63
  237. package/dist/devices-matter/BaseMatterAccessory.d.ts.map +0 -1
  238. package/dist/devices-matter/BaseMatterAccessory.js +0 -100
  239. package/dist/devices-matter/BaseMatterAccessory.js.map +0 -1
  240. package/dist/devices-matter/ColorLightAccessory.d.ts +0 -20
  241. package/dist/devices-matter/ColorLightAccessory.d.ts.map +0 -1
  242. package/dist/devices-matter/ColorLightAccessory.js +0 -95
  243. package/dist/devices-matter/ColorLightAccessory.js.map +0 -1
  244. package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts +0 -18
  245. package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts.map +0 -1
  246. package/dist/devices-matter/ColorTemperatureLightAccessory.js +0 -78
  247. package/dist/devices-matter/ColorTemperatureLightAccessory.js.map +0 -1
  248. package/dist/devices-matter/ContactSensorAccessory.d.ts +0 -12
  249. package/dist/devices-matter/ContactSensorAccessory.d.ts.map +0 -1
  250. package/dist/devices-matter/ContactSensorAccessory.js +0 -34
  251. package/dist/devices-matter/ContactSensorAccessory.js.map +0 -1
  252. package/dist/devices-matter/DimmableLightAccessory.d.ts +0 -58
  253. package/dist/devices-matter/DimmableLightAccessory.d.ts.map +0 -1
  254. package/dist/devices-matter/DimmableLightAccessory.js +0 -167
  255. package/dist/devices-matter/DimmableLightAccessory.js.map +0 -1
  256. package/dist/devices-matter/DoorLockAccessory.d.ts +0 -14
  257. package/dist/devices-matter/DoorLockAccessory.d.ts.map +0 -1
  258. package/dist/devices-matter/DoorLockAccessory.js +0 -50
  259. package/dist/devices-matter/DoorLockAccessory.js.map +0 -1
  260. package/dist/devices-matter/ExtendedColorLightAccessory.d.ts +0 -21
  261. package/dist/devices-matter/ExtendedColorLightAccessory.d.ts.map +0 -1
  262. package/dist/devices-matter/ExtendedColorLightAccessory.js +0 -107
  263. package/dist/devices-matter/ExtendedColorLightAccessory.js.map +0 -1
  264. package/dist/devices-matter/FanAccessory.d.ts +0 -16
  265. package/dist/devices-matter/FanAccessory.d.ts.map +0 -1
  266. package/dist/devices-matter/FanAccessory.js +0 -81
  267. package/dist/devices-matter/FanAccessory.js.map +0 -1
  268. package/dist/devices-matter/HumiditySensorAccessory.d.ts +0 -12
  269. package/dist/devices-matter/HumiditySensorAccessory.d.ts.map +0 -1
  270. package/dist/devices-matter/HumiditySensorAccessory.js +0 -34
  271. package/dist/devices-matter/HumiditySensorAccessory.js.map +0 -1
  272. package/dist/devices-matter/LeakSensorAccessory.d.ts +0 -12
  273. package/dist/devices-matter/LeakSensorAccessory.d.ts.map +0 -1
  274. package/dist/devices-matter/LeakSensorAccessory.js +0 -33
  275. package/dist/devices-matter/LeakSensorAccessory.js.map +0 -1
  276. package/dist/devices-matter/LightSensorAccessory.d.ts +0 -12
  277. package/dist/devices-matter/LightSensorAccessory.d.ts.map +0 -1
  278. package/dist/devices-matter/LightSensorAccessory.js +0 -34
  279. package/dist/devices-matter/LightSensorAccessory.js.map +0 -1
  280. package/dist/devices-matter/OccupancySensorAccessory.d.ts +0 -12
  281. package/dist/devices-matter/OccupancySensorAccessory.d.ts.map +0 -1
  282. package/dist/devices-matter/OccupancySensorAccessory.js +0 -39
  283. package/dist/devices-matter/OccupancySensorAccessory.js.map +0 -1
  284. package/dist/devices-matter/OnOffLightAccessory.d.ts +0 -38
  285. package/dist/devices-matter/OnOffLightAccessory.d.ts.map +0 -1
  286. package/dist/devices-matter/OnOffLightAccessory.js +0 -118
  287. package/dist/devices-matter/OnOffLightAccessory.js.map +0 -1
  288. package/dist/devices-matter/OnOffOutletAccessory.d.ts +0 -12
  289. package/dist/devices-matter/OnOffOutletAccessory.d.ts.map +0 -1
  290. package/dist/devices-matter/OnOffOutletAccessory.js +0 -40
  291. package/dist/devices-matter/OnOffOutletAccessory.js.map +0 -1
  292. package/dist/devices-matter/OnOffSwitchAccessory.d.ts +0 -14
  293. package/dist/devices-matter/OnOffSwitchAccessory.d.ts.map +0 -1
  294. package/dist/devices-matter/OnOffSwitchAccessory.js +0 -42
  295. package/dist/devices-matter/OnOffSwitchAccessory.js.map +0 -1
  296. package/dist/devices-matter/RoboticVacuumAccessory.d.ts +0 -68
  297. package/dist/devices-matter/RoboticVacuumAccessory.d.ts.map +0 -1
  298. package/dist/devices-matter/RoboticVacuumAccessory.js +0 -334
  299. package/dist/devices-matter/RoboticVacuumAccessory.js.map +0 -1
  300. package/dist/devices-matter/SmokeCOAlarmAccessory.d.ts +0 -11
  301. package/dist/devices-matter/SmokeCOAlarmAccessory.d.ts.map +0 -1
  302. package/dist/devices-matter/SmokeCOAlarmAccessory.js +0 -49
  303. package/dist/devices-matter/SmokeCOAlarmAccessory.js.map +0 -1
  304. package/dist/devices-matter/TemperatureSensorAccessory.d.ts +0 -12
  305. package/dist/devices-matter/TemperatureSensorAccessory.d.ts.map +0 -1
  306. package/dist/devices-matter/TemperatureSensorAccessory.js +0 -36
  307. package/dist/devices-matter/TemperatureSensorAccessory.js.map +0 -1
  308. package/dist/devices-matter/ThermostatAccessory.d.ts +0 -19
  309. package/dist/devices-matter/ThermostatAccessory.d.ts.map +0 -1
  310. package/dist/devices-matter/ThermostatAccessory.js +0 -95
  311. package/dist/devices-matter/ThermostatAccessory.js.map +0 -1
  312. package/dist/devices-matter/VenetianBlindAccessory.d.ts +0 -19
  313. package/dist/devices-matter/VenetianBlindAccessory.d.ts.map +0 -1
  314. package/dist/devices-matter/VenetianBlindAccessory.js +0 -99
  315. package/dist/devices-matter/VenetianBlindAccessory.js.map +0 -1
  316. package/dist/devices-matter/WindowBlindAccessory.d.ts +0 -17
  317. package/dist/devices-matter/WindowBlindAccessory.d.ts.map +0 -1
  318. package/dist/devices-matter/WindowBlindAccessory.js +0 -80
  319. package/dist/devices-matter/WindowBlindAccessory.js.map +0 -1
  320. package/dist/devices-matter/custom/PowerStripAccessory.d.ts +0 -97
  321. package/dist/devices-matter/custom/PowerStripAccessory.d.ts.map +0 -1
  322. package/dist/devices-matter/custom/PowerStripAccessory.js +0 -265
  323. package/dist/devices-matter/custom/PowerStripAccessory.js.map +0 -1
  324. package/dist/devices-matter/custom/index.d.ts +0 -8
  325. package/dist/devices-matter/custom/index.d.ts.map +0 -1
  326. package/dist/devices-matter/custom/index.js +0 -8
  327. package/dist/devices-matter/custom/index.js.map +0 -1
  328. package/dist/devices-matter/index.d.ts +0 -29
  329. package/dist/devices-matter/index.d.ts.map +0 -1
  330. package/dist/devices-matter/index.js +0 -28
  331. package/dist/devices-matter/index.js.map +0 -1
  332. package/dist/index.test.d.ts +0 -2
  333. package/dist/index.test.d.ts.map +0 -1
  334. package/dist/index.test.js +0 -14
  335. package/dist/index.test.js.map +0 -1
  336. package/dist/irdevice/airconditioner.d.ts +0 -61
  337. package/dist/irdevice/airconditioner.d.ts.map +0 -1
  338. package/dist/irdevice/airconditioner.js +0 -472
  339. package/dist/irdevice/airconditioner.js.map +0 -1
  340. package/dist/irdevice/airpurifier.d.ts +0 -50
  341. package/dist/irdevice/airpurifier.d.ts.map +0 -1
  342. package/dist/irdevice/airpurifier.js +0 -213
  343. package/dist/irdevice/airpurifier.js.map +0 -1
  344. package/dist/irdevice/camera.d.ts +0 -32
  345. package/dist/irdevice/camera.d.ts.map +0 -1
  346. package/dist/irdevice/camera.js +0 -107
  347. package/dist/irdevice/camera.js.map +0 -1
  348. package/dist/irdevice/fan.d.ts +0 -36
  349. package/dist/irdevice/fan.d.ts.map +0 -1
  350. package/dist/irdevice/fan.js +0 -200
  351. package/dist/irdevice/fan.js.map +0 -1
  352. package/dist/irdevice/irdevice.d.ts +0 -68
  353. package/dist/irdevice/irdevice.d.ts.map +0 -1
  354. package/dist/irdevice/irdevice.js +0 -298
  355. package/dist/irdevice/irdevice.js.map +0 -1
  356. package/dist/irdevice/light.d.ts +0 -36
  357. package/dist/irdevice/light.d.ts.map +0 -1
  358. package/dist/irdevice/light.js +0 -206
  359. package/dist/irdevice/light.js.map +0 -1
  360. package/dist/irdevice/other.d.ts +0 -57
  361. package/dist/irdevice/other.d.ts.map +0 -1
  362. package/dist/irdevice/other.js +0 -778
  363. package/dist/irdevice/other.js.map +0 -1
  364. package/dist/irdevice/tv.d.ts +0 -45
  365. package/dist/irdevice/tv.d.ts.map +0 -1
  366. package/dist/irdevice/tv.js +0 -327
  367. package/dist/irdevice/tv.js.map +0 -1
  368. package/dist/irdevice/vacuumcleaner.d.ts +0 -28
  369. package/dist/irdevice/vacuumcleaner.d.ts.map +0 -1
  370. package/dist/irdevice/vacuumcleaner.js +0 -104
  371. package/dist/irdevice/vacuumcleaner.js.map +0 -1
  372. package/dist/irdevice/waterheater.d.ts +0 -30
  373. package/dist/irdevice/waterheater.d.ts.map +0 -1
  374. package/dist/irdevice/waterheater.js +0 -105
  375. package/dist/irdevice/waterheater.js.map +0 -1
  376. package/dist/platform-hap.d.ts +0 -149
  377. package/dist/platform-hap.d.ts.map +0 -1
  378. package/dist/platform-hap.js +0 -2861
  379. package/dist/platform-hap.js.map +0 -1
  380. package/dist/platform-matter.d.ts +0 -120
  381. package/dist/platform-matter.d.ts.map +0 -1
  382. package/dist/platform-matter.js +0 -966
  383. package/dist/platform-matter.js.map +0 -1
  384. package/dist/verifyconfig.test.d.ts +0 -2
  385. package/dist/verifyconfig.test.d.ts.map +0 -1
  386. package/dist/verifyconfig.test.js +0 -167
  387. package/dist/verifyconfig.test.js.map +0 -1
  388. package/src/custom.d.ts +0 -7
  389. package/src/devices-hap/airpurifier.ts +0 -563
  390. package/src/devices-hap/blindtilt.ts +0 -1049
  391. package/src/devices-hap/bot.ts +0 -900
  392. package/src/devices-hap/ceilinglight.ts +0 -742
  393. package/src/devices-hap/colorbulb.ts +0 -904
  394. package/src/devices-hap/contact.ts +0 -457
  395. package/src/devices-hap/curtain.ts +0 -944
  396. package/src/devices-hap/device.ts +0 -811
  397. package/src/devices-hap/fan.ts +0 -711
  398. package/src/devices-hap/hub.ts +0 -439
  399. package/src/devices-hap/humidifier.ts +0 -669
  400. package/src/devices-hap/iosensor.ts +0 -427
  401. package/src/devices-hap/lightstrip.ts +0 -836
  402. package/src/devices-hap/lock.ts +0 -620
  403. package/src/devices-hap/meter.ts +0 -426
  404. package/src/devices-hap/meterplus.ts +0 -430
  405. package/src/devices-hap/meterpro.ts +0 -522
  406. package/src/devices-hap/motion.ts +0 -390
  407. package/src/devices-hap/plug.ts +0 -423
  408. package/src/devices-hap/relayswitch.ts +0 -727
  409. package/src/devices-hap/robotvacuumcleaner.ts +0 -568
  410. package/src/devices-hap/waterdetector.ts +0 -400
  411. package/src/devices-matter/BaseMatterAccessory.ts +0 -131
  412. package/src/devices-matter/ColorLightAccessory.ts +0 -110
  413. package/src/devices-matter/ColorTemperatureLightAccessory.ts +0 -92
  414. package/src/devices-matter/ContactSensorAccessory.ts +0 -41
  415. package/src/devices-matter/DimmableLightAccessory.ts +0 -192
  416. package/src/devices-matter/DoorLockAccessory.ts +0 -60
  417. package/src/devices-matter/ExtendedColorLightAccessory.ts +0 -123
  418. package/src/devices-matter/FanAccessory.ts +0 -95
  419. package/src/devices-matter/HumiditySensorAccessory.ts +0 -41
  420. package/src/devices-matter/LeakSensorAccessory.ts +0 -40
  421. package/src/devices-matter/LightSensorAccessory.ts +0 -41
  422. package/src/devices-matter/OccupancySensorAccessory.ts +0 -48
  423. package/src/devices-matter/OnOffLightAccessory.ts +0 -133
  424. package/src/devices-matter/OnOffOutletAccessory.ts +0 -46
  425. package/src/devices-matter/OnOffSwitchAccessory.ts +0 -51
  426. package/src/devices-matter/RoboticVacuumAccessory.ts +0 -407
  427. package/src/devices-matter/SmokeCOAlarmAccessory.ts +0 -59
  428. package/src/devices-matter/TemperatureSensorAccessory.ts +0 -43
  429. package/src/devices-matter/ThermostatAccessory.ts +0 -110
  430. package/src/devices-matter/VenetianBlindAccessory.ts +0 -115
  431. package/src/devices-matter/WindowBlindAccessory.ts +0 -92
  432. package/src/devices-matter/custom/PowerStripAccessory.ts +0 -309
  433. package/src/devices-matter/custom/index.ts +0 -8
  434. package/src/devices-matter/index.ts +0 -29
  435. package/src/index.test.ts +0 -19
  436. package/src/irdevice/airconditioner.ts +0 -533
  437. package/src/irdevice/airpurifier.ts +0 -252
  438. package/src/irdevice/camera.ts +0 -129
  439. package/src/irdevice/fan.ts +0 -226
  440. package/src/irdevice/irdevice.ts +0 -344
  441. package/src/irdevice/light.ts +0 -246
  442. package/src/irdevice/other.ts +0 -790
  443. package/src/irdevice/tv.ts +0 -378
  444. package/src/irdevice/vacuumcleaner.ts +0 -126
  445. package/src/irdevice/waterheater.ts +0 -127
  446. package/src/platform-hap.ts +0 -2997
  447. package/src/platform-matter.ts +0 -1092
  448. package/src/verifyconfig.test.ts +0 -197
package/src/settings.ts CHANGED
@@ -1,283 +1,18 @@
1
- /* Copyright(C) 2017-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
2
- *
3
- * settings.ts: @switchbot/homebridge-switchbot platform class.
4
- */
5
- import type { IClientOptions } from 'async-mqtt'
6
- import type { PlatformConfig } from 'homebridge'
7
- /*
8
- * For Testing Locally:
9
- * import type { device, irdevice, SwitchBotBLEModel, SwitchBotBLEModelFriendlyName, SwitchBotBLEModelName } from '/Users/Shared/GitHub/OpenWonderLabs/node-switchbot/dist/index.js';
10
- */
11
- import type { device, irdevice, SwitchBotBLEModel, SwitchBotBLEModelFriendlyName, SwitchBotBLEModelName, SwitchBotModel } from 'node-switchbot'
12
- /**
13
- * This is the name of the platform that users will use to register the plugin in the Homebridge config.json
14
- */
15
- export const PLATFORM_NAME = 'SwitchBot'
16
-
17
- /**
18
- * This must match the name of your plugin as defined the package.json
19
- */
20
1
  export const PLUGIN_NAME = '@switchbot/homebridge-switchbot'
2
+ export const PLATFORM_NAME = 'SwitchBot'
21
3
 
22
- // Config
23
- export interface SwitchBotPlatformConfig extends PlatformConfig {
24
- credentials?: credentials
25
- options?: options
26
- }
27
- interface credentials {
28
- token?: string
29
- secret?: string
30
- notice?: string
31
- }
32
-
33
- export interface options {
34
- devices?: devicesConfig[]
35
- deviceConfig?: { [deviceType: string]: devicesConfig }
36
- irdevices?: irDevicesConfig[]
37
- irdeviceConfig?: { [remoteType: string]: irDevicesConfig }
38
- allowInvalidCharacters?: boolean
39
- mqttURL?: string
40
- mqttOptions?: IClientOptions
41
- mqttPubOptions?: IClientOptions
42
- BLE?: boolean
43
- discoverBLE?: boolean
44
- disableLogsforBLE?: boolean
45
- disableLogsforOpenAPI?: boolean
46
- hostname?: string
47
- webhookURL?: string
48
- maxRetries?: number
49
- delayBetweenRetries?: number
50
- refreshRate?: number
51
- updateRate?: number
52
- pushRate?: number
53
- logging?: string
54
- };
55
-
56
- export type devicesConfig = botConfig | relaySwitch1Config | relaySwitch1PMConfig | meterConfig | meterProConfig | indoorOutdoorSensorConfig | humidifierConfig | curtainConfig | blindTiltConfig | contactConfig | motionConfig | waterDetectorConfig | plugConfig | colorBulbConfig | stripLightConfig | ceilingLightConfig | lockConfig | hubConfig
57
-
58
- export interface BaseDeviceConfig extends device {
59
- bleMac?: string
60
- model: SwitchBotModel
61
- bleModel: SwitchBotBLEModel
62
- bleModelName: SwitchBotBLEModelName
63
- bleModelFriednlyName: SwitchBotBLEModelFriendlyName
64
- configDeviceType: string
65
- configDeviceName?: string
66
- deviceId: string
67
- external?: boolean
68
- refreshRate?: number
69
- updateRate?: number
70
- pushRate?: number
71
- firmware?: string
72
- logging?: string
73
- connectionType?: string
74
- customBLEaddress?: string
75
- scanDuration?: number
76
- hide_device?: boolean
77
- offline?: boolean
78
- maxRetry?: number
79
- maxRetries?: number
80
- delayBetweenRetries?: number
81
- disableCaching?: boolean
82
- disablePlatformBLE?: boolean
83
- mqttURL?: string
84
- mqttOptions?: IClientOptions
85
- mqttPubOptions?: IClientOptions
86
- history?: boolean
87
- webhook?: boolean
4
+ export interface SwitchBotPluginConfig {
5
+ openApiToken?: string
6
+ openApiSecret?: string
7
+ preferMatter?: boolean
8
+ enableMatter?: boolean
9
+ // other plugin-specific configuration
10
+ [key: string]: any
88
11
  }
89
12
 
90
- export interface botConfig extends BaseDeviceConfig {
91
- configDeviceType: 'Bot'
92
- mode?: string
93
- type: string
94
- doublePress?: number
95
- pushRatePress?: number
96
- allowPush?: boolean
97
- multiPress?: boolean
98
- };
99
-
100
- export interface relaySwitch1Config extends BaseDeviceConfig {
101
- configDeviceType: 'Relay Switch 1'
102
- type: string
103
- allowPush?: boolean
104
- };
105
-
106
- export interface relaySwitch1PMConfig extends BaseDeviceConfig {
107
- configDeviceType: 'Relay Switch 1PM'
108
- type: string
109
- allowPush?: boolean
110
- };
111
-
112
- export interface meterConfig extends BaseDeviceConfig {
113
- configDeviceType: 'Meter' | 'MeterPlus'
114
- hide_temperature?: boolean
115
- convertUnitTo?: string
116
- hide_humidity?: boolean
117
- };
118
-
119
- export interface meterProConfig extends BaseDeviceConfig {
120
- configDeviceType: 'Meter Pro' | 'MeterPro(CO2)'
121
- hide_temperature?: boolean
122
- convertUnitTo?: string
123
- hide_humidity?: boolean
124
- hide_co2?: boolean
125
- };
126
-
127
- export interface indoorOutdoorSensorConfig extends BaseDeviceConfig {
128
- configDeviceType: 'WoIOSensor'
129
- hide_temperature?: boolean
130
- convertUnitTo?: string
131
- hide_humidity?: boolean
132
- };
133
-
134
- export interface humidifierConfig extends BaseDeviceConfig {
135
- configDeviceType: 'Humidifier' | 'Humidifier2'
136
- hide_temperature?: boolean
137
- convertUnitTo?: string
138
- set_minStep?: number
139
- };
140
-
141
- export interface curtainConfig extends BaseDeviceConfig {
142
- configDeviceType: 'Curtain' | 'Curtain3' | 'WoRollerShade' | 'Roller Shade'
143
- disable_group?: boolean
144
- hide_lightsensor?: boolean
145
- set_minLux?: number
146
- set_maxLux?: number
147
- set_max?: number
148
- set_min?: number
149
- set_minStep?: number
150
- setCloseMode?: string
151
- setOpenMode?: string
152
- silentModeSwitch?: boolean
153
- };
154
-
155
- export interface blindTiltConfig extends BaseDeviceConfig {
156
- configDeviceType: 'Blind Tilt'
157
- disable_group?: boolean
158
- mapping?: string
159
- hide_lightsensor?: boolean
160
- set_minLux?: number
161
- set_maxLux?: number
162
- set_max?: number
163
- set_min?: number
164
- set_minStep?: number
165
- setCloseMode?: string
166
- setOpenMode?: string
167
- silentModeSwitch?: boolean
168
- };
169
-
170
- export interface contactConfig extends BaseDeviceConfig {
171
- configDeviceType: 'Contact Sensor'
172
- hide_lightsensor?: boolean
173
- set_minLux?: number
174
- set_maxLux?: number
175
- hide_motionsensor?: boolean
176
- };
177
-
178
- export interface motionConfig extends BaseDeviceConfig {
179
- configDeviceType: 'Motion Sensor'
180
- hide_lightsensor?: boolean
181
- set_minLux?: number
182
- set_maxLux?: number
183
- };
184
-
185
- export interface waterDetectorConfig extends BaseDeviceConfig {
186
- configDeviceType: 'Water Detector'
187
- hide_leak?: boolean
188
- dry?: boolean
189
- };
190
-
191
- export interface plugConfig extends BaseDeviceConfig {
192
- configDeviceType: 'Plug' | 'Plug Mini (US)' | 'Plug Mini (JP)'
193
- };
194
-
195
- export interface colorBulbConfig extends BaseDeviceConfig {
196
- configDeviceType: 'Color Bulb'
197
- set_minStep?: number
198
- adaptiveLightingShift?: number
199
- };
200
-
201
- export interface stripLightConfig extends BaseDeviceConfig {
202
- configDeviceType: 'Strip Light'
203
- set_minStep?: number
204
- adaptiveLightingShift?: number
205
- };
206
-
207
- export interface ceilingLightConfig extends BaseDeviceConfig {
208
- configDeviceType: 'Ceiling Light' | 'Ceiling Light Pro'
209
- set_minStep?: number
210
- adaptiveLightingShift?: number
211
- };
212
-
213
- export interface lockConfig extends BaseDeviceConfig {
214
- configDeviceType: 'Smart Lock' | 'Smart Lock Pro'
215
- hide_contactsensor?: boolean
216
- activate_latchbutton?: boolean
217
- };
218
-
219
- export interface hubConfig extends BaseDeviceConfig {
220
- configDeviceType: 'Hub 2'
221
- hide_temperature?: boolean
222
- convertUnitTo?: string
223
- hide_humidity?: boolean
224
- hide_lightsensor?: boolean
225
- set_minLux?: number
226
- set_maxLux?: number
227
- };
228
-
229
- export type irDevicesConfig = irFanConfig | irLightConfig | irAirConfig | irOtherConfig
230
-
231
- export interface irBaseDeviceConfig extends irdevice {
232
- configDeviceName?: string
233
- configRemoteType?: string
234
- connectionType?: string
235
- hide_device?: boolean
236
- external?: boolean
237
- refreshRate?: number
238
- updateRate?: number
239
- pushRate?: number
240
- maxRetries?: number
241
- delayBetweenRetries?: number
242
- firmware?: string
243
- deviceId: string
244
- logging?: string
245
- customOn?: string
246
- customOff?: string
247
- customize?: boolean
248
- commandType?: string
249
- disablePushOn?: boolean
250
- disablePushOff?: boolean
251
- disablePushDetail?: boolean
13
+ export const DEFAULT_CONFIG: Partial<SwitchBotPluginConfig> = {
14
+ preferMatter: true,
15
+ enableMatter: true,
252
16
  }
253
17
 
254
- export interface irFanConfig extends irBaseDeviceConfig {
255
- configRemoteType?: 'Fan' | 'DIY Fan'
256
- swing_mode?: boolean
257
- rotation_speed?: boolean
258
- set_minStep?: number
259
- set_max?: number
260
- set_min?: number
261
- };
262
-
263
- export interface irLightConfig extends irBaseDeviceConfig {
264
- configRemoteType?: 'Light' | 'DIY Light'
265
- stateless?: boolean
266
- };
267
-
268
- export interface irAirConfig extends irBaseDeviceConfig {
269
- configRemoteType?: 'Air Conditioner' | 'DIY Air Conditioner'
270
- hide_automode?: boolean
271
- set_max_heat?: number
272
- set_min_heat?: number
273
- set_max_cool?: number
274
- set_min_cool?: number
275
- meterType?: string
276
- meterId?: string
277
- meterUuid?: string
278
- };
279
-
280
- export interface irOtherConfig extends irBaseDeviceConfig {
281
- configRemoteType?: 'Others'
282
- type?: string
283
- };
18
+ export type DeviceType = string
@@ -0,0 +1,203 @@
1
+ import type { SwitchBotPluginConfig } from './settings.js'
2
+
3
+ export interface ISwitchBotClient {
4
+ init(): Promise<void>
5
+ getDevice(id: string): Promise<any>
6
+ getDevices(): Promise<any[]>
7
+ setDeviceState(id: string, body: any): Promise<any>
8
+ destroy(): Promise<void>
9
+ }
10
+
11
+ /**
12
+ * Thin wrapper around node-switchbot v4.0.0-beta.2+
13
+ * Leverages upstream resilience features (retry, circuit breaker, connection intelligence)
14
+ * while maintaining plugin-specific features like write debouncing and OpenAPI fallback.
15
+ */
16
+ export class SwitchBotClient implements ISwitchBotClient {
17
+ private cfg: SwitchBotPluginConfig
18
+ private client: any | null = null
19
+ private baseUrl = 'https://api.switch-bot.com/v1.0'
20
+ private requestTimeout = 5000
21
+ private maxRetries = 2
22
+ private writeDebounceMs = 100
23
+ private logger: any
24
+ private pendingWrites: Map<string, { timer: any; body: any; resolvers: Array<{ resolve: (v:any)=>void; reject: (e:any)=>void }>; }> = new Map()
25
+
26
+ constructor(cfg: SwitchBotPluginConfig) {
27
+ this.cfg = cfg
28
+ this.logger = (cfg as any)?.logger ?? console
29
+ if (typeof (cfg as any)?.requestTimeout === 'number') this.requestTimeout = (cfg as any).requestTimeout
30
+ if (typeof (cfg as any)?.maxRetries === 'number') this.maxRetries = (cfg as any).maxRetries
31
+ if (typeof (cfg as any)?.writeDebounceMs === 'number') this.writeDebounceMs = (cfg as any).writeDebounceMs
32
+ }
33
+
34
+ async init(): Promise<void> {
35
+ try {
36
+ // Dynamic import of node-switchbot v4 with native resilience features
37
+ const { SwitchBot } = await import('node-switchbot')
38
+ this.client = new SwitchBot({
39
+ token: this.cfg.openApiToken,
40
+ secret: this.cfg.openApiSecret,
41
+ // Enable all built-in resilience features from node-switchbot v4
42
+ enableFallback: true, // Auto-fallback from BLE to API
43
+ enableRetry: true, // Retry with exponential backoff
44
+ enableCircuitBreaker: true, // Circuit breaker per connection type
45
+ enableMetrics: true, // Connection tracking and statistics
46
+ ...(typeof (this.cfg as any)?.nodeClientConfig === 'object' && (this.cfg as any).nodeClientConfig),
47
+ })
48
+ this.logger?.info?.('SwitchBot client initialized with native resilience features')
49
+ } catch (e) {
50
+ this.logger?.warn?.('Failed to load node-switchbot; will use OpenAPI fallback:', e)
51
+ this.client = null
52
+ }
53
+ }
54
+
55
+ private async fetchWithTimeoutAndRetry(url: string, opts: any = {}, timeoutMs?: number, retries?: number) {
56
+ const to = typeof timeoutMs === 'number' ? timeoutMs : this.requestTimeout
57
+ const max = typeof retries === 'number' ? retries : this.maxRetries
58
+
59
+ let attempt = 0
60
+ while (true) {
61
+ attempt += 1
62
+ const controller = new AbortController()
63
+ const timer = setTimeout(() => controller.abort(), to)
64
+ try {
65
+ const fetchOpts = Object.assign({}, opts)
66
+ try {
67
+ Object.defineProperty(fetchOpts, 'signal', { value: controller.signal, enumerable: false, configurable: true })
68
+ } catch (_e) {
69
+ // ignore
70
+ }
71
+ const resp = await fetch(url, fetchOpts)
72
+ clearTimeout(timer)
73
+ if (resp.ok) return resp
74
+ // server error / rate limit: retry
75
+ if (resp.status >= 500 || resp.status === 429) {
76
+ throw new Error(`retriable response: ${resp.status}`)
77
+ }
78
+ return resp
79
+ } catch (err: any) {
80
+ clearTimeout(timer)
81
+ if (attempt > max) throw err
82
+ // exponential backoff
83
+ const backoff = Math.min(100 * Math.pow(2, attempt - 1), 2000)
84
+ await new Promise((r) => setTimeout(r, backoff))
85
+ }
86
+ }
87
+ }
88
+
89
+ async getDevice(id: string): Promise<any> {
90
+ // Try client API first (with node-switchbot's smart fallback and retry)
91
+ if (this.client?.getDevice) {
92
+ try {
93
+ return await this.client.getDevice(id)
94
+ } catch (e) {
95
+ this.logger?.warn?.(`Client getDevice failed for ${id}, trying OpenAPI fallback:`, e)
96
+ }
97
+ }
98
+ // Fallback: call OpenAPI via HTTP
99
+ if (this.cfg.openApiToken) {
100
+ const url = `${this.baseUrl}/devices/${id}`
101
+ const opts = { headers: { Authorization: this.cfg.openApiToken } }
102
+ const resp = await this.fetchWithTimeoutAndRetry(url, opts)
103
+ return resp.json()
104
+ }
105
+ throw new Error('No SwitchBot client available')
106
+ }
107
+
108
+ async getDevices(): Promise<any[]> {
109
+ // Try client API first
110
+ if (this.client?.getDevices) {
111
+ try {
112
+ return await this.client.getDevices()
113
+ } catch (e) {
114
+ this.logger?.warn?.('Client getDevices failed, trying OpenAPI fallback:', e)
115
+ }
116
+ }
117
+ // Fallback: call OpenAPI
118
+ if (this.cfg.openApiToken) {
119
+ const url = `${this.baseUrl}/devices`
120
+ const opts = { headers: { Authorization: this.cfg.openApiToken } }
121
+ const resp = await this.fetchWithTimeoutAndRetry(url, opts)
122
+ const data = await resp.json()
123
+ return (data?.body || data) as any[]
124
+ }
125
+ return []
126
+ }
127
+
128
+ async setDeviceState(id: string, body: any): Promise<any> {
129
+ // Plugin-level debounce: coalesce rapid writes per device
130
+ if (!this.writeDebounceMs || this.writeDebounceMs <= 0) {
131
+ return this._doSetDeviceState(id, body)
132
+ }
133
+
134
+ return new Promise((resolve, reject) => {
135
+ const existing = this.pendingWrites.get(id)
136
+ if (existing) {
137
+ existing.body = body
138
+ existing.resolvers.push({ resolve, reject })
139
+ return
140
+ }
141
+
142
+ const resolvers: Array<{ resolve: (v:any)=>void; reject: (e:any)=>void }> = [{ resolve, reject }]
143
+ const timer = setTimeout(async () => {
144
+ const entry = this.pendingWrites.get(id)
145
+ if (!entry) return
146
+ this.pendingWrites.delete(id)
147
+ try {
148
+ const out = await this._doSetDeviceState(id, entry.body)
149
+ for (const r of entry.resolvers) r.resolve(out)
150
+ } catch (e) {
151
+ for (const r of entry.resolvers) r.reject(e)
152
+ }
153
+ }, this.writeDebounceMs)
154
+
155
+ this.pendingWrites.set(id, { timer, body, resolvers })
156
+ })
157
+ }
158
+
159
+ private async _doSetDeviceState(id: string, body: any): Promise<any> {
160
+ // Prefer client API (which has node-switchbot's native resilience)
161
+ if (this.client?.setDeviceState) {
162
+ try {
163
+ return await this.client.setDeviceState(id, body)
164
+ } catch (e) {
165
+ this.logger?.warn?.(`Client setDeviceState failed for ${id}, trying fallback:`, e)
166
+ }
167
+ }
168
+
169
+ // Try generic sendCommand if available
170
+ if (this.client?.sendCommand) {
171
+ try {
172
+ return await this.client.sendCommand(id, body)
173
+ } catch (e) {
174
+ this.logger?.warn?.(`Client sendCommand failed for ${id}, trying OpenAPI:`, e)
175
+ }
176
+ }
177
+
178
+ // OpenAPI fallback
179
+ if (this.cfg.openApiToken) {
180
+ const url = `${this.baseUrl}/devices/${id}/commands`
181
+ const opts = {
182
+ method: 'POST',
183
+ headers: {
184
+ Authorization: this.cfg.openApiToken,
185
+ 'Content-Type': 'application/json',
186
+ },
187
+ body: JSON.stringify(body),
188
+ }
189
+ const resp = await this.fetchWithTimeoutAndRetry(url, opts)
190
+ return resp.json()
191
+ }
192
+
193
+ throw new Error('No SwitchBot client available for setDeviceState')
194
+ }
195
+
196
+ async destroy(): Promise<void> {
197
+ if (this.client?.destroy) {
198
+ await this.client.destroy()
199
+ }
200
+ this.client = null
201
+ }
202
+ }
203
+