@switchbot/homebridge-switchbot 5.0.0-beta.11 → 5.0.0-beta.110

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 (577) 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 +170 -14787
  10. package/dist/device-types.d.ts +31 -0
  11. package/dist/device-types.d.ts.map +1 -0
  12. package/dist/device-types.js +216 -0
  13. package/dist/device-types.js.map +1 -0
  14. package/dist/deviceFactory.d.ts +13 -0
  15. package/dist/deviceFactory.d.ts.map +1 -0
  16. package/dist/deviceFactory.js +120 -0
  17. package/dist/deviceFactory.js.map +1 -0
  18. package/dist/devices/deviceBase.d.ts +50 -0
  19. package/dist/devices/deviceBase.d.ts.map +1 -0
  20. package/dist/devices/deviceBase.js +119 -0
  21. package/dist/devices/deviceBase.js.map +1 -0
  22. package/dist/devices/genericDevice.d.ts +298 -0
  23. package/dist/devices/genericDevice.d.ts.map +1 -0
  24. package/dist/devices/genericDevice.js +1067 -0
  25. package/dist/devices/genericDevice.js.map +1 -0
  26. package/dist/homebridge-ui/endpoints/config.d.ts +3 -0
  27. package/dist/homebridge-ui/endpoints/config.d.ts.map +1 -0
  28. package/dist/homebridge-ui/endpoints/config.js +104 -0
  29. package/dist/homebridge-ui/endpoints/config.js.map +1 -0
  30. package/dist/homebridge-ui/endpoints/credentials.d.ts +6 -0
  31. package/dist/homebridge-ui/endpoints/credentials.d.ts.map +1 -0
  32. package/dist/homebridge-ui/endpoints/credentials.js +65 -0
  33. package/dist/homebridge-ui/endpoints/credentials.js.map +1 -0
  34. package/dist/homebridge-ui/endpoints/devices.d.ts +6 -0
  35. package/dist/homebridge-ui/endpoints/devices.d.ts.map +1 -0
  36. package/dist/homebridge-ui/endpoints/devices.js +342 -0
  37. package/dist/homebridge-ui/endpoints/devices.js.map +1 -0
  38. package/dist/homebridge-ui/endpoints/discovery.d.ts +7 -0
  39. package/dist/homebridge-ui/endpoints/discovery.d.ts.map +1 -0
  40. package/dist/homebridge-ui/endpoints/discovery.js +171 -0
  41. package/dist/homebridge-ui/endpoints/discovery.js.map +1 -0
  42. package/dist/homebridge-ui/public/css/styles.css +254 -0
  43. package/dist/homebridge-ui/public/index.html +112 -247
  44. package/dist/homebridge-ui/public/js/api.d.ts +19 -0
  45. package/dist/homebridge-ui/public/js/api.d.ts.map +1 -0
  46. package/dist/homebridge-ui/public/js/api.js +178 -0
  47. package/dist/homebridge-ui/public/js/api.js.map +1 -0
  48. package/dist/homebridge-ui/public/js/api.ts +205 -0
  49. package/dist/homebridge-ui/public/js/app.d.ts +2 -0
  50. package/dist/homebridge-ui/public/js/app.d.ts.map +1 -0
  51. package/dist/homebridge-ui/public/js/app.js +1581 -0
  52. package/dist/homebridge-ui/public/js/app.js.map +7 -0
  53. package/dist/homebridge-ui/public/js/app.ts +22 -0
  54. package/dist/homebridge-ui/public/js/constants.d.ts +2 -0
  55. package/dist/homebridge-ui/public/js/constants.d.ts.map +1 -0
  56. package/dist/homebridge-ui/public/js/constants.js +2 -0
  57. package/dist/homebridge-ui/public/js/constants.js.map +1 -0
  58. package/dist/homebridge-ui/public/js/constants.ts +1 -0
  59. package/dist/homebridge-ui/public/js/credentials.d.ts +3 -0
  60. package/dist/homebridge-ui/public/js/credentials.d.ts.map +1 -0
  61. package/dist/homebridge-ui/public/js/credentials.js +96 -0
  62. package/dist/homebridge-ui/public/js/credentials.js.map +1 -0
  63. package/dist/homebridge-ui/public/js/credentials.ts +100 -0
  64. package/dist/homebridge-ui/public/js/devices-delete.d.ts +3 -0
  65. package/dist/homebridge-ui/public/js/devices-delete.d.ts.map +1 -0
  66. package/dist/homebridge-ui/public/js/devices-delete.js +206 -0
  67. package/dist/homebridge-ui/public/js/devices-delete.js.map +1 -0
  68. package/dist/homebridge-ui/public/js/devices-delete.ts +236 -0
  69. package/dist/homebridge-ui/public/js/devices.d.ts +9 -0
  70. package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -0
  71. package/dist/homebridge-ui/public/js/devices.js +81 -0
  72. package/dist/homebridge-ui/public/js/devices.js.map +1 -0
  73. package/dist/homebridge-ui/public/js/devices.ts +88 -0
  74. package/dist/homebridge-ui/public/js/discovery.d.ts +3 -0
  75. package/dist/homebridge-ui/public/js/discovery.d.ts.map +1 -0
  76. package/dist/homebridge-ui/public/js/discovery.js +233 -0
  77. package/dist/homebridge-ui/public/js/discovery.js.map +1 -0
  78. package/dist/homebridge-ui/public/js/discovery.ts +277 -0
  79. package/dist/homebridge-ui/public/js/logger.d.ts +6 -0
  80. package/dist/homebridge-ui/public/js/logger.d.ts.map +1 -0
  81. package/dist/homebridge-ui/public/js/logger.js +12 -0
  82. package/dist/homebridge-ui/public/js/logger.js.map +1 -0
  83. package/dist/homebridge-ui/public/js/logger.ts +11 -0
  84. package/dist/homebridge-ui/public/js/modal.d.ts +5 -0
  85. package/dist/homebridge-ui/public/js/modal.d.ts.map +1 -0
  86. package/dist/homebridge-ui/public/js/modal.js +26 -0
  87. package/dist/homebridge-ui/public/js/modal.js.map +1 -0
  88. package/dist/homebridge-ui/public/js/modal.ts +28 -0
  89. package/dist/homebridge-ui/public/js/modals.d.ts +2 -0
  90. package/dist/homebridge-ui/public/js/modals.d.ts.map +1 -0
  91. package/dist/homebridge-ui/public/js/modals.js +177 -0
  92. package/dist/homebridge-ui/public/js/modals.js.map +1 -0
  93. package/dist/homebridge-ui/public/js/modals.ts +200 -0
  94. package/dist/homebridge-ui/public/js/render.d.ts +63 -0
  95. package/dist/homebridge-ui/public/js/render.d.ts.map +1 -0
  96. package/dist/homebridge-ui/public/js/render.js +439 -0
  97. package/dist/homebridge-ui/public/js/render.js.map +1 -0
  98. package/dist/homebridge-ui/public/js/render.ts +510 -0
  99. package/dist/homebridge-ui/public/js/toast.d.ts +6 -0
  100. package/dist/homebridge-ui/public/js/toast.d.ts.map +1 -0
  101. package/dist/homebridge-ui/public/js/toast.js +29 -0
  102. package/dist/homebridge-ui/public/js/toast.js.map +1 -0
  103. package/dist/homebridge-ui/public/js/toast.ts +37 -0
  104. package/dist/homebridge-ui/public/js/types.d.ts +23 -0
  105. package/dist/homebridge-ui/public/js/types.d.ts.map +1 -0
  106. package/dist/homebridge-ui/public/js/types.js +2 -0
  107. package/dist/homebridge-ui/public/js/types.js.map +1 -0
  108. package/dist/homebridge-ui/public/js/types.ts +26 -0
  109. package/dist/homebridge-ui/server.js +11 -41
  110. package/dist/homebridge-ui/server.js.map +1 -1
  111. package/dist/homebridge-ui/utils/config-parser.d.ts +35 -0
  112. package/dist/homebridge-ui/utils/config-parser.d.ts.map +1 -0
  113. package/dist/homebridge-ui/utils/config-parser.js +87 -0
  114. package/dist/homebridge-ui/utils/config-parser.js.map +1 -0
  115. package/dist/homebridge-ui/utils/device-migration.d.ts +35 -0
  116. package/dist/homebridge-ui/utils/device-migration.d.ts.map +1 -0
  117. package/dist/homebridge-ui/utils/device-migration.js +111 -0
  118. package/dist/homebridge-ui/utils/device-migration.js.map +1 -0
  119. package/dist/homebridge-ui/utils/logger.d.ts +7 -0
  120. package/dist/homebridge-ui/utils/logger.d.ts.map +1 -0
  121. package/dist/homebridge-ui/utils/logger.js +17 -0
  122. package/dist/homebridge-ui/utils/logger.js.map +1 -0
  123. package/dist/index.d.ts.map +1 -1
  124. package/dist/index.js +4 -65
  125. package/dist/index.js.map +1 -1
  126. package/dist/platform.d.ts +35 -0
  127. package/dist/platform.d.ts.map +1 -0
  128. package/dist/platform.js +1567 -0
  129. package/dist/platform.js.map +1 -0
  130. package/dist/settings.d.ts +10 -249
  131. package/dist/settings.d.ts.map +1 -1
  132. package/dist/settings.js +5 -30
  133. package/dist/settings.js.map +1 -1
  134. package/dist/switchbotClient.d.ts +32 -0
  135. package/dist/switchbotClient.d.ts.map +1 -0
  136. package/dist/switchbotClient.js +207 -0
  137. package/dist/switchbotClient.js.map +1 -0
  138. package/dist/utils.d.ts +39 -50
  139. package/dist/utils.d.ts.map +1 -1
  140. package/dist/utils.js +39 -688
  141. package/dist/utils.js.map +1 -1
  142. package/docs/assets/highlight.css +14 -0
  143. package/docs/assets/icons.js +1 -1
  144. package/docs/assets/icons.svg +1 -1
  145. package/docs/assets/main.js +2 -2
  146. package/docs/assets/style.css +3 -3
  147. package/docs/index.html +77 -13
  148. package/docs/variables/default.html +1 -1
  149. package/eslint.config.js +7 -8
  150. package/nodemon.json +2 -2
  151. package/package.json +35 -32
  152. package/scripts/build-ui.js +36 -0
  153. package/scripts/e2e/README.md +25 -0
  154. package/scripts/e2e/curtain-e2e.sh +70 -0
  155. package/scripts/e2e/fan-e2e.sh +75 -0
  156. package/scripts/e2e/light-advanced-e2e.sh +97 -0
  157. package/scripts/e2e/light-e2e.sh +75 -0
  158. package/scripts/e2e/list-accessories.sh +19 -0
  159. package/scripts/e2e/lock-e2e.sh +65 -0
  160. package/scripts/free-dev-ports.mjs +105 -0
  161. package/scripts/generate-matter-maps.js +60 -0
  162. package/scripts/run-e2e-local.sh +14 -0
  163. package/scripts/sync-device-types.mjs +28 -0
  164. package/src/device-types.ts +230 -0
  165. package/src/deviceFactory.ts +163 -0
  166. package/src/devices/deviceBase.ts +141 -0
  167. package/src/devices/genericDevice.ts +998 -0
  168. package/src/homebridge-ui/endpoints/config.ts +123 -0
  169. package/src/homebridge-ui/endpoints/credentials.ts +74 -0
  170. package/src/homebridge-ui/endpoints/devices.ts +380 -0
  171. package/src/homebridge-ui/endpoints/discovery.ts +191 -0
  172. package/src/homebridge-ui/public/css/styles.css +254 -0
  173. package/src/homebridge-ui/public/index.html +112 -247
  174. package/src/homebridge-ui/public/js/api.ts +205 -0
  175. package/src/homebridge-ui/public/js/app.ts +22 -0
  176. package/src/homebridge-ui/public/js/constants.ts +1 -0
  177. package/src/homebridge-ui/public/js/credentials.ts +100 -0
  178. package/src/homebridge-ui/public/js/devices-delete.ts +236 -0
  179. package/src/homebridge-ui/public/js/devices.ts +88 -0
  180. package/src/homebridge-ui/public/js/discovery.ts +277 -0
  181. package/src/homebridge-ui/public/js/logger.ts +11 -0
  182. package/src/homebridge-ui/public/js/modal.ts +28 -0
  183. package/src/homebridge-ui/public/js/modals.ts +200 -0
  184. package/src/homebridge-ui/public/js/render.ts +510 -0
  185. package/src/homebridge-ui/public/js/toast.ts +37 -0
  186. package/src/homebridge-ui/public/js/types.ts +26 -0
  187. package/src/homebridge-ui/server.ts +11 -43
  188. package/src/homebridge-ui/utils/config-parser.ts +108 -0
  189. package/src/homebridge-ui/utils/device-migration.ts +144 -0
  190. package/src/homebridge-ui/utils/logger.ts +17 -0
  191. package/src/index.ts +4 -66
  192. package/src/platform.ts +1566 -0
  193. package/src/settings.ts +12 -277
  194. package/src/switchbotClient.ts +216 -0
  195. package/src/utils.ts +45 -713
  196. package/switchbot-homebridge-switchbot-5.0.0.tgz +0 -0
  197. package/test/accessory-restore.spec.ts +73 -0
  198. package/test/device-mapping.spec.ts +37 -0
  199. package/test/deviceFactory.spec.ts +18 -0
  200. package/test/e2e/run-e2e.spec.ts +50 -0
  201. package/test/fan-swing.spec.ts +29 -0
  202. package/test/helpers/matter-harness.ts +53 -0
  203. package/test/lock-users.spec.ts +44 -0
  204. package/test/matter-childbridge.spec.ts +55 -0
  205. package/test/matter-descriptors.spec.ts +97 -0
  206. package/test/matter-device-state.spec.ts +101 -0
  207. package/test/matter-integration.spec.ts +70 -0
  208. package/test/platform.integration.spec.ts +55 -0
  209. package/test/switchbot-client-debounce.spec.ts +131 -0
  210. package/test/switchbot-client-openapi.spec.ts +56 -0
  211. package/test/switchbotClient.spec.ts +10 -0
  212. package/test/utils.spec.ts +20 -0
  213. package/todo/COMPLETED.md +189 -0
  214. package/todo/README.md +59 -0
  215. package/todo/TODO.md +205 -0
  216. package/todo/mockups/filtering-sorting.svg +112 -0
  217. package/todo/mockups/modal-switchbot-theme.svg +66 -0
  218. package/todo/mockups/signal-strength-visualization.svg +94 -0
  219. package/vitest.config.ts +7 -0
  220. package/coverage/base.css +0 -224
  221. package/coverage/block-navigation.js +0 -87
  222. package/coverage/clover.xml +0 -15847
  223. package/coverage/coverage-final.json +0 -42
  224. package/coverage/docs/assets/dmt/dmt-component-data.js.html +0 -85
  225. package/coverage/docs/assets/dmt/dmt-components.js.html +0 -286
  226. package/coverage/docs/assets/dmt/index.html +0 -131
  227. package/coverage/docs/assets/hierarchy.js.html +0 -85
  228. package/coverage/docs/assets/icons.js.html +0 -136
  229. package/coverage/docs/assets/index.html +0 -146
  230. package/coverage/docs/assets/main.js.html +0 -265
  231. package/coverage/favicon.png +0 -0
  232. package/coverage/index.html +0 -191
  233. package/coverage/prettify.css +0 -1
  234. package/coverage/prettify.js +0 -2
  235. package/coverage/sort-arrow-sprite.png +0 -0
  236. package/coverage/sorter.js +0 -196
  237. package/coverage/src/device/blindtilt.ts.html +0 -3238
  238. package/coverage/src/device/bot.ts.html +0 -2803
  239. package/coverage/src/device/ceilinglight.ts.html +0 -2338
  240. package/coverage/src/device/colorbulb.ts.html +0 -2824
  241. package/coverage/src/device/contact.ts.html +0 -1465
  242. package/coverage/src/device/curtain.ts.html +0 -2869
  243. package/coverage/src/device/device.ts.html +0 -2500
  244. package/coverage/src/device/fan.ts.html +0 -2242
  245. package/coverage/src/device/hub.ts.html +0 -1408
  246. package/coverage/src/device/humidifier.ts.html +0 -2116
  247. package/coverage/src/device/index.html +0 -416
  248. package/coverage/src/device/iosensor.ts.html +0 -1375
  249. package/coverage/src/device/lightstrip.ts.html +0 -2617
  250. package/coverage/src/device/lock.ts.html +0 -1963
  251. package/coverage/src/device/meter.ts.html +0 -1372
  252. package/coverage/src/device/meterplus.ts.html +0 -1384
  253. package/coverage/src/device/meterpro.ts.html +0 -1618
  254. package/coverage/src/device/motion.ts.html +0 -1264
  255. package/coverage/src/device/plug.ts.html +0 -1372
  256. package/coverage/src/device/relayswitch.ts.html +0 -2284
  257. package/coverage/src/device/robotvacuumcleaner.ts.html +0 -1810
  258. package/coverage/src/device/waterdetector.ts.html +0 -1294
  259. package/coverage/src/homebridge-ui/index.html +0 -116
  260. package/coverage/src/homebridge-ui/server.ts.html +0 -229
  261. package/coverage/src/index.html +0 -161
  262. package/coverage/src/index.ts.html +0 -124
  263. package/coverage/src/irdevice/airconditioner.ts.html +0 -1687
  264. package/coverage/src/irdevice/airpurifier.ts.html +0 -844
  265. package/coverage/src/irdevice/camera.ts.html +0 -475
  266. package/coverage/src/irdevice/fan.ts.html +0 -766
  267. package/coverage/src/irdevice/index.html +0 -251
  268. package/coverage/src/irdevice/irdevice.ts.html +0 -1117
  269. package/coverage/src/irdevice/light.ts.html +0 -826
  270. package/coverage/src/irdevice/other.ts.html +0 -2458
  271. package/coverage/src/irdevice/tv.ts.html +0 -1222
  272. package/coverage/src/irdevice/vacuumcleaner.ts.html +0 -466
  273. package/coverage/src/irdevice/waterheater.ts.html +0 -469
  274. package/coverage/src/platform.ts.html +0 -8776
  275. package/coverage/src/settings.ts.html +0 -934
  276. package/coverage/src/utils.ts.html +0 -2092
  277. package/dist/devices-hap/airpurifier.d.ts +0 -54
  278. package/dist/devices-hap/airpurifier.d.ts.map +0 -1
  279. package/dist/devices-hap/airpurifier.js +0 -527
  280. package/dist/devices-hap/airpurifier.js.map +0 -1
  281. package/dist/devices-hap/blindtilt.d.ts +0 -90
  282. package/dist/devices-hap/blindtilt.d.ts.map +0 -1
  283. package/dist/devices-hap/blindtilt.js +0 -974
  284. package/dist/devices-hap/blindtilt.js.map +0 -1
  285. package/dist/devices-hap/bot.d.ts +0 -102
  286. package/dist/devices-hap/bot.d.ts.map +0 -1
  287. package/dist/devices-hap/bot.js +0 -811
  288. package/dist/devices-hap/bot.js.map +0 -1
  289. package/dist/devices-hap/ceilinglight.d.ts +0 -85
  290. package/dist/devices-hap/ceilinglight.d.ts.map +0 -1
  291. package/dist/devices-hap/ceilinglight.js +0 -701
  292. package/dist/devices-hap/ceilinglight.js.map +0 -1
  293. package/dist/devices-hap/colorbulb.d.ts +0 -88
  294. package/dist/devices-hap/colorbulb.d.ts.map +0 -1
  295. package/dist/devices-hap/colorbulb.js +0 -881
  296. package/dist/devices-hap/colorbulb.js.map +0 -1
  297. package/dist/devices-hap/contact.d.ts +0 -44
  298. package/dist/devices-hap/contact.d.ts.map +0 -1
  299. package/dist/devices-hap/contact.js +0 -409
  300. package/dist/devices-hap/contact.js.map +0 -1
  301. package/dist/devices-hap/curtain.d.ts +0 -73
  302. package/dist/devices-hap/curtain.d.ts.map +0 -1
  303. package/dist/devices-hap/curtain.js +0 -869
  304. package/dist/devices-hap/curtain.js.map +0 -1
  305. package/dist/devices-hap/device.d.ts +0 -98
  306. package/dist/devices-hap/device.d.ts.map +0 -1
  307. package/dist/devices-hap/device.js +0 -749
  308. package/dist/devices-hap/device.js.map +0 -1
  309. package/dist/devices-hap/fan.d.ts +0 -69
  310. package/dist/devices-hap/fan.d.ts.map +0 -1
  311. package/dist/devices-hap/fan.js +0 -649
  312. package/dist/devices-hap/fan.js.map +0 -1
  313. package/dist/devices-hap/hub.d.ts +0 -37
  314. package/dist/devices-hap/hub.d.ts.map +0 -1
  315. package/dist/devices-hap/hub.js +0 -392
  316. package/dist/devices-hap/hub.js.map +0 -1
  317. package/dist/devices-hap/humidifier.d.ts +0 -68
  318. package/dist/devices-hap/humidifier.d.ts.map +0 -1
  319. package/dist/devices-hap/humidifier.js +0 -628
  320. package/dist/devices-hap/humidifier.js.map +0 -1
  321. package/dist/devices-hap/iosensor.d.ts +0 -42
  322. package/dist/devices-hap/iosensor.d.ts.map +0 -1
  323. package/dist/devices-hap/iosensor.js +0 -382
  324. package/dist/devices-hap/iosensor.js.map +0 -1
  325. package/dist/devices-hap/lightstrip.d.ts +0 -79
  326. package/dist/devices-hap/lightstrip.d.ts.map +0 -1
  327. package/dist/devices-hap/lightstrip.js +0 -797
  328. package/dist/devices-hap/lightstrip.js.map +0 -1
  329. package/dist/devices-hap/lock.d.ts +0 -53
  330. package/dist/devices-hap/lock.d.ts.map +0 -1
  331. package/dist/devices-hap/lock.js +0 -561
  332. package/dist/devices-hap/lock.js.map +0 -1
  333. package/dist/devices-hap/meter.d.ts +0 -37
  334. package/dist/devices-hap/meter.d.ts.map +0 -1
  335. package/dist/devices-hap/meter.js +0 -379
  336. package/dist/devices-hap/meter.js.map +0 -1
  337. package/dist/devices-hap/meterplus.d.ts +0 -42
  338. package/dist/devices-hap/meterplus.d.ts.map +0 -1
  339. package/dist/devices-hap/meterplus.js +0 -384
  340. package/dist/devices-hap/meterplus.js.map +0 -1
  341. package/dist/devices-hap/meterpro.d.ts +0 -43
  342. package/dist/devices-hap/meterpro.d.ts.map +0 -1
  343. package/dist/devices-hap/meterpro.js +0 -468
  344. package/dist/devices-hap/meterpro.js.map +0 -1
  345. package/dist/devices-hap/motion.d.ts +0 -42
  346. package/dist/devices-hap/motion.d.ts.map +0 -1
  347. package/dist/devices-hap/motion.js +0 -345
  348. package/dist/devices-hap/motion.js.map +0 -1
  349. package/dist/devices-hap/plug.d.ts +0 -49
  350. package/dist/devices-hap/plug.d.ts.map +0 -1
  351. package/dist/devices-hap/plug.js +0 -395
  352. package/dist/devices-hap/plug.js.map +0 -1
  353. package/dist/devices-hap/relayswitch.d.ts +0 -96
  354. package/dist/devices-hap/relayswitch.d.ts.map +0 -1
  355. package/dist/devices-hap/relayswitch.js +0 -642
  356. package/dist/devices-hap/relayswitch.js.map +0 -1
  357. package/dist/devices-hap/robotvacuumcleaner.d.ts +0 -54
  358. package/dist/devices-hap/robotvacuumcleaner.d.ts.map +0 -1
  359. package/dist/devices-hap/robotvacuumcleaner.js +0 -523
  360. package/dist/devices-hap/robotvacuumcleaner.js.map +0 -1
  361. package/dist/devices-hap/waterdetector.d.ts +0 -41
  362. package/dist/devices-hap/waterdetector.d.ts.map +0 -1
  363. package/dist/devices-hap/waterdetector.js +0 -356
  364. package/dist/devices-hap/waterdetector.js.map +0 -1
  365. package/dist/devices-matter/BaseMatterAccessory.d.ts +0 -63
  366. package/dist/devices-matter/BaseMatterAccessory.d.ts.map +0 -1
  367. package/dist/devices-matter/BaseMatterAccessory.js +0 -100
  368. package/dist/devices-matter/BaseMatterAccessory.js.map +0 -1
  369. package/dist/devices-matter/ColorLightAccessory.d.ts +0 -20
  370. package/dist/devices-matter/ColorLightAccessory.d.ts.map +0 -1
  371. package/dist/devices-matter/ColorLightAccessory.js +0 -95
  372. package/dist/devices-matter/ColorLightAccessory.js.map +0 -1
  373. package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts +0 -18
  374. package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts.map +0 -1
  375. package/dist/devices-matter/ColorTemperatureLightAccessory.js +0 -78
  376. package/dist/devices-matter/ColorTemperatureLightAccessory.js.map +0 -1
  377. package/dist/devices-matter/ContactSensorAccessory.d.ts +0 -12
  378. package/dist/devices-matter/ContactSensorAccessory.d.ts.map +0 -1
  379. package/dist/devices-matter/ContactSensorAccessory.js +0 -34
  380. package/dist/devices-matter/ContactSensorAccessory.js.map +0 -1
  381. package/dist/devices-matter/DimmableLightAccessory.d.ts +0 -58
  382. package/dist/devices-matter/DimmableLightAccessory.d.ts.map +0 -1
  383. package/dist/devices-matter/DimmableLightAccessory.js +0 -167
  384. package/dist/devices-matter/DimmableLightAccessory.js.map +0 -1
  385. package/dist/devices-matter/DoorLockAccessory.d.ts +0 -14
  386. package/dist/devices-matter/DoorLockAccessory.d.ts.map +0 -1
  387. package/dist/devices-matter/DoorLockAccessory.js +0 -50
  388. package/dist/devices-matter/DoorLockAccessory.js.map +0 -1
  389. package/dist/devices-matter/ExtendedColorLightAccessory.d.ts +0 -21
  390. package/dist/devices-matter/ExtendedColorLightAccessory.d.ts.map +0 -1
  391. package/dist/devices-matter/ExtendedColorLightAccessory.js +0 -107
  392. package/dist/devices-matter/ExtendedColorLightAccessory.js.map +0 -1
  393. package/dist/devices-matter/FanAccessory.d.ts +0 -16
  394. package/dist/devices-matter/FanAccessory.d.ts.map +0 -1
  395. package/dist/devices-matter/FanAccessory.js +0 -81
  396. package/dist/devices-matter/FanAccessory.js.map +0 -1
  397. package/dist/devices-matter/HumiditySensorAccessory.d.ts +0 -12
  398. package/dist/devices-matter/HumiditySensorAccessory.d.ts.map +0 -1
  399. package/dist/devices-matter/HumiditySensorAccessory.js +0 -34
  400. package/dist/devices-matter/HumiditySensorAccessory.js.map +0 -1
  401. package/dist/devices-matter/LeakSensorAccessory.d.ts +0 -12
  402. package/dist/devices-matter/LeakSensorAccessory.d.ts.map +0 -1
  403. package/dist/devices-matter/LeakSensorAccessory.js +0 -33
  404. package/dist/devices-matter/LeakSensorAccessory.js.map +0 -1
  405. package/dist/devices-matter/LightSensorAccessory.d.ts +0 -12
  406. package/dist/devices-matter/LightSensorAccessory.d.ts.map +0 -1
  407. package/dist/devices-matter/LightSensorAccessory.js +0 -34
  408. package/dist/devices-matter/LightSensorAccessory.js.map +0 -1
  409. package/dist/devices-matter/OccupancySensorAccessory.d.ts +0 -12
  410. package/dist/devices-matter/OccupancySensorAccessory.d.ts.map +0 -1
  411. package/dist/devices-matter/OccupancySensorAccessory.js +0 -39
  412. package/dist/devices-matter/OccupancySensorAccessory.js.map +0 -1
  413. package/dist/devices-matter/OnOffLightAccessory.d.ts +0 -38
  414. package/dist/devices-matter/OnOffLightAccessory.d.ts.map +0 -1
  415. package/dist/devices-matter/OnOffLightAccessory.js +0 -118
  416. package/dist/devices-matter/OnOffLightAccessory.js.map +0 -1
  417. package/dist/devices-matter/OnOffOutletAccessory.d.ts +0 -12
  418. package/dist/devices-matter/OnOffOutletAccessory.d.ts.map +0 -1
  419. package/dist/devices-matter/OnOffOutletAccessory.js +0 -40
  420. package/dist/devices-matter/OnOffOutletAccessory.js.map +0 -1
  421. package/dist/devices-matter/OnOffSwitchAccessory.d.ts +0 -14
  422. package/dist/devices-matter/OnOffSwitchAccessory.d.ts.map +0 -1
  423. package/dist/devices-matter/OnOffSwitchAccessory.js +0 -42
  424. package/dist/devices-matter/OnOffSwitchAccessory.js.map +0 -1
  425. package/dist/devices-matter/RoboticVacuumAccessory.d.ts +0 -68
  426. package/dist/devices-matter/RoboticVacuumAccessory.d.ts.map +0 -1
  427. package/dist/devices-matter/RoboticVacuumAccessory.js +0 -334
  428. package/dist/devices-matter/RoboticVacuumAccessory.js.map +0 -1
  429. package/dist/devices-matter/SmokeCOAlarmAccessory.d.ts +0 -11
  430. package/dist/devices-matter/SmokeCOAlarmAccessory.d.ts.map +0 -1
  431. package/dist/devices-matter/SmokeCOAlarmAccessory.js +0 -49
  432. package/dist/devices-matter/SmokeCOAlarmAccessory.js.map +0 -1
  433. package/dist/devices-matter/TemperatureSensorAccessory.d.ts +0 -12
  434. package/dist/devices-matter/TemperatureSensorAccessory.d.ts.map +0 -1
  435. package/dist/devices-matter/TemperatureSensorAccessory.js +0 -36
  436. package/dist/devices-matter/TemperatureSensorAccessory.js.map +0 -1
  437. package/dist/devices-matter/ThermostatAccessory.d.ts +0 -19
  438. package/dist/devices-matter/ThermostatAccessory.d.ts.map +0 -1
  439. package/dist/devices-matter/ThermostatAccessory.js +0 -95
  440. package/dist/devices-matter/ThermostatAccessory.js.map +0 -1
  441. package/dist/devices-matter/VenetianBlindAccessory.d.ts +0 -19
  442. package/dist/devices-matter/VenetianBlindAccessory.d.ts.map +0 -1
  443. package/dist/devices-matter/VenetianBlindAccessory.js +0 -99
  444. package/dist/devices-matter/VenetianBlindAccessory.js.map +0 -1
  445. package/dist/devices-matter/WindowBlindAccessory.d.ts +0 -17
  446. package/dist/devices-matter/WindowBlindAccessory.d.ts.map +0 -1
  447. package/dist/devices-matter/WindowBlindAccessory.js +0 -80
  448. package/dist/devices-matter/WindowBlindAccessory.js.map +0 -1
  449. package/dist/devices-matter/custom/PowerStripAccessory.d.ts +0 -97
  450. package/dist/devices-matter/custom/PowerStripAccessory.d.ts.map +0 -1
  451. package/dist/devices-matter/custom/PowerStripAccessory.js +0 -265
  452. package/dist/devices-matter/custom/PowerStripAccessory.js.map +0 -1
  453. package/dist/devices-matter/custom/index.d.ts +0 -8
  454. package/dist/devices-matter/custom/index.d.ts.map +0 -1
  455. package/dist/devices-matter/custom/index.js +0 -8
  456. package/dist/devices-matter/custom/index.js.map +0 -1
  457. package/dist/devices-matter/index.d.ts +0 -29
  458. package/dist/devices-matter/index.d.ts.map +0 -1
  459. package/dist/devices-matter/index.js +0 -28
  460. package/dist/devices-matter/index.js.map +0 -1
  461. package/dist/index.test.d.ts +0 -2
  462. package/dist/index.test.d.ts.map +0 -1
  463. package/dist/index.test.js +0 -19
  464. package/dist/index.test.js.map +0 -1
  465. package/dist/irdevice/airconditioner.d.ts +0 -61
  466. package/dist/irdevice/airconditioner.d.ts.map +0 -1
  467. package/dist/irdevice/airconditioner.js +0 -472
  468. package/dist/irdevice/airconditioner.js.map +0 -1
  469. package/dist/irdevice/airpurifier.d.ts +0 -50
  470. package/dist/irdevice/airpurifier.d.ts.map +0 -1
  471. package/dist/irdevice/airpurifier.js +0 -213
  472. package/dist/irdevice/airpurifier.js.map +0 -1
  473. package/dist/irdevice/camera.d.ts +0 -32
  474. package/dist/irdevice/camera.d.ts.map +0 -1
  475. package/dist/irdevice/camera.js +0 -107
  476. package/dist/irdevice/camera.js.map +0 -1
  477. package/dist/irdevice/fan.d.ts +0 -36
  478. package/dist/irdevice/fan.d.ts.map +0 -1
  479. package/dist/irdevice/fan.js +0 -200
  480. package/dist/irdevice/fan.js.map +0 -1
  481. package/dist/irdevice/irdevice.d.ts +0 -68
  482. package/dist/irdevice/irdevice.d.ts.map +0 -1
  483. package/dist/irdevice/irdevice.js +0 -298
  484. package/dist/irdevice/irdevice.js.map +0 -1
  485. package/dist/irdevice/light.d.ts +0 -36
  486. package/dist/irdevice/light.d.ts.map +0 -1
  487. package/dist/irdevice/light.js +0 -206
  488. package/dist/irdevice/light.js.map +0 -1
  489. package/dist/irdevice/other.d.ts +0 -57
  490. package/dist/irdevice/other.d.ts.map +0 -1
  491. package/dist/irdevice/other.js +0 -778
  492. package/dist/irdevice/other.js.map +0 -1
  493. package/dist/irdevice/tv.d.ts +0 -45
  494. package/dist/irdevice/tv.d.ts.map +0 -1
  495. package/dist/irdevice/tv.js +0 -327
  496. package/dist/irdevice/tv.js.map +0 -1
  497. package/dist/irdevice/vacuumcleaner.d.ts +0 -28
  498. package/dist/irdevice/vacuumcleaner.d.ts.map +0 -1
  499. package/dist/irdevice/vacuumcleaner.js +0 -104
  500. package/dist/irdevice/vacuumcleaner.js.map +0 -1
  501. package/dist/irdevice/waterheater.d.ts +0 -30
  502. package/dist/irdevice/waterheater.d.ts.map +0 -1
  503. package/dist/irdevice/waterheater.js +0 -105
  504. package/dist/irdevice/waterheater.js.map +0 -1
  505. package/dist/platform-hap.d.ts +0 -149
  506. package/dist/platform-hap.d.ts.map +0 -1
  507. package/dist/platform-hap.js +0 -2861
  508. package/dist/platform-hap.js.map +0 -1
  509. package/dist/platform-matter.d.ts +0 -120
  510. package/dist/platform-matter.d.ts.map +0 -1
  511. package/dist/platform-matter.js +0 -984
  512. package/dist/platform-matter.js.map +0 -1
  513. package/dist/verifyconfig.test.d.ts +0 -2
  514. package/dist/verifyconfig.test.d.ts.map +0 -1
  515. package/dist/verifyconfig.test.js +0 -167
  516. package/dist/verifyconfig.test.js.map +0 -1
  517. package/src/custom.d.ts +0 -7
  518. package/src/devices-hap/airpurifier.ts +0 -563
  519. package/src/devices-hap/blindtilt.ts +0 -1049
  520. package/src/devices-hap/bot.ts +0 -900
  521. package/src/devices-hap/ceilinglight.ts +0 -742
  522. package/src/devices-hap/colorbulb.ts +0 -904
  523. package/src/devices-hap/contact.ts +0 -457
  524. package/src/devices-hap/curtain.ts +0 -944
  525. package/src/devices-hap/device.ts +0 -811
  526. package/src/devices-hap/fan.ts +0 -711
  527. package/src/devices-hap/hub.ts +0 -439
  528. package/src/devices-hap/humidifier.ts +0 -669
  529. package/src/devices-hap/iosensor.ts +0 -427
  530. package/src/devices-hap/lightstrip.ts +0 -836
  531. package/src/devices-hap/lock.ts +0 -620
  532. package/src/devices-hap/meter.ts +0 -426
  533. package/src/devices-hap/meterplus.ts +0 -430
  534. package/src/devices-hap/meterpro.ts +0 -522
  535. package/src/devices-hap/motion.ts +0 -390
  536. package/src/devices-hap/plug.ts +0 -423
  537. package/src/devices-hap/relayswitch.ts +0 -727
  538. package/src/devices-hap/robotvacuumcleaner.ts +0 -568
  539. package/src/devices-hap/waterdetector.ts +0 -400
  540. package/src/devices-matter/BaseMatterAccessory.ts +0 -131
  541. package/src/devices-matter/ColorLightAccessory.ts +0 -110
  542. package/src/devices-matter/ColorTemperatureLightAccessory.ts +0 -92
  543. package/src/devices-matter/ContactSensorAccessory.ts +0 -41
  544. package/src/devices-matter/DimmableLightAccessory.ts +0 -192
  545. package/src/devices-matter/DoorLockAccessory.ts +0 -60
  546. package/src/devices-matter/ExtendedColorLightAccessory.ts +0 -123
  547. package/src/devices-matter/FanAccessory.ts +0 -95
  548. package/src/devices-matter/HumiditySensorAccessory.ts +0 -41
  549. package/src/devices-matter/LeakSensorAccessory.ts +0 -40
  550. package/src/devices-matter/LightSensorAccessory.ts +0 -41
  551. package/src/devices-matter/OccupancySensorAccessory.ts +0 -48
  552. package/src/devices-matter/OnOffLightAccessory.ts +0 -133
  553. package/src/devices-matter/OnOffOutletAccessory.ts +0 -46
  554. package/src/devices-matter/OnOffSwitchAccessory.ts +0 -51
  555. package/src/devices-matter/RoboticVacuumAccessory.ts +0 -407
  556. package/src/devices-matter/SmokeCOAlarmAccessory.ts +0 -59
  557. package/src/devices-matter/TemperatureSensorAccessory.ts +0 -43
  558. package/src/devices-matter/ThermostatAccessory.ts +0 -110
  559. package/src/devices-matter/VenetianBlindAccessory.ts +0 -115
  560. package/src/devices-matter/WindowBlindAccessory.ts +0 -92
  561. package/src/devices-matter/custom/PowerStripAccessory.ts +0 -309
  562. package/src/devices-matter/custom/index.ts +0 -8
  563. package/src/devices-matter/index.ts +0 -29
  564. package/src/index.test.ts +0 -24
  565. package/src/irdevice/airconditioner.ts +0 -533
  566. package/src/irdevice/airpurifier.ts +0 -252
  567. package/src/irdevice/camera.ts +0 -129
  568. package/src/irdevice/fan.ts +0 -226
  569. package/src/irdevice/irdevice.ts +0 -344
  570. package/src/irdevice/light.ts +0 -246
  571. package/src/irdevice/other.ts +0 -790
  572. package/src/irdevice/tv.ts +0 -378
  573. package/src/irdevice/vacuumcleaner.ts +0 -126
  574. package/src/irdevice/waterheater.ts +0 -127
  575. package/src/platform-hap.ts +0 -2997
  576. package/src/platform-matter.ts +0 -1110
  577. package/src/verifyconfig.test.ts +0 -197
@@ -0,0 +1,123 @@
1
+ import type { HomebridgePluginUiServer } from '@homebridge/plugin-ui-utils'
2
+
3
+ import { RequestError } from '@homebridge/plugin-ui-utils'
4
+ import fs from 'node:fs/promises'
5
+
6
+ import { getAllDevices, getSwitchBotPlatformConfig, SWITCHBOT_PLATFORM_REGEX } from '../utils/config-parser.js'
7
+ import { uiLog } from '../utils/logger.js'
8
+ import { validateAndMigrateDeviceType } from '../utils/device-migration.js'
9
+ import { isValidDeviceType } from '../../device-types.js'
10
+
11
+ export function registerConfigEndpoints(server: HomebridgePluginUiServer) {
12
+ /**
13
+ * GET /devices - List all configured devices from Homebridge config
14
+ */
15
+ server.onRequest('/devices', async () => {
16
+ try {
17
+ const cfgPath = server.homebridgeConfigPath
18
+ if (!cfgPath) {
19
+ throw new Error('HOMEBRIDGE_CONFIG_PATH not set')
20
+ }
21
+
22
+ const raw = await fs.readFile(cfgPath, 'utf8')
23
+ const cfg = JSON.parse(raw)
24
+ const found: Array<{
25
+ id: string
26
+ name?: string
27
+ type?: string
28
+ connectionPreference?: string
29
+ room?: string
30
+ typeValidationWarning?: string
31
+ }> = []
32
+ const invalidTypeDevices: Array<{ name: string, type: string, suggestion?: string }> = []
33
+
34
+ const platforms = Array.isArray(cfg.platforms) ? cfg.platforms : []
35
+ for (const p of platforms) {
36
+ try {
37
+ const platformName = p.platform || p.name || ''
38
+ // Match known SwitchBot platform identifiers
39
+ if (!platformName || !SWITCHBOT_PLATFORM_REGEX.test(String(platformName))) {
40
+ continue
41
+ }
42
+
43
+ const devices = getAllDevices(p)
44
+ for (const d of devices) {
45
+ const id = d.deviceId ?? d.id
46
+ if (!id) {
47
+ continue
48
+ }
49
+
50
+ const deviceType = d.configDeviceType ?? d.type ?? d.deviceType
51
+ const deviceName = d.configDeviceName ?? d.name ?? d.deviceName
52
+
53
+ // Validate device type
54
+ const deviceObj = { configDeviceType: deviceType, configDeviceName: deviceName, deviceId: id }
55
+ const validationResult = validateAndMigrateDeviceType(deviceObj, false)
56
+
57
+ const deviceEntry: typeof found[0] = {
58
+ id,
59
+ name: deviceName,
60
+ type: deviceType,
61
+ connectionPreference: d.connectionPreference ?? d.connection ?? 'auto',
62
+ room: d.room || d.location || undefined,
63
+ }
64
+
65
+ if (!isValidDeviceType(deviceType)) {
66
+ deviceEntry.typeValidationWarning = `Invalid type "${deviceType}"`
67
+ if (validationResult.correctedType) {
68
+ deviceEntry.typeValidationWarning += ` - should be "${validationResult.correctedType}"`
69
+ invalidTypeDevices.push({
70
+ name: deviceName,
71
+ type: deviceType,
72
+ suggestion: validationResult.correctedType,
73
+ })
74
+ } else {
75
+ invalidTypeDevices.push({
76
+ name: deviceName,
77
+ type: deviceType,
78
+ })
79
+ }
80
+ }
81
+
82
+ found.push(deviceEntry)
83
+ }
84
+ } catch (e) {
85
+ // ignore malformed platform entries
86
+ }
87
+ }
88
+
89
+ // Log validation issues
90
+ if (invalidTypeDevices.length > 0) {
91
+ uiLog.warn(
92
+ `Found ${invalidTypeDevices.length} device(s) with invalid types:\n${invalidTypeDevices.map(d => ` - "${d.name}": "${d.type}"${d.suggestion ? ` → should be "${d.suggestion}"` : ''}`).join('\n')}`,
93
+ )
94
+ }
95
+
96
+ uiLog.info(`GET /devices - Found ${found.length} devices in ${cfgPath}${invalidTypeDevices.length > 0 ? ` (${invalidTypeDevices.length} with validation warnings)` : ''}`)
97
+ return {
98
+ success: true,
99
+ data: found,
100
+ ...(invalidTypeDevices.length > 0 && { validationWarnings: invalidTypeDevices }),
101
+ }
102
+ } catch (e) {
103
+ uiLog.error(`Error in /devices: ${e instanceof Error ? e.message : String(e)}`)
104
+ throw new RequestError('Failed to read Homebridge config', e)
105
+ }
106
+ })
107
+
108
+ /**
109
+ * GET /platform-config - Get SwitchBot platform configuration
110
+ */
111
+ server.onRequest('/platform-config', async () => {
112
+ try {
113
+ const { platform } = await getSwitchBotPlatformConfig(server)
114
+ return {
115
+ success: true,
116
+ data: JSON.parse(JSON.stringify(platform)),
117
+ }
118
+ } catch (e) {
119
+ uiLog.error(`Error in /platform-config: ${e instanceof Error ? e.message : String(e)}`)
120
+ throw new RequestError('Failed to read platform config', e)
121
+ }
122
+ })
123
+ }
@@ -0,0 +1,74 @@
1
+ import type { HomebridgePluginUiServer } from '@homebridge/plugin-ui-utils'
2
+
3
+ import { RequestError } from '@homebridge/plugin-ui-utils'
4
+ import fs from 'node:fs/promises'
5
+
6
+ import { getCredential, getSwitchBotPlatformConfig, setCredential } from '../utils/config-parser.js'
7
+ import { uiLog } from '../utils/logger.js'
8
+
9
+ /**
10
+ * Register credentials endpoint
11
+ */
12
+ export function registerCredentialsEndpoint(server: HomebridgePluginUiServer) {
13
+ server.onRequest('/credentials', async (body: any) => {
14
+ try {
15
+ // Handle both GET and POST requests
16
+ if (!body || Object.keys(body).length === 0) {
17
+ // GET request - return current status
18
+ const { platform } = await getSwitchBotPlatformConfig(server)
19
+
20
+ const token = getCredential(platform, 'openApiToken')
21
+ const secret = getCredential(platform, 'openApiSecret')
22
+ const status = {
23
+ hasToken: !!token,
24
+ hasSecret: !!secret,
25
+ tokenLength: token ? String(token).length : 0,
26
+ secretLength: secret ? String(secret).length : 0,
27
+ }
28
+
29
+ uiLog.info(`GET /credentials - Status hasToken=${status.hasToken} hasSecret=${status.hasSecret}`)
30
+ return { success: true, data: status }
31
+ } else {
32
+ // POST request - save credentials
33
+ const { token, secret } = body
34
+
35
+ if (!token || !secret) {
36
+ throw new Error('Token and secret are required')
37
+ }
38
+
39
+ const { cfg, platform, cfgPath } = await getSwitchBotPlatformConfig(server)
40
+
41
+ uiLog.info(`POST /credentials - Saving to platform: ${platform.platform || platform.name}`)
42
+ uiLog.debug(`POST /credentials - Config path: ${cfgPath}`)
43
+ uiLog.debug(`POST /credentials - Token length: ${token.length}, Secret length: ${secret.length}`)
44
+
45
+ // Save token and secret
46
+ setCredential(platform, 'openApiToken', token)
47
+ setCredential(platform, 'openApiSecret', secret)
48
+
49
+ // Write config with fsync
50
+ const configJson = JSON.stringify(cfg, null, 2)
51
+ const fileHandle = await fs.open(cfgPath, 'w')
52
+ try {
53
+ await fileHandle.writeFile(configJson, 'utf8')
54
+ await fileHandle.sync()
55
+ uiLog.info('POST /credentials - Credentials saved and synced to disk')
56
+ } finally {
57
+ await fileHandle.close()
58
+ }
59
+
60
+ // Notify Homebridge UI that config changed
61
+ server.pushEvent('configChanged', { source: 'switchbot-plugin' })
62
+ uiLog.debug('POST /credentials - Sent configChanged event to UI')
63
+
64
+ return {
65
+ success: true,
66
+ data: { message: 'Credentials saved successfully' },
67
+ }
68
+ }
69
+ } catch (e) {
70
+ uiLog.error(`Error in /credentials: ${e instanceof Error ? e.message : String(e)}`)
71
+ throw new RequestError('Failed to handle credentials request', e)
72
+ }
73
+ })
74
+ }
@@ -0,0 +1,380 @@
1
+ import type { HomebridgePluginUiServer } from '@homebridge/plugin-ui-utils'
2
+
3
+ import { RequestError } from '@homebridge/plugin-ui-utils'
4
+ import fs from 'node:fs/promises'
5
+
6
+ import { getAllDevices, getDeviceArrays, getSwitchBotPlatformConfig } from '../utils/config-parser.js'
7
+ import { uiLog } from '../utils/logger.js'
8
+
9
+ /**
10
+ * Register device CRUD endpoints
11
+ */
12
+ export function registerDeviceEndpoints(server: HomebridgePluginUiServer) {
13
+ /**
14
+ * POST /add-device - Add a single device to config
15
+ */
16
+ server.onRequest('/add-device', async (body: any) => {
17
+ try {
18
+ const { deviceId, name, type } = body
19
+
20
+ if (!deviceId) {
21
+ throw new Error('Device ID is required')
22
+ }
23
+
24
+ const { cfg, platform, cfgPath } = await getSwitchBotPlatformConfig(server)
25
+
26
+ uiLog.info('POST /add-device - Request received')
27
+ uiLog.debug(`POST /add-device - Request body: ${JSON.stringify(body)}`)
28
+ uiLog.debug(`POST /add-device - Config file path: ${cfgPath}`)
29
+
30
+ const deviceArrays = getDeviceArrays(platform)
31
+ const allDevices = getAllDevices(platform)
32
+
33
+ const normalizedDeviceId = String(deviceId).trim().toLowerCase()
34
+ const exists = allDevices.some((d: any) => String(d.deviceId ?? d.id ?? '').trim().toLowerCase() === normalizedDeviceId)
35
+ if (exists) {
36
+ uiLog.info(`POST /add-device - Device already exists: ${deviceId}`)
37
+ return {
38
+ success: true,
39
+ data: {
40
+ message: 'Device already in config',
41
+ alreadyExists: true,
42
+ },
43
+ }
44
+ }
45
+
46
+ let addCount = 0
47
+ for (const ref of deviceArrays) {
48
+ const refHasDevice = ref.some((d: any) => String(d.deviceId ?? d.id ?? '').trim().toLowerCase() === normalizedDeviceId)
49
+ if (!refHasDevice) {
50
+ const newDevice = {
51
+ deviceId,
52
+ configDeviceName: name,
53
+ configDeviceType: type,
54
+ }
55
+ ref.push(newDevice)
56
+ addCount++
57
+ }
58
+ }
59
+
60
+ if (!addCount) {
61
+ return {
62
+ success: false,
63
+ data: { message: 'Failed to add device to config' },
64
+ }
65
+ }
66
+
67
+ const configJson = JSON.stringify(cfg, null, 2)
68
+ const fileHandle = await fs.open(cfgPath, 'w')
69
+ try {
70
+ await fileHandle.writeFile(configJson, 'utf8')
71
+ await fileHandle.sync()
72
+ } finally {
73
+ await fileHandle.close()
74
+ }
75
+
76
+ // Notify Homebridge UI that config changed
77
+ server.pushEvent('configChanged', { source: 'switchbot-plugin' })
78
+ uiLog.debug('POST /add-device - Sent configChanged event to UI')
79
+
80
+ return {
81
+ success: true,
82
+ data: {
83
+ message: `Device "${name}" added successfully`,
84
+ alreadyExists: false,
85
+ added: true,
86
+ },
87
+ }
88
+ } catch (e) {
89
+ uiLog.error(`Error in /add-device: ${e instanceof Error ? e.message : String(e)}`)
90
+ throw new RequestError(`Failed to add device: ${e instanceof Error ? e.message : String(e)}`, e)
91
+ }
92
+ })
93
+
94
+ /**
95
+ * POST /add-devices - Add multiple devices to config (bulk add)
96
+ */
97
+ server.onRequest('/add-devices', async (body: any) => {
98
+ try {
99
+ const { devices } = body
100
+
101
+ if (!Array.isArray(devices) || devices.length === 0) {
102
+ throw new Error('Devices array is required and must not be empty')
103
+ }
104
+
105
+ const { cfg, platform, cfgPath } = await getSwitchBotPlatformConfig(server)
106
+ uiLog.debug(`POST /add-devices - Received ${devices.length} device(s) to add`)
107
+ uiLog.debug(`POST /add-devices - Config path: ${cfgPath}`)
108
+
109
+ const deviceArrays = getDeviceArrays(platform)
110
+ const allDevices = getAllDevices(platform)
111
+
112
+ uiLog.debug(`POST /add-devices - Device arrays count: ${deviceArrays.length}, existing devices: ${allDevices.length}`)
113
+ uiLog.debug(`POST /add-devices - Platform.devices exists: ${!!platform.devices}, length: ${platform.devices?.length || 0}`)
114
+
115
+ let addedCount = 0
116
+ let skippedCount = 0
117
+ const results: Array<Record<string, any>> = []
118
+
119
+ for (const { deviceId, name, type, rssi, address, model } of devices) {
120
+ if (!deviceId) {
121
+ results.push({ deviceId: '?', success: false, message: 'Device ID is required' })
122
+ skippedCount++
123
+ continue
124
+ }
125
+
126
+ const normalizedDeviceId = String(deviceId).trim().toLowerCase()
127
+ const exists = allDevices.some((d: any) => String(d.deviceId ?? d.id ?? '').trim().toLowerCase() === normalizedDeviceId)
128
+
129
+ if (exists) {
130
+ uiLog.debug(`POST /add-devices - Device already exists: ${deviceId}`)
131
+ results.push({ deviceId, success: true, message: 'Already in config', alreadyExists: true })
132
+ skippedCount++
133
+ continue
134
+ }
135
+
136
+ let addedToArray = false
137
+ for (const ref of deviceArrays) {
138
+ const refHasDevice = ref.some((d: any) => String(d.deviceId ?? d.id ?? '').trim().toLowerCase() === normalizedDeviceId)
139
+ if (!refHasDevice) {
140
+ const newDevice: any = {
141
+ deviceId,
142
+ configDeviceName: name,
143
+ configDeviceType: type,
144
+ }
145
+ // Store RSSI, address, and model if available
146
+ if (rssi !== undefined && rssi !== null && rssi !== 0) { newDevice.rssi = rssi }
147
+ if (address) { newDevice.address = address }
148
+ if (model) { newDevice.model = model }
149
+
150
+ ref.push(newDevice)
151
+ addedToArray = true
152
+ uiLog.debug(`POST /add-devices - Added device to array: ${deviceId} (${name}, ${type}, RSSI: ${rssi})`)
153
+ }
154
+ }
155
+
156
+ if (addedToArray) {
157
+ results.push({ deviceId, success: true, message: `Device "${name}" added` })
158
+ addedCount++
159
+ } else {
160
+ uiLog.warn(`POST /add-devices - Failed to add device: ${deviceId}`)
161
+ results.push({ deviceId, success: false, message: 'Failed to add device' })
162
+ skippedCount++
163
+ }
164
+ }
165
+
166
+ uiLog.info(`POST /add-devices - Adding ${addedCount} devices to config: ${cfgPath}`)
167
+ const configJson = JSON.stringify(cfg, null, 2)
168
+ const fileHandle = await fs.open(cfgPath, 'w')
169
+ try {
170
+ await fileHandle.writeFile(configJson, 'utf8')
171
+ await fileHandle.sync()
172
+ uiLog.info(`POST /add-devices - Config file written successfully`)
173
+ } finally {
174
+ await fileHandle.close()
175
+ }
176
+
177
+ // Notify Homebridge UI that config changed to prevent cache overwrite
178
+ server.pushEvent('configChanged', { source: 'switchbot-plugin' })
179
+ uiLog.debug('POST /add-devices - Sent configChanged event to UI')
180
+
181
+ return {
182
+ success: true,
183
+ data: {
184
+ message: `Added ${addedCount} device(s)${skippedCount > 0 ? ` (${skippedCount} skipped)` : ''}`,
185
+ addedCount,
186
+ skippedCount,
187
+ results,
188
+ },
189
+ }
190
+ } catch (e) {
191
+ const errorMsg = e instanceof Error ? e.message : String(e)
192
+ uiLog.error(`Error in /add-devices: ${errorMsg}`)
193
+ if (e instanceof Error && e.stack) {
194
+ uiLog.debug(`Stack trace: ${e.stack}`)
195
+ }
196
+ throw new RequestError(`Failed to add devices: ${errorMsg}`, e)
197
+ }
198
+ })
199
+
200
+ /**
201
+ * POST /update-device - Update existing device in config
202
+ */
203
+ server.onRequest('/update-device', async (body: any) => {
204
+ try {
205
+ const { deviceId, configDeviceName, configDeviceType, connectionPreference, room } = body
206
+
207
+ if (!deviceId) {
208
+ throw new Error('Device ID is required')
209
+ }
210
+
211
+ const { cfg, platform, cfgPath } = await getSwitchBotPlatformConfig(server)
212
+ const deviceArrays = getDeviceArrays(platform)
213
+ const devices = getAllDevices(platform)
214
+
215
+ const normalizedDeviceId = String(deviceId).trim().toLowerCase()
216
+ const device = devices.find((d: any) => String(d.deviceId ?? d.id ?? '').trim().toLowerCase() === normalizedDeviceId)
217
+ if (!device) {
218
+ throw new Error('Device not found in config')
219
+ }
220
+
221
+ for (const ref of deviceArrays) {
222
+ for (const entry of ref) {
223
+ const entryId = String(entry.deviceId ?? entry.id ?? '').trim().toLowerCase()
224
+ if (entryId !== normalizedDeviceId) {
225
+ continue
226
+ }
227
+
228
+ if (configDeviceName !== undefined && configDeviceName !== null && String(configDeviceName).trim() !== '') {
229
+ entry.configDeviceName = configDeviceName
230
+ }
231
+ if (configDeviceType !== undefined && configDeviceType !== null && String(configDeviceType).trim() !== '') {
232
+ entry.configDeviceType = configDeviceType
233
+ }
234
+ if (connectionPreference !== undefined) {
235
+ entry.connectionPreference = connectionPreference
236
+ }
237
+ if (room !== undefined) {
238
+ entry.room = room || undefined
239
+ }
240
+ }
241
+ }
242
+
243
+ const configJson = JSON.stringify(cfg, null, 2)
244
+ const fileHandle = await fs.open(cfgPath, 'w')
245
+ try {
246
+ await fileHandle.writeFile(configJson, 'utf8')
247
+ await fileHandle.sync()
248
+ } finally {
249
+ await fileHandle.close()
250
+ }
251
+
252
+ // Notify Homebridge UI that config changed
253
+ server.pushEvent('configChanged', { source: 'switchbot-plugin' })
254
+ uiLog.debug('POST /update-device - Sent configChanged event to UI')
255
+
256
+ return {
257
+ success: true,
258
+ data: { message: `Device updated successfully - type: ${configDeviceType || device.configDeviceType}` },
259
+ }
260
+ } catch (e) {
261
+ uiLog.error(`Error in /update-device: ${e instanceof Error ? e.message : String(e)}`)
262
+ throw new RequestError(`Failed to update device: ${e instanceof Error ? e.message : String(e)}`, e)
263
+ }
264
+ })
265
+
266
+ /**
267
+ * POST /delete-device - Remove device from config
268
+ */
269
+ server.onRequest('/delete-device', async (body: any) => {
270
+ try {
271
+ const { deviceId } = body
272
+ uiLog.info(`POST /delete-device - Requested deviceId: ${deviceId ?? 'undefined'}`)
273
+
274
+ if (!deviceId) {
275
+ throw new Error('Device ID is required')
276
+ }
277
+
278
+ const { cfg, platform, cfgPath } = await getSwitchBotPlatformConfig(server)
279
+ const deviceArrays = getDeviceArrays(platform)
280
+ const normalizedDeviceId = String(deviceId).trim().toLowerCase()
281
+ const allDevices = getAllDevices(platform)
282
+
283
+ const existing = allDevices.find((d: any) => String(d.deviceId ?? d.id ?? '').trim().toLowerCase() === normalizedDeviceId)
284
+ if (!existing) {
285
+ throw new Error('Device not found in config')
286
+ }
287
+
288
+ const deviceName = existing.configDeviceName || deviceId
289
+ let removedCount = 0
290
+ for (const ref of deviceArrays) {
291
+ for (let i = ref.length - 1; i >= 0; i--) {
292
+ const id = String(ref[i]?.deviceId ?? ref[i]?.id ?? '').trim().toLowerCase()
293
+ if (id === normalizedDeviceId) {
294
+ ref.splice(i, 1)
295
+ removedCount++
296
+ }
297
+ }
298
+ }
299
+
300
+ if (!removedCount) {
301
+ throw new Error('Device not found in config')
302
+ }
303
+
304
+ const configJson = JSON.stringify(cfg, null, 2)
305
+ const fileHandle = await fs.open(cfgPath, 'w')
306
+ try {
307
+ await fileHandle.writeFile(configJson, 'utf8')
308
+ await fileHandle.sync()
309
+ } finally {
310
+ await fileHandle.close()
311
+ }
312
+
313
+ // Notify Homebridge UI that config changed
314
+ server.pushEvent('configChanged', { source: 'switchbot-plugin' })
315
+ uiLog.debug('DELETE /delete-device - Sent configChanged event to UI')
316
+
317
+ return {
318
+ success: true,
319
+ data: { message: `Device "${deviceName}" removed from config` },
320
+ }
321
+ } catch (e) {
322
+ uiLog.error(`Error in /delete-device: ${e instanceof Error ? e.message : String(e)}`)
323
+ throw new RequestError('Failed to delete device', e)
324
+ }
325
+ })
326
+
327
+ /**
328
+ * POST /delete-all-devices - Remove all devices from config
329
+ */
330
+ server.onRequest('/delete-all-devices', async () => {
331
+ try {
332
+ uiLog.info('POST /delete-all-devices - Request received')
333
+
334
+ const { cfg, platform, cfgPath } = await getSwitchBotPlatformConfig(server)
335
+ const deviceArrays = getDeviceArrays(platform)
336
+ const allDevices = getAllDevices(platform)
337
+
338
+ if (allDevices.length === 0) {
339
+ return {
340
+ success: true,
341
+ data: { message: 'No devices to delete', deletedCount: 0 },
342
+ }
343
+ }
344
+
345
+ const deviceCount = allDevices.length
346
+ uiLog.info(`POST /delete-all-devices - Removing ${deviceCount} device(s)`)
347
+
348
+ // Clear all device arrays
349
+ for (const ref of deviceArrays) {
350
+ ref.length = 0 // Clear array in place
351
+ }
352
+
353
+ const configJson = JSON.stringify(cfg, null, 2)
354
+ const fileHandle = await fs.open(cfgPath, 'w')
355
+ try {
356
+ await fileHandle.writeFile(configJson, 'utf8')
357
+ await fileHandle.sync()
358
+ uiLog.info('POST /delete-all-devices - Config file written successfully')
359
+ } finally {
360
+ await fileHandle.close()
361
+ }
362
+
363
+ // Notify Homebridge UI that config changed
364
+ server.pushEvent('configChanged', { source: 'switchbot-plugin' })
365
+ uiLog.debug('DELETE /delete-all-devices - Sent configChanged event to UI')
366
+
367
+ return {
368
+ success: true,
369
+ data: {
370
+ message: `Removed ${deviceCount} device(s) from config`,
371
+ deletedCount: deviceCount,
372
+ },
373
+ }
374
+ } catch (e) {
375
+ uiLog.error(`Error in /delete-all-devices: ${e instanceof Error ? e.message : String(e)}`)
376
+ throw new RequestError(`Failed to delete all devices: ${e instanceof Error ? e.message : String(e)}`, e)
377
+ }
378
+ })
379
+
380
+ }