@switchbot/homebridge-switchbot 5.0.0-beta.13 → 5.0.0-beta.130

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 (593) hide show
  1. package/.github/ISSUE_TEMPLATE/e2e-verification.md +36 -0
  2. package/.github/copilot-instructions.md +39 -0
  3. package/.github/workflows/ci.yml +32 -0
  4. package/.github/workflows/manual-e2e.yml +115 -0
  5. package/.github/workflows/release.yml +0 -4
  6. package/CHANGELOG.md +35 -0
  7. package/E2E-VERIFICATION.md +121 -0
  8. package/MIGRATION.md +54 -0
  9. package/README.md +99 -3
  10. package/TODO.md +263 -0
  11. package/config.schema.json +198 -14787
  12. package/dist/device-types.d.ts +31 -0
  13. package/dist/device-types.d.ts.map +1 -0
  14. package/dist/device-types.js +246 -0
  15. package/dist/device-types.js.map +1 -0
  16. package/dist/deviceFactory.d.ts +13 -0
  17. package/dist/deviceFactory.d.ts.map +1 -0
  18. package/dist/deviceFactory.js +146 -0
  19. package/dist/deviceFactory.js.map +1 -0
  20. package/dist/devices/deviceBase.d.ts +50 -0
  21. package/dist/devices/deviceBase.d.ts.map +1 -0
  22. package/dist/devices/deviceBase.js +119 -0
  23. package/dist/devices/deviceBase.js.map +1 -0
  24. package/dist/devices/genericDevice.d.ts +277 -0
  25. package/dist/devices/genericDevice.d.ts.map +1 -0
  26. package/dist/devices/genericDevice.js +1383 -0
  27. package/dist/devices/genericDevice.js.map +1 -0
  28. package/dist/errors.d.ts +5 -0
  29. package/dist/errors.d.ts.map +1 -0
  30. package/dist/errors.js +51 -0
  31. package/dist/errors.js.map +1 -0
  32. package/dist/homebridge-ui/endpoints/config.d.ts +3 -0
  33. package/dist/homebridge-ui/endpoints/config.d.ts.map +1 -0
  34. package/dist/homebridge-ui/endpoints/config.js +107 -0
  35. package/dist/homebridge-ui/endpoints/config.js.map +1 -0
  36. package/dist/homebridge-ui/endpoints/credentials.d.ts +6 -0
  37. package/dist/homebridge-ui/endpoints/credentials.d.ts.map +1 -0
  38. package/dist/homebridge-ui/endpoints/credentials.js +65 -0
  39. package/dist/homebridge-ui/endpoints/credentials.js.map +1 -0
  40. package/dist/homebridge-ui/endpoints/devices.d.ts +6 -0
  41. package/dist/homebridge-ui/endpoints/devices.d.ts.map +1 -0
  42. package/dist/homebridge-ui/endpoints/devices.js +430 -0
  43. package/dist/homebridge-ui/endpoints/devices.js.map +1 -0
  44. package/dist/homebridge-ui/endpoints/discovery.d.ts +7 -0
  45. package/dist/homebridge-ui/endpoints/discovery.d.ts.map +1 -0
  46. package/dist/homebridge-ui/endpoints/discovery.js +212 -0
  47. package/dist/homebridge-ui/endpoints/discovery.js.map +1 -0
  48. package/dist/homebridge-ui/public/css/styles.css +447 -0
  49. package/dist/homebridge-ui/public/index.html +144 -248
  50. package/dist/homebridge-ui/public/js/api.d.ts +62 -0
  51. package/dist/homebridge-ui/public/js/api.d.ts.map +1 -0
  52. package/dist/homebridge-ui/public/js/api.js +225 -0
  53. package/dist/homebridge-ui/public/js/api.js.map +1 -0
  54. package/dist/homebridge-ui/public/js/api.ts +279 -0
  55. package/dist/homebridge-ui/public/js/app.d.ts +2 -0
  56. package/dist/homebridge-ui/public/js/app.d.ts.map +1 -0
  57. package/dist/homebridge-ui/public/js/app.js +3600 -0
  58. package/dist/homebridge-ui/public/js/app.js.map +7 -0
  59. package/dist/homebridge-ui/public/js/app.ts +23 -0
  60. package/dist/homebridge-ui/public/js/constants.d.ts +2 -0
  61. package/dist/homebridge-ui/public/js/constants.d.ts.map +1 -0
  62. package/dist/homebridge-ui/public/js/constants.js +2 -0
  63. package/dist/homebridge-ui/public/js/constants.js.map +1 -0
  64. package/dist/homebridge-ui/public/js/constants.ts +1 -0
  65. package/dist/homebridge-ui/public/js/credentials.d.ts +3 -0
  66. package/dist/homebridge-ui/public/js/credentials.d.ts.map +1 -0
  67. package/dist/homebridge-ui/public/js/credentials.js +96 -0
  68. package/dist/homebridge-ui/public/js/credentials.js.map +1 -0
  69. package/dist/homebridge-ui/public/js/credentials.ts +100 -0
  70. package/dist/homebridge-ui/public/js/devices-delete.d.ts +3 -0
  71. package/dist/homebridge-ui/public/js/devices-delete.d.ts.map +1 -0
  72. package/dist/homebridge-ui/public/js/devices-delete.js +206 -0
  73. package/dist/homebridge-ui/public/js/devices-delete.js.map +1 -0
  74. package/dist/homebridge-ui/public/js/devices-delete.ts +236 -0
  75. package/dist/homebridge-ui/public/js/devices.d.ts +9 -0
  76. package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -0
  77. package/dist/homebridge-ui/public/js/devices.js +94 -0
  78. package/dist/homebridge-ui/public/js/devices.js.map +1 -0
  79. package/dist/homebridge-ui/public/js/devices.ts +114 -0
  80. package/dist/homebridge-ui/public/js/discovery.d.ts +4 -0
  81. package/dist/homebridge-ui/public/js/discovery.d.ts.map +1 -0
  82. package/dist/homebridge-ui/public/js/discovery.js +1364 -0
  83. package/dist/homebridge-ui/public/js/discovery.js.map +1 -0
  84. package/dist/homebridge-ui/public/js/discovery.ts +1492 -0
  85. package/dist/homebridge-ui/public/js/logger.d.ts +6 -0
  86. package/dist/homebridge-ui/public/js/logger.d.ts.map +1 -0
  87. package/dist/homebridge-ui/public/js/logger.js +12 -0
  88. package/dist/homebridge-ui/public/js/logger.js.map +1 -0
  89. package/dist/homebridge-ui/public/js/logger.ts +11 -0
  90. package/dist/homebridge-ui/public/js/modal.d.ts +5 -0
  91. package/dist/homebridge-ui/public/js/modal.d.ts.map +1 -0
  92. package/dist/homebridge-ui/public/js/modal.js +26 -0
  93. package/dist/homebridge-ui/public/js/modal.js.map +1 -0
  94. package/dist/homebridge-ui/public/js/modal.ts +28 -0
  95. package/dist/homebridge-ui/public/js/modals.d.ts +7 -0
  96. package/dist/homebridge-ui/public/js/modals.d.ts.map +1 -0
  97. package/dist/homebridge-ui/public/js/modals.js +494 -0
  98. package/dist/homebridge-ui/public/js/modals.js.map +1 -0
  99. package/dist/homebridge-ui/public/js/modals.ts +546 -0
  100. package/dist/homebridge-ui/public/js/render.d.ts +68 -0
  101. package/dist/homebridge-ui/public/js/render.d.ts.map +1 -0
  102. package/dist/homebridge-ui/public/js/render.js +914 -0
  103. package/dist/homebridge-ui/public/js/render.js.map +1 -0
  104. package/dist/homebridge-ui/public/js/render.ts +1026 -0
  105. package/dist/homebridge-ui/public/js/toast.d.ts +6 -0
  106. package/dist/homebridge-ui/public/js/toast.d.ts.map +1 -0
  107. package/dist/homebridge-ui/public/js/toast.js +29 -0
  108. package/dist/homebridge-ui/public/js/toast.js.map +1 -0
  109. package/dist/homebridge-ui/public/js/toast.ts +37 -0
  110. package/dist/homebridge-ui/public/js/types.d.ts +23 -0
  111. package/dist/homebridge-ui/public/js/types.d.ts.map +1 -0
  112. package/dist/homebridge-ui/public/js/types.js +2 -0
  113. package/dist/homebridge-ui/public/js/types.js.map +1 -0
  114. package/dist/homebridge-ui/public/js/types.ts +26 -0
  115. package/dist/homebridge-ui/server.js +11 -41
  116. package/dist/homebridge-ui/server.js.map +1 -1
  117. package/dist/homebridge-ui/utils/config-parser.d.ts +35 -0
  118. package/dist/homebridge-ui/utils/config-parser.d.ts.map +1 -0
  119. package/dist/homebridge-ui/utils/config-parser.js +87 -0
  120. package/dist/homebridge-ui/utils/config-parser.js.map +1 -0
  121. package/dist/homebridge-ui/utils/device-migration.d.ts +35 -0
  122. package/dist/homebridge-ui/utils/device-migration.d.ts.map +1 -0
  123. package/dist/homebridge-ui/utils/device-migration.js +111 -0
  124. package/dist/homebridge-ui/utils/device-migration.js.map +1 -0
  125. package/dist/homebridge-ui/utils/logger.d.ts +7 -0
  126. package/dist/homebridge-ui/utils/logger.d.ts.map +1 -0
  127. package/dist/homebridge-ui/utils/logger.js +17 -0
  128. package/dist/homebridge-ui/utils/logger.js.map +1 -0
  129. package/dist/index.d.ts.map +1 -1
  130. package/dist/index.js +1 -3
  131. package/dist/index.js.map +1 -1
  132. package/dist/platform.d.ts +39 -0
  133. package/dist/platform.d.ts.map +1 -0
  134. package/dist/platform.js +1570 -0
  135. package/dist/platform.js.map +1 -0
  136. package/dist/settings.d.ts +10 -249
  137. package/dist/settings.d.ts.map +1 -1
  138. package/dist/settings.js +5 -30
  139. package/dist/settings.js.map +1 -1
  140. package/dist/switchbotClient.d.ts +32 -0
  141. package/dist/switchbotClient.d.ts.map +1 -0
  142. package/dist/switchbotClient.js +253 -0
  143. package/dist/switchbotClient.js.map +1 -0
  144. package/dist/utils.d.ts +36 -82
  145. package/dist/utils.d.ts.map +1 -1
  146. package/dist/utils.js +40 -786
  147. package/dist/utils.js.map +1 -1
  148. package/docs/assets/highlight.css +21 -0
  149. package/docs/assets/icons.js +1 -1
  150. package/docs/assets/icons.svg +1 -1
  151. package/docs/assets/main.js +2 -2
  152. package/docs/assets/style.css +3 -3
  153. package/docs/index.html +103 -13
  154. package/docs/variables/default.html +1 -1
  155. package/eslint.config.js +7 -8
  156. package/nodemon.json +2 -2
  157. package/package.json +36 -32
  158. package/scripts/build-ui.js +36 -0
  159. package/scripts/e2e/README.md +25 -0
  160. package/scripts/e2e/curtain-e2e.sh +70 -0
  161. package/scripts/e2e/fan-e2e.sh +75 -0
  162. package/scripts/e2e/light-advanced-e2e.sh +97 -0
  163. package/scripts/e2e/light-e2e.sh +75 -0
  164. package/scripts/e2e/list-accessories.sh +19 -0
  165. package/scripts/e2e/lock-e2e.sh +65 -0
  166. package/scripts/free-dev-ports.mjs +105 -0
  167. package/scripts/generate-matter-maps.js +60 -0
  168. package/scripts/run-e2e-local.sh +14 -0
  169. package/scripts/sync-device-types.mjs +28 -0
  170. package/src/device-types.ts +261 -0
  171. package/src/deviceFactory.ts +190 -0
  172. package/src/devices/deviceBase.ts +141 -0
  173. package/src/devices/genericDevice.ts +1333 -0
  174. package/src/errors.ts +43 -0
  175. package/src/homebridge-ui/endpoints/config.ts +126 -0
  176. package/src/homebridge-ui/endpoints/credentials.ts +74 -0
  177. package/src/homebridge-ui/endpoints/devices.ts +464 -0
  178. package/src/homebridge-ui/endpoints/discovery.ts +233 -0
  179. package/src/homebridge-ui/public/css/styles.css +447 -0
  180. package/src/homebridge-ui/public/index.html +144 -248
  181. package/src/homebridge-ui/public/js/api.ts +279 -0
  182. package/src/homebridge-ui/public/js/app.ts +23 -0
  183. package/src/homebridge-ui/public/js/constants.ts +1 -0
  184. package/src/homebridge-ui/public/js/credentials.ts +100 -0
  185. package/src/homebridge-ui/public/js/devices-delete.ts +236 -0
  186. package/src/homebridge-ui/public/js/devices.ts +114 -0
  187. package/src/homebridge-ui/public/js/discovery.ts +1492 -0
  188. package/src/homebridge-ui/public/js/logger.ts +11 -0
  189. package/src/homebridge-ui/public/js/modal.ts +28 -0
  190. package/src/homebridge-ui/public/js/modals.ts +546 -0
  191. package/src/homebridge-ui/public/js/render.ts +1026 -0
  192. package/src/homebridge-ui/public/js/toast.ts +37 -0
  193. package/src/homebridge-ui/public/js/types.ts +26 -0
  194. package/src/homebridge-ui/server.ts +11 -43
  195. package/src/homebridge-ui/utils/config-parser.ts +108 -0
  196. package/src/homebridge-ui/utils/device-migration.ts +144 -0
  197. package/src/homebridge-ui/utils/logger.ts +17 -0
  198. package/src/index.ts +1 -3
  199. package/src/platform.ts +1585 -0
  200. package/src/settings.ts +12 -277
  201. package/src/switchbotClient.ts +251 -0
  202. package/src/utils.ts +48 -812
  203. package/test/accessory-restore.spec.ts +73 -0
  204. package/test/device-mapping.spec.ts +37 -0
  205. package/test/deviceFactory.spec.ts +18 -0
  206. package/test/e2e/run-e2e.spec.ts +52 -0
  207. package/test/fan-swing.spec.ts +29 -0
  208. package/test/helpers/matter-harness.ts +53 -0
  209. package/test/lock-users.spec.ts +44 -0
  210. package/test/matter-childbridge.spec.ts +59 -0
  211. package/test/matter-descriptors.spec.ts +97 -0
  212. package/test/matter-device-state.spec.ts +101 -0
  213. package/test/matter-integration.spec.ts +136 -0
  214. package/test/platform.integration.spec.ts +55 -0
  215. package/test/switchbot-client-debounce.spec.ts +131 -0
  216. package/test/switchbot-client-openapi.spec.ts +56 -0
  217. package/test/switchbotClient.spec.ts +10 -0
  218. package/test/utils.spec.ts +36 -0
  219. package/todo/COMPLETED.md +542 -0
  220. package/todo/README.md +59 -0
  221. package/todo/TODO.md +190 -0
  222. package/todo/mockups/filtering-sorting.svg +112 -0
  223. package/todo/mockups/modal-switchbot-theme.svg +66 -0
  224. package/todo/mockups/signal-strength-visualization.svg +94 -0
  225. package/vitest.config.ts +7 -0
  226. package/coverage/base.css +0 -224
  227. package/coverage/block-navigation.js +0 -87
  228. package/coverage/clover.xml +0 -15847
  229. package/coverage/coverage-final.json +0 -42
  230. package/coverage/docs/assets/dmt/dmt-component-data.js.html +0 -85
  231. package/coverage/docs/assets/dmt/dmt-components.js.html +0 -286
  232. package/coverage/docs/assets/dmt/index.html +0 -131
  233. package/coverage/docs/assets/hierarchy.js.html +0 -85
  234. package/coverage/docs/assets/icons.js.html +0 -136
  235. package/coverage/docs/assets/index.html +0 -146
  236. package/coverage/docs/assets/main.js.html +0 -265
  237. package/coverage/favicon.png +0 -0
  238. package/coverage/index.html +0 -191
  239. package/coverage/prettify.css +0 -1
  240. package/coverage/prettify.js +0 -2
  241. package/coverage/sort-arrow-sprite.png +0 -0
  242. package/coverage/sorter.js +0 -196
  243. package/coverage/src/device/blindtilt.ts.html +0 -3238
  244. package/coverage/src/device/bot.ts.html +0 -2803
  245. package/coverage/src/device/ceilinglight.ts.html +0 -2338
  246. package/coverage/src/device/colorbulb.ts.html +0 -2824
  247. package/coverage/src/device/contact.ts.html +0 -1465
  248. package/coverage/src/device/curtain.ts.html +0 -2869
  249. package/coverage/src/device/device.ts.html +0 -2500
  250. package/coverage/src/device/fan.ts.html +0 -2242
  251. package/coverage/src/device/hub.ts.html +0 -1408
  252. package/coverage/src/device/humidifier.ts.html +0 -2116
  253. package/coverage/src/device/index.html +0 -416
  254. package/coverage/src/device/iosensor.ts.html +0 -1375
  255. package/coverage/src/device/lightstrip.ts.html +0 -2617
  256. package/coverage/src/device/lock.ts.html +0 -1963
  257. package/coverage/src/device/meter.ts.html +0 -1372
  258. package/coverage/src/device/meterplus.ts.html +0 -1384
  259. package/coverage/src/device/meterpro.ts.html +0 -1618
  260. package/coverage/src/device/motion.ts.html +0 -1264
  261. package/coverage/src/device/plug.ts.html +0 -1372
  262. package/coverage/src/device/relayswitch.ts.html +0 -2284
  263. package/coverage/src/device/robotvacuumcleaner.ts.html +0 -1810
  264. package/coverage/src/device/waterdetector.ts.html +0 -1294
  265. package/coverage/src/homebridge-ui/index.html +0 -116
  266. package/coverage/src/homebridge-ui/server.ts.html +0 -229
  267. package/coverage/src/index.html +0 -161
  268. package/coverage/src/index.ts.html +0 -124
  269. package/coverage/src/irdevice/airconditioner.ts.html +0 -1687
  270. package/coverage/src/irdevice/airpurifier.ts.html +0 -844
  271. package/coverage/src/irdevice/camera.ts.html +0 -475
  272. package/coverage/src/irdevice/fan.ts.html +0 -766
  273. package/coverage/src/irdevice/index.html +0 -251
  274. package/coverage/src/irdevice/irdevice.ts.html +0 -1117
  275. package/coverage/src/irdevice/light.ts.html +0 -826
  276. package/coverage/src/irdevice/other.ts.html +0 -2458
  277. package/coverage/src/irdevice/tv.ts.html +0 -1222
  278. package/coverage/src/irdevice/vacuumcleaner.ts.html +0 -466
  279. package/coverage/src/irdevice/waterheater.ts.html +0 -469
  280. package/coverage/src/platform.ts.html +0 -8776
  281. package/coverage/src/settings.ts.html +0 -934
  282. package/coverage/src/utils.ts.html +0 -2092
  283. package/dist/baseMatterAccessory.test.d.ts +0 -2
  284. package/dist/baseMatterAccessory.test.d.ts.map +0 -1
  285. package/dist/baseMatterAccessory.test.js +0 -71
  286. package/dist/baseMatterAccessory.test.js.map +0 -1
  287. package/dist/devices-hap/airpurifier.d.ts +0 -54
  288. package/dist/devices-hap/airpurifier.d.ts.map +0 -1
  289. package/dist/devices-hap/airpurifier.js +0 -527
  290. package/dist/devices-hap/airpurifier.js.map +0 -1
  291. package/dist/devices-hap/blindtilt.d.ts +0 -90
  292. package/dist/devices-hap/blindtilt.d.ts.map +0 -1
  293. package/dist/devices-hap/blindtilt.js +0 -974
  294. package/dist/devices-hap/blindtilt.js.map +0 -1
  295. package/dist/devices-hap/bot.d.ts +0 -102
  296. package/dist/devices-hap/bot.d.ts.map +0 -1
  297. package/dist/devices-hap/bot.js +0 -811
  298. package/dist/devices-hap/bot.js.map +0 -1
  299. package/dist/devices-hap/ceilinglight.d.ts +0 -85
  300. package/dist/devices-hap/ceilinglight.d.ts.map +0 -1
  301. package/dist/devices-hap/ceilinglight.js +0 -701
  302. package/dist/devices-hap/ceilinglight.js.map +0 -1
  303. package/dist/devices-hap/colorbulb.d.ts +0 -88
  304. package/dist/devices-hap/colorbulb.d.ts.map +0 -1
  305. package/dist/devices-hap/colorbulb.js +0 -881
  306. package/dist/devices-hap/colorbulb.js.map +0 -1
  307. package/dist/devices-hap/contact.d.ts +0 -44
  308. package/dist/devices-hap/contact.d.ts.map +0 -1
  309. package/dist/devices-hap/contact.js +0 -409
  310. package/dist/devices-hap/contact.js.map +0 -1
  311. package/dist/devices-hap/curtain.d.ts +0 -73
  312. package/dist/devices-hap/curtain.d.ts.map +0 -1
  313. package/dist/devices-hap/curtain.js +0 -869
  314. package/dist/devices-hap/curtain.js.map +0 -1
  315. package/dist/devices-hap/device.d.ts +0 -98
  316. package/dist/devices-hap/device.d.ts.map +0 -1
  317. package/dist/devices-hap/device.js +0 -749
  318. package/dist/devices-hap/device.js.map +0 -1
  319. package/dist/devices-hap/fan.d.ts +0 -69
  320. package/dist/devices-hap/fan.d.ts.map +0 -1
  321. package/dist/devices-hap/fan.js +0 -649
  322. package/dist/devices-hap/fan.js.map +0 -1
  323. package/dist/devices-hap/hub.d.ts +0 -37
  324. package/dist/devices-hap/hub.d.ts.map +0 -1
  325. package/dist/devices-hap/hub.js +0 -392
  326. package/dist/devices-hap/hub.js.map +0 -1
  327. package/dist/devices-hap/humidifier.d.ts +0 -68
  328. package/dist/devices-hap/humidifier.d.ts.map +0 -1
  329. package/dist/devices-hap/humidifier.js +0 -628
  330. package/dist/devices-hap/humidifier.js.map +0 -1
  331. package/dist/devices-hap/iosensor.d.ts +0 -42
  332. package/dist/devices-hap/iosensor.d.ts.map +0 -1
  333. package/dist/devices-hap/iosensor.js +0 -382
  334. package/dist/devices-hap/iosensor.js.map +0 -1
  335. package/dist/devices-hap/lightstrip.d.ts +0 -79
  336. package/dist/devices-hap/lightstrip.d.ts.map +0 -1
  337. package/dist/devices-hap/lightstrip.js +0 -797
  338. package/dist/devices-hap/lightstrip.js.map +0 -1
  339. package/dist/devices-hap/lock.d.ts +0 -53
  340. package/dist/devices-hap/lock.d.ts.map +0 -1
  341. package/dist/devices-hap/lock.js +0 -561
  342. package/dist/devices-hap/lock.js.map +0 -1
  343. package/dist/devices-hap/meter.d.ts +0 -37
  344. package/dist/devices-hap/meter.d.ts.map +0 -1
  345. package/dist/devices-hap/meter.js +0 -379
  346. package/dist/devices-hap/meter.js.map +0 -1
  347. package/dist/devices-hap/meterplus.d.ts +0 -42
  348. package/dist/devices-hap/meterplus.d.ts.map +0 -1
  349. package/dist/devices-hap/meterplus.js +0 -384
  350. package/dist/devices-hap/meterplus.js.map +0 -1
  351. package/dist/devices-hap/meterpro.d.ts +0 -43
  352. package/dist/devices-hap/meterpro.d.ts.map +0 -1
  353. package/dist/devices-hap/meterpro.js +0 -468
  354. package/dist/devices-hap/meterpro.js.map +0 -1
  355. package/dist/devices-hap/motion.d.ts +0 -42
  356. package/dist/devices-hap/motion.d.ts.map +0 -1
  357. package/dist/devices-hap/motion.js +0 -345
  358. package/dist/devices-hap/motion.js.map +0 -1
  359. package/dist/devices-hap/plug.d.ts +0 -49
  360. package/dist/devices-hap/plug.d.ts.map +0 -1
  361. package/dist/devices-hap/plug.js +0 -395
  362. package/dist/devices-hap/plug.js.map +0 -1
  363. package/dist/devices-hap/relayswitch.d.ts +0 -96
  364. package/dist/devices-hap/relayswitch.d.ts.map +0 -1
  365. package/dist/devices-hap/relayswitch.js +0 -642
  366. package/dist/devices-hap/relayswitch.js.map +0 -1
  367. package/dist/devices-hap/robotvacuumcleaner.d.ts +0 -54
  368. package/dist/devices-hap/robotvacuumcleaner.d.ts.map +0 -1
  369. package/dist/devices-hap/robotvacuumcleaner.js +0 -523
  370. package/dist/devices-hap/robotvacuumcleaner.js.map +0 -1
  371. package/dist/devices-hap/waterdetector.d.ts +0 -41
  372. package/dist/devices-hap/waterdetector.d.ts.map +0 -1
  373. package/dist/devices-hap/waterdetector.js +0 -356
  374. package/dist/devices-hap/waterdetector.js.map +0 -1
  375. package/dist/devices-matter/BaseMatterAccessory.d.ts +0 -78
  376. package/dist/devices-matter/BaseMatterAccessory.d.ts.map +0 -1
  377. package/dist/devices-matter/BaseMatterAccessory.js +0 -220
  378. package/dist/devices-matter/BaseMatterAccessory.js.map +0 -1
  379. package/dist/devices-matter/ColorLightAccessory.d.ts +0 -20
  380. package/dist/devices-matter/ColorLightAccessory.d.ts.map +0 -1
  381. package/dist/devices-matter/ColorLightAccessory.js +0 -95
  382. package/dist/devices-matter/ColorLightAccessory.js.map +0 -1
  383. package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts +0 -18
  384. package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts.map +0 -1
  385. package/dist/devices-matter/ColorTemperatureLightAccessory.js +0 -76
  386. package/dist/devices-matter/ColorTemperatureLightAccessory.js.map +0 -1
  387. package/dist/devices-matter/ContactSensorAccessory.d.ts +0 -12
  388. package/dist/devices-matter/ContactSensorAccessory.d.ts.map +0 -1
  389. package/dist/devices-matter/ContactSensorAccessory.js +0 -34
  390. package/dist/devices-matter/ContactSensorAccessory.js.map +0 -1
  391. package/dist/devices-matter/DimmableLightAccessory.d.ts +0 -58
  392. package/dist/devices-matter/DimmableLightAccessory.d.ts.map +0 -1
  393. package/dist/devices-matter/DimmableLightAccessory.js +0 -167
  394. package/dist/devices-matter/DimmableLightAccessory.js.map +0 -1
  395. package/dist/devices-matter/DoorLockAccessory.d.ts +0 -14
  396. package/dist/devices-matter/DoorLockAccessory.d.ts.map +0 -1
  397. package/dist/devices-matter/DoorLockAccessory.js +0 -50
  398. package/dist/devices-matter/DoorLockAccessory.js.map +0 -1
  399. package/dist/devices-matter/ExtendedColorLightAccessory.d.ts +0 -21
  400. package/dist/devices-matter/ExtendedColorLightAccessory.d.ts.map +0 -1
  401. package/dist/devices-matter/ExtendedColorLightAccessory.js +0 -106
  402. package/dist/devices-matter/ExtendedColorLightAccessory.js.map +0 -1
  403. package/dist/devices-matter/FanAccessory.d.ts +0 -16
  404. package/dist/devices-matter/FanAccessory.d.ts.map +0 -1
  405. package/dist/devices-matter/FanAccessory.js +0 -81
  406. package/dist/devices-matter/FanAccessory.js.map +0 -1
  407. package/dist/devices-matter/HumiditySensorAccessory.d.ts +0 -12
  408. package/dist/devices-matter/HumiditySensorAccessory.d.ts.map +0 -1
  409. package/dist/devices-matter/HumiditySensorAccessory.js +0 -34
  410. package/dist/devices-matter/HumiditySensorAccessory.js.map +0 -1
  411. package/dist/devices-matter/LeakSensorAccessory.d.ts +0 -12
  412. package/dist/devices-matter/LeakSensorAccessory.d.ts.map +0 -1
  413. package/dist/devices-matter/LeakSensorAccessory.js +0 -33
  414. package/dist/devices-matter/LeakSensorAccessory.js.map +0 -1
  415. package/dist/devices-matter/LightSensorAccessory.d.ts +0 -12
  416. package/dist/devices-matter/LightSensorAccessory.d.ts.map +0 -1
  417. package/dist/devices-matter/LightSensorAccessory.js +0 -34
  418. package/dist/devices-matter/LightSensorAccessory.js.map +0 -1
  419. package/dist/devices-matter/OccupancySensorAccessory.d.ts +0 -12
  420. package/dist/devices-matter/OccupancySensorAccessory.d.ts.map +0 -1
  421. package/dist/devices-matter/OccupancySensorAccessory.js +0 -39
  422. package/dist/devices-matter/OccupancySensorAccessory.js.map +0 -1
  423. package/dist/devices-matter/OnOffLightAccessory.d.ts +0 -38
  424. package/dist/devices-matter/OnOffLightAccessory.d.ts.map +0 -1
  425. package/dist/devices-matter/OnOffLightAccessory.js +0 -110
  426. package/dist/devices-matter/OnOffLightAccessory.js.map +0 -1
  427. package/dist/devices-matter/OnOffOutletAccessory.d.ts +0 -14
  428. package/dist/devices-matter/OnOffOutletAccessory.d.ts.map +0 -1
  429. package/dist/devices-matter/OnOffOutletAccessory.js +0 -43
  430. package/dist/devices-matter/OnOffOutletAccessory.js.map +0 -1
  431. package/dist/devices-matter/OnOffSwitchAccessory.d.ts +0 -14
  432. package/dist/devices-matter/OnOffSwitchAccessory.d.ts.map +0 -1
  433. package/dist/devices-matter/OnOffSwitchAccessory.js +0 -42
  434. package/dist/devices-matter/OnOffSwitchAccessory.js.map +0 -1
  435. package/dist/devices-matter/RoboticVacuumAccessory.d.ts +0 -68
  436. package/dist/devices-matter/RoboticVacuumAccessory.d.ts.map +0 -1
  437. package/dist/devices-matter/RoboticVacuumAccessory.js +0 -334
  438. package/dist/devices-matter/RoboticVacuumAccessory.js.map +0 -1
  439. package/dist/devices-matter/SmokeCOAlarmAccessory.d.ts +0 -11
  440. package/dist/devices-matter/SmokeCOAlarmAccessory.d.ts.map +0 -1
  441. package/dist/devices-matter/SmokeCOAlarmAccessory.js +0 -49
  442. package/dist/devices-matter/SmokeCOAlarmAccessory.js.map +0 -1
  443. package/dist/devices-matter/TemperatureSensorAccessory.d.ts +0 -12
  444. package/dist/devices-matter/TemperatureSensorAccessory.d.ts.map +0 -1
  445. package/dist/devices-matter/TemperatureSensorAccessory.js +0 -36
  446. package/dist/devices-matter/TemperatureSensorAccessory.js.map +0 -1
  447. package/dist/devices-matter/ThermostatAccessory.d.ts +0 -19
  448. package/dist/devices-matter/ThermostatAccessory.d.ts.map +0 -1
  449. package/dist/devices-matter/ThermostatAccessory.js +0 -95
  450. package/dist/devices-matter/ThermostatAccessory.js.map +0 -1
  451. package/dist/devices-matter/VenetianBlindAccessory.d.ts +0 -19
  452. package/dist/devices-matter/VenetianBlindAccessory.d.ts.map +0 -1
  453. package/dist/devices-matter/VenetianBlindAccessory.js +0 -99
  454. package/dist/devices-matter/VenetianBlindAccessory.js.map +0 -1
  455. package/dist/devices-matter/WindowBlindAccessory.d.ts +0 -17
  456. package/dist/devices-matter/WindowBlindAccessory.d.ts.map +0 -1
  457. package/dist/devices-matter/WindowBlindAccessory.js +0 -80
  458. package/dist/devices-matter/WindowBlindAccessory.js.map +0 -1
  459. package/dist/devices-matter/custom/PowerStripAccessory.d.ts +0 -97
  460. package/dist/devices-matter/custom/PowerStripAccessory.d.ts.map +0 -1
  461. package/dist/devices-matter/custom/PowerStripAccessory.js +0 -265
  462. package/dist/devices-matter/custom/PowerStripAccessory.js.map +0 -1
  463. package/dist/devices-matter/custom/index.d.ts +0 -8
  464. package/dist/devices-matter/custom/index.d.ts.map +0 -1
  465. package/dist/devices-matter/custom/index.js +0 -8
  466. package/dist/devices-matter/custom/index.js.map +0 -1
  467. package/dist/devices-matter/index.d.ts +0 -29
  468. package/dist/devices-matter/index.d.ts.map +0 -1
  469. package/dist/devices-matter/index.js +0 -28
  470. package/dist/devices-matter/index.js.map +0 -1
  471. package/dist/index.test.d.ts +0 -2
  472. package/dist/index.test.d.ts.map +0 -1
  473. package/dist/index.test.js +0 -19
  474. package/dist/index.test.js.map +0 -1
  475. package/dist/irdevice/airconditioner.d.ts +0 -61
  476. package/dist/irdevice/airconditioner.d.ts.map +0 -1
  477. package/dist/irdevice/airconditioner.js +0 -472
  478. package/dist/irdevice/airconditioner.js.map +0 -1
  479. package/dist/irdevice/airpurifier.d.ts +0 -50
  480. package/dist/irdevice/airpurifier.d.ts.map +0 -1
  481. package/dist/irdevice/airpurifier.js +0 -213
  482. package/dist/irdevice/airpurifier.js.map +0 -1
  483. package/dist/irdevice/camera.d.ts +0 -32
  484. package/dist/irdevice/camera.d.ts.map +0 -1
  485. package/dist/irdevice/camera.js +0 -107
  486. package/dist/irdevice/camera.js.map +0 -1
  487. package/dist/irdevice/fan.d.ts +0 -36
  488. package/dist/irdevice/fan.d.ts.map +0 -1
  489. package/dist/irdevice/fan.js +0 -200
  490. package/dist/irdevice/fan.js.map +0 -1
  491. package/dist/irdevice/irdevice.d.ts +0 -68
  492. package/dist/irdevice/irdevice.d.ts.map +0 -1
  493. package/dist/irdevice/irdevice.js +0 -298
  494. package/dist/irdevice/irdevice.js.map +0 -1
  495. package/dist/irdevice/light.d.ts +0 -36
  496. package/dist/irdevice/light.d.ts.map +0 -1
  497. package/dist/irdevice/light.js +0 -206
  498. package/dist/irdevice/light.js.map +0 -1
  499. package/dist/irdevice/other.d.ts +0 -57
  500. package/dist/irdevice/other.d.ts.map +0 -1
  501. package/dist/irdevice/other.js +0 -778
  502. package/dist/irdevice/other.js.map +0 -1
  503. package/dist/irdevice/tv.d.ts +0 -45
  504. package/dist/irdevice/tv.d.ts.map +0 -1
  505. package/dist/irdevice/tv.js +0 -327
  506. package/dist/irdevice/tv.js.map +0 -1
  507. package/dist/irdevice/vacuumcleaner.d.ts +0 -28
  508. package/dist/irdevice/vacuumcleaner.d.ts.map +0 -1
  509. package/dist/irdevice/vacuumcleaner.js +0 -104
  510. package/dist/irdevice/vacuumcleaner.js.map +0 -1
  511. package/dist/irdevice/waterheater.d.ts +0 -30
  512. package/dist/irdevice/waterheater.d.ts.map +0 -1
  513. package/dist/irdevice/waterheater.js +0 -105
  514. package/dist/irdevice/waterheater.js.map +0 -1
  515. package/dist/platform-hap.d.ts +0 -149
  516. package/dist/platform-hap.d.ts.map +0 -1
  517. package/dist/platform-hap.js +0 -2861
  518. package/dist/platform-hap.js.map +0 -1
  519. package/dist/platform-matter.d.ts +0 -120
  520. package/dist/platform-matter.d.ts.map +0 -1
  521. package/dist/platform-matter.js +0 -999
  522. package/dist/platform-matter.js.map +0 -1
  523. package/dist/utils.test.d.ts +0 -2
  524. package/dist/utils.test.d.ts.map +0 -1
  525. package/dist/utils.test.js +0 -95
  526. package/dist/utils.test.js.map +0 -1
  527. package/dist/verifyconfig.test.d.ts +0 -2
  528. package/dist/verifyconfig.test.d.ts.map +0 -1
  529. package/dist/verifyconfig.test.js +0 -167
  530. package/dist/verifyconfig.test.js.map +0 -1
  531. package/src/baseMatterAccessory.test.ts +0 -88
  532. package/src/custom.d.ts +0 -7
  533. package/src/devices-hap/airpurifier.ts +0 -563
  534. package/src/devices-hap/blindtilt.ts +0 -1049
  535. package/src/devices-hap/bot.ts +0 -900
  536. package/src/devices-hap/ceilinglight.ts +0 -742
  537. package/src/devices-hap/colorbulb.ts +0 -904
  538. package/src/devices-hap/contact.ts +0 -457
  539. package/src/devices-hap/curtain.ts +0 -944
  540. package/src/devices-hap/device.ts +0 -811
  541. package/src/devices-hap/fan.ts +0 -711
  542. package/src/devices-hap/hub.ts +0 -439
  543. package/src/devices-hap/humidifier.ts +0 -669
  544. package/src/devices-hap/iosensor.ts +0 -427
  545. package/src/devices-hap/lightstrip.ts +0 -836
  546. package/src/devices-hap/lock.ts +0 -620
  547. package/src/devices-hap/meter.ts +0 -426
  548. package/src/devices-hap/meterplus.ts +0 -430
  549. package/src/devices-hap/meterpro.ts +0 -522
  550. package/src/devices-hap/motion.ts +0 -390
  551. package/src/devices-hap/plug.ts +0 -423
  552. package/src/devices-hap/relayswitch.ts +0 -727
  553. package/src/devices-hap/robotvacuumcleaner.ts +0 -568
  554. package/src/devices-hap/waterdetector.ts +0 -400
  555. package/src/devices-matter/BaseMatterAccessory.ts +0 -249
  556. package/src/devices-matter/ColorLightAccessory.ts +0 -110
  557. package/src/devices-matter/ColorTemperatureLightAccessory.ts +0 -90
  558. package/src/devices-matter/ContactSensorAccessory.ts +0 -41
  559. package/src/devices-matter/DimmableLightAccessory.ts +0 -192
  560. package/src/devices-matter/DoorLockAccessory.ts +0 -60
  561. package/src/devices-matter/ExtendedColorLightAccessory.ts +0 -122
  562. package/src/devices-matter/FanAccessory.ts +0 -95
  563. package/src/devices-matter/HumiditySensorAccessory.ts +0 -41
  564. package/src/devices-matter/LeakSensorAccessory.ts +0 -40
  565. package/src/devices-matter/LightSensorAccessory.ts +0 -41
  566. package/src/devices-matter/OccupancySensorAccessory.ts +0 -48
  567. package/src/devices-matter/OnOffLightAccessory.ts +0 -125
  568. package/src/devices-matter/OnOffOutletAccessory.ts +0 -51
  569. package/src/devices-matter/OnOffSwitchAccessory.ts +0 -51
  570. package/src/devices-matter/RoboticVacuumAccessory.ts +0 -407
  571. package/src/devices-matter/SmokeCOAlarmAccessory.ts +0 -59
  572. package/src/devices-matter/TemperatureSensorAccessory.ts +0 -43
  573. package/src/devices-matter/ThermostatAccessory.ts +0 -110
  574. package/src/devices-matter/VenetianBlindAccessory.ts +0 -115
  575. package/src/devices-matter/WindowBlindAccessory.ts +0 -92
  576. package/src/devices-matter/custom/PowerStripAccessory.ts +0 -309
  577. package/src/devices-matter/custom/index.ts +0 -8
  578. package/src/devices-matter/index.ts +0 -29
  579. package/src/index.test.ts +0 -24
  580. package/src/irdevice/airconditioner.ts +0 -533
  581. package/src/irdevice/airpurifier.ts +0 -252
  582. package/src/irdevice/camera.ts +0 -129
  583. package/src/irdevice/fan.ts +0 -226
  584. package/src/irdevice/irdevice.ts +0 -344
  585. package/src/irdevice/light.ts +0 -246
  586. package/src/irdevice/other.ts +0 -790
  587. package/src/irdevice/tv.ts +0 -378
  588. package/src/irdevice/vacuumcleaner.ts +0 -126
  589. package/src/irdevice/waterheater.ts +0 -127
  590. package/src/platform-hap.ts +0 -2997
  591. package/src/platform-matter.ts +0 -1124
  592. package/src/utils.test.ts +0 -96
  593. package/src/verifyconfig.test.ts +0 -197
@@ -0,0 +1,279 @@
1
+ import { isValidDeviceType, normalizeDeviceType } from '../../../device-types.js'
2
+ import './types.js'
3
+ import { uiLog } from './logger.js'
4
+
5
+ /**
6
+ * Validate and auto-correct device types in the config array before saving.
7
+ * Returns an array of errors for devices that cannot be fixed.
8
+ */
9
+ export function validateAndFixDeviceTypes(devices: Array<{ deviceId: string, configDeviceName: string, configDeviceType: string }>) {
10
+ const errors: Array<{ deviceId: string, name: string, type: string }> = []
11
+ for (const d of devices) {
12
+ if (!isValidDeviceType(d.configDeviceType)) {
13
+ const fixed = normalizeDeviceType(d.configDeviceType)
14
+ if (fixed) {
15
+ d.configDeviceType = fixed
16
+ } else {
17
+ errors.push({
18
+ deviceId: d.deviceId,
19
+ name: d.configDeviceName,
20
+ type: d.configDeviceType,
21
+ })
22
+ }
23
+ }
24
+ }
25
+ return errors
26
+ }
27
+
28
+ // API wrapper functions for communicating with the Homebridge UI server
29
+
30
+ function isSwitchBotPlatformConfig(block: any): boolean {
31
+ const platformName = String(block?.platform || block?.name || '').toLowerCase()
32
+ return (
33
+ platformName === 'switchbot'
34
+ || platformName === '@switchbot/homebridge-switchbot'
35
+ || platformName.includes('switchbot')
36
+ )
37
+ }
38
+
39
+ export async function syncParentPluginConfigFromDisk(autoSave = false): Promise<boolean> {
40
+ try {
41
+ if (
42
+ typeof homebridge.getPluginConfig !== 'function'
43
+ || typeof homebridge.updatePluginConfig !== 'function'
44
+ ) {
45
+ uiLog.warn('Parent config sync API not available')
46
+ return false
47
+ }
48
+
49
+ const diskResp = await homebridge.request('/platform-config', {})
50
+ if (!diskResp || diskResp.success === false || !diskResp.data) {
51
+ uiLog.warn('Failed to fetch fresh platform config from disk')
52
+ return false
53
+ }
54
+
55
+ const pluginConfigBlocks = await homebridge.getPluginConfig()
56
+ if (!Array.isArray(pluginConfigBlocks) || !pluginConfigBlocks.length) {
57
+ uiLog.warn('No plugin config blocks returned from Homebridge')
58
+ return false
59
+ }
60
+
61
+ const index = pluginConfigBlocks.findIndex(block => isSwitchBotPlatformConfig(block))
62
+ if (index < 0) {
63
+ uiLog.warn('SwitchBot platform block not found in Homebridge plugin config')
64
+ return false
65
+ }
66
+
67
+ // Validate and fix device types before saving
68
+ const errors = validateAndFixDeviceTypes(diskResp.data.devices || [])
69
+ if (errors.length > 0) {
70
+ homebridge.toast?.error?.(`Invalid device types found: ${errors.map(e => `${e.name} (${e.type})`).join(', ')}`)
71
+ return false
72
+ }
73
+ pluginConfigBlocks[index] = diskResp.data
74
+ await homebridge.updatePluginConfig(pluginConfigBlocks)
75
+
76
+ // Auto-save to disk if requested - prevents parent UI from overwriting with stale cache
77
+ if (autoSave && typeof homebridge.savePluginConfig === 'function') {
78
+ uiLog.info('Auto-saving config to disk...')
79
+ await homebridge.savePluginConfig()
80
+ uiLog.info('Config saved successfully')
81
+ }
82
+
83
+ return true
84
+ } catch (e) {
85
+ uiLog.warn('Failed to sync parent plugin config cache:', e)
86
+ return false
87
+ }
88
+ }
89
+
90
+ export async function fetchCredentialStatus(): Promise<any> {
91
+ try {
92
+ const resp = await homebridge.request('/credentials', {})
93
+ uiLog.info('Load credentials response:', resp)
94
+
95
+ if (!resp || resp.success === false) {
96
+ uiLog.error('Failed to load credentials:', resp)
97
+ return null
98
+ }
99
+
100
+ return resp.data || {}
101
+ } catch (e) {
102
+ uiLog.error('Error loading credentials:', e)
103
+ return null
104
+ }
105
+ }
106
+
107
+ export async function saveCredentials(token: string, secret: string): Promise<any> {
108
+ uiLog.info('Saving credentials...')
109
+ const resp = await homebridge.request('/credentials', { token, secret })
110
+ uiLog.info('Save response:', resp)
111
+ if (!resp || resp.success === false) {
112
+ throw new Error(resp?.message || 'Save failed')
113
+ }
114
+ return resp.data || resp
115
+ }
116
+
117
+ export async function fetchDevices(): Promise<any[]> {
118
+ try {
119
+ const resp = await homebridge.request('/devices', {})
120
+ if (!resp || resp.success === false) {
121
+ // Prefer backend error message if available
122
+ const backendMsg = resp?.data?.message || resp?.message
123
+ throw new Error(backendMsg || 'request failed')
124
+ }
125
+ return resp.data || []
126
+ } catch (e) {
127
+ // Show the real error to the user
128
+ const msg = e instanceof Error ? e.message : String(e)
129
+ uiLog.error('Error fetching devices:', msg)
130
+ // Optionally, show a toast or UI error here if needed
131
+ return []
132
+ }
133
+ }
134
+
135
+ export interface DiscoverRequestOptions {
136
+ bleEnabled?: boolean
137
+ bleScanDurationSeconds?: number
138
+ bleTimeoutSeconds?: number
139
+ }
140
+
141
+ export async function discoverDevices(
142
+ mode: 'all' | 'ble' | 'openapi' = 'all',
143
+ options?: DiscoverRequestOptions,
144
+ ): Promise<any[]> {
145
+ const resp = await homebridge.request('/discover', { mode, ...options })
146
+ uiLog.info('Discover response:', resp)
147
+ if (!resp || resp.success === false) {
148
+ throw new Error(resp?.data?.message || 'Discovery failed')
149
+ }
150
+ return resp.data || []
151
+ }
152
+
153
+ export async function fetchBluetoothStatus(): Promise<{ available: boolean, message: string }> {
154
+ try {
155
+ const resp = await homebridge.request('/ble-status', {})
156
+ if (!resp || resp.success === false) {
157
+ return { available: false, message: 'Bluetooth status unavailable' }
158
+ }
159
+ return resp.data || { available: false, message: 'Bluetooth status unavailable' }
160
+ } catch (_e) {
161
+ return { available: false, message: 'Bluetooth status unavailable' }
162
+ }
163
+ }
164
+
165
+ export async function testDeviceConnection(payload: {
166
+ deviceId: string
167
+ connectionType?: string
168
+ address?: string
169
+ }): Promise<{
170
+ success: boolean
171
+ deviceId: string
172
+ method: string
173
+ latencyMs: number
174
+ message: string
175
+ state?: Record<string, any>
176
+ }> {
177
+ const resp = await homebridge.request('/test-connection', payload)
178
+ if (!resp || resp.success === false) {
179
+ throw new Error(resp?.data?.message || 'Connection test failed')
180
+ }
181
+
182
+ return resp.data || {
183
+ success: false,
184
+ deviceId: payload.deviceId,
185
+ method: 'Auto',
186
+ latencyMs: 0,
187
+ message: 'Connection test failed',
188
+ }
189
+ }
190
+
191
+ export async function addDevice(
192
+ deviceId: string,
193
+ name: string,
194
+ type: string,
195
+ options?: { address?: string, model?: string, rssi?: number, encryptionKey?: string, keyId?: string, matterType?: number },
196
+ ): Promise<any> {
197
+ const payload: any = { deviceId, name, type }
198
+ if (options?.address) {
199
+ payload.address = options.address
200
+ }
201
+ if (options?.model) {
202
+ payload.model = options.model
203
+ }
204
+ if (options?.rssi !== undefined && options?.rssi !== null && options?.rssi !== 0) {
205
+ payload.rssi = options.rssi
206
+ }
207
+ if (options?.encryptionKey) {
208
+ payload.encryptionKey = options.encryptionKey
209
+ }
210
+ if (options?.keyId) {
211
+ payload.keyId = options.keyId
212
+ }
213
+ if (options?.matterType !== undefined) {
214
+ payload.matterType = options.matterType
215
+ }
216
+ uiLog.info('Adding device to config:', payload)
217
+ const resp = await homebridge.request('/add-device', payload)
218
+ uiLog.info('Add device response:', resp)
219
+ if (!resp || resp.success === false) {
220
+ throw new Error(resp?.data?.message || 'Failed to add device')
221
+ }
222
+ return resp.data || resp
223
+ }
224
+
225
+ export async function addDevicesInBulk(
226
+ devices: Array<{ deviceId: string, name: string, type: string, rssi?: number, address?: string, model?: string }>,
227
+ ): Promise<any> {
228
+ const resp = await homebridge.request('/add-devices', { devices })
229
+ uiLog.info('Bulk add response:', resp)
230
+ if (!resp || resp.success === false) {
231
+ throw new Error(resp?.data?.message || 'Bulk add failed')
232
+ }
233
+ return resp.data || resp
234
+ }
235
+
236
+ export async function updateDevice(
237
+ deviceId: string,
238
+ configDeviceName?: string,
239
+ configDeviceType?: string,
240
+ options?: { matterType?: number },
241
+ ): Promise<any> {
242
+ const params: any = { deviceId }
243
+ if (configDeviceName) {
244
+ params.configDeviceName = configDeviceName
245
+ }
246
+ if (configDeviceType) {
247
+ params.configDeviceType = configDeviceType
248
+ }
249
+ if (options?.matterType !== undefined) {
250
+ params.matterType = options.matterType
251
+ }
252
+ uiLog.info('[Update Device] Sending update request with params:', params)
253
+ const resp = await homebridge.request('/update-device', params)
254
+ uiLog.info('[Update Device] Update response:', resp)
255
+ if (!resp || resp.success === false) {
256
+ throw new Error(resp?.data?.message || 'Failed to update device')
257
+ }
258
+ return resp.data || resp
259
+ }
260
+
261
+ export async function deleteDevice(deviceId: string): Promise<any> {
262
+ uiLog.info('Sending delete request for deviceId:', deviceId)
263
+ const resp = await homebridge.request('/delete-device', { deviceId })
264
+ uiLog.info('Delete response:', resp)
265
+ if (!resp || resp.success === false) {
266
+ throw new Error(resp?.data?.message || 'Failed to delete device')
267
+ }
268
+ return resp.data || resp
269
+ }
270
+
271
+ export async function deleteAllDevices(): Promise<any> {
272
+ uiLog.info('Sending delete all devices request')
273
+ const resp = await homebridge.request('/delete-all-devices', {})
274
+ uiLog.info('Delete all response:', resp)
275
+ if (!resp || resp.success === false) {
276
+ throw new Error(resp?.data?.message || 'Failed to delete all devices')
277
+ }
278
+ return resp.data || resp
279
+ }
@@ -0,0 +1,23 @@
1
+ import { loadCredentialStatus, saveCredentials } from './credentials.js'
2
+ import { loadConfiguredDevices, initRemoveAllButton } from './devices.js'
3
+ // Wire up global event handlers
4
+ import { discoverDevices, initializeDiscoverySettings } from './discovery.js';
5
+
6
+ (window as any).loadCredentialStatus = loadCredentialStatus
7
+ ;(window as any).saveCredentials = saveCredentials
8
+ ;(window as any).discoverDevices = discoverDevices
9
+
10
+ // Initialize on page load
11
+ async function init(): Promise<void> {
12
+ await loadCredentialStatus()
13
+ await initializeDiscoverySettings()
14
+ const list = await loadConfiguredDevices()
15
+ await initRemoveAllButton()
16
+ }
17
+
18
+ // Run initialization when DOM is ready
19
+ if (document.readyState === 'loading') {
20
+ document.addEventListener('DOMContentLoaded', init)
21
+ } else {
22
+ init()
23
+ }
@@ -0,0 +1 @@
1
+ export { DEVICE_TYPES } from '../../../device-types.js'
@@ -0,0 +1,100 @@
1
+ import { fetchCredentialStatus as apiFetchCredentialStatus, saveCredentials as apiSaveCredentials, syncParentPluginConfigFromDisk } from './api.js'
2
+ import { uiLog } from './logger.js'
3
+ import { hideBusyUi, showBusyUi } from './modal.js'
4
+ import { toastError, toastSuccess, toastWarning } from './toast.js'
5
+
6
+ export async function loadCredentialStatus(): Promise<void> {
7
+ try {
8
+ const creds = await apiFetchCredentialStatus()
9
+
10
+ if (!creds) {
11
+ uiLog.error('Failed to load credentials')
12
+ return
13
+ }
14
+
15
+ const tokenStatus = document.getElementById('tokenStatus')
16
+ const secretStatus = document.getElementById('secretStatus')
17
+
18
+ if (!tokenStatus || !secretStatus) { return }
19
+
20
+ if (creds.hasToken) {
21
+ tokenStatus.textContent = `✓ Configured (${creds.tokenLength} characters)`
22
+ tokenStatus.classList.add('ok')
23
+ } else {
24
+ tokenStatus.textContent = 'Not configured'
25
+ tokenStatus.classList.remove('ok')
26
+ }
27
+
28
+ if (creds.hasSecret) {
29
+ secretStatus.textContent = `✓ Configured (${creds.secretLength} characters)`
30
+ secretStatus.classList.add('ok')
31
+ } else {
32
+ secretStatus.textContent = 'Not configured'
33
+ secretStatus.classList.remove('ok')
34
+ }
35
+ } catch (e) {
36
+ uiLog.error('Error loading credentials:', e)
37
+ }
38
+ }
39
+
40
+ export async function saveCredentials(): Promise<void> {
41
+ const token = (document.getElementById('token') as HTMLInputElement)?.value
42
+ const secret = (document.getElementById('secret') as HTMLInputElement)?.value
43
+ const saveStatus = document.getElementById('saveStatus')
44
+ const saveBtn = document.getElementById('saveBtn') as HTMLButtonElement
45
+
46
+ if (!saveStatus || !saveBtn) { return }
47
+
48
+ if (!token || !secret) {
49
+ saveStatus.textContent = 'Please enter both token and secret'
50
+ saveStatus.classList.add('error')
51
+ toastWarning('Please enter both token and secret')
52
+ return
53
+ }
54
+
55
+ try {
56
+ showBusyUi()
57
+ saveBtn.disabled = true
58
+ saveBtn.textContent = 'Saving...'
59
+ uiLog.info('Saving credentials...')
60
+
61
+ const result = await apiSaveCredentials(token, secret)
62
+ uiLog.info('Save response:', result)
63
+
64
+ const message = result?.message || 'Credentials saved successfully'
65
+ saveStatus.textContent = `✓ ${message}`
66
+ saveStatus.classList.remove('error')
67
+ saveStatus.classList.add('success-msg')
68
+ toastSuccess(message)
69
+
70
+ const synced = await syncParentPluginConfigFromDisk(true)
71
+ if (synced) {
72
+ saveStatus.textContent += ' - Config saved automatically.'
73
+ toastSuccess('Configuration synced and saved automatically')
74
+ } else {
75
+ toastWarning('Credentials saved, but configuration sync failed')
76
+ }
77
+
78
+ // Clear inputs after successful save
79
+ ;(document.getElementById('token') as HTMLInputElement).value = ''
80
+ ;(document.getElementById('secret') as HTMLInputElement).value = ''
81
+
82
+ // Reload status to verify save
83
+ setTimeout(() => loadCredentialStatus(), 500)
84
+
85
+ // Clear status message
86
+ setTimeout(() => {
87
+ saveStatus.textContent = ''
88
+ saveStatus.classList.remove('success-msg')
89
+ }, 3000)
90
+ } catch (e) {
91
+ uiLog.error('Save error:', e)
92
+ saveStatus.textContent = `Error: ${e instanceof Error ? e.message : 'Failed to save'}`
93
+ saveStatus.classList.add('error')
94
+ toastError(e instanceof Error ? e.message : 'Failed to save credentials')
95
+ } finally {
96
+ hideBusyUi()
97
+ saveBtn.disabled = false
98
+ saveBtn.textContent = 'Save Credentials'
99
+ }
100
+ }
@@ -0,0 +1,236 @@
1
+ import { deleteDevice as apiDeleteDevice, deleteAllDevices as apiDeleteAllDevices, syncParentPluginConfigFromDisk, fetchDevices } from './api.js'
2
+ import { uiLog } from './logger.js'
3
+ import { hideBusyUi, showBusyUi } from './modal.js'
4
+ import { renderDeviceList } from './render.js'
5
+ import { toastError, toastSuccess, toastWarning } from './toast.js'
6
+
7
+ async function confirmDeleteDialog(deviceNameOrId: string): Promise<boolean> {
8
+ return new Promise((resolve) => {
9
+ const overlay = document.createElement('div')
10
+ overlay.style.position = 'fixed'
11
+ overlay.style.top = '0'
12
+ overlay.style.left = '0'
13
+ overlay.style.width = '100%'
14
+ overlay.style.height = '100%'
15
+ overlay.style.background = 'rgba(0,0,0,0.6)'
16
+ overlay.style.display = 'flex'
17
+ overlay.style.alignItems = 'center'
18
+ overlay.style.justifyContent = 'center'
19
+ overlay.style.zIndex = '10000'
20
+
21
+ const modal = document.createElement('div')
22
+ modal.style.background = getComputedStyle(document.body).backgroundColor
23
+ modal.style.color = getComputedStyle(document.body).color
24
+ modal.style.padding = '20px'
25
+ modal.style.borderRadius = '10px'
26
+ modal.style.minWidth = '340px'
27
+ modal.style.maxWidth = '90vw'
28
+ modal.style.boxShadow = '0 12px 40px rgba(0,0,0,0.35)'
29
+
30
+ const title = document.createElement('h3')
31
+ title.textContent = 'Delete Device'
32
+ title.style.margin = '0 0 10px 0'
33
+
34
+ const message = document.createElement('p')
35
+ message.textContent = `Remove "${deviceNameOrId}" from configuration?`
36
+ message.style.margin = '0 0 16px 0'
37
+
38
+ const actions = document.createElement('div')
39
+ actions.style.display = 'flex'
40
+ actions.style.justifyContent = 'flex-end'
41
+ actions.style.gap = '8px'
42
+
43
+ const cancelBtn = document.createElement('button')
44
+ cancelBtn.textContent = 'Cancel'
45
+ cancelBtn.style.background = '#6b7280'
46
+
47
+ const deleteBtn = document.createElement('button')
48
+ deleteBtn.textContent = 'Delete'
49
+ deleteBtn.style.background = '#ef4444'
50
+
51
+ const cleanup = (result: boolean) => {
52
+ overlay.remove()
53
+ resolve(result)
54
+ }
55
+
56
+ cancelBtn.onclick = () => cleanup(false)
57
+ deleteBtn.onclick = () => cleanup(true)
58
+ overlay.addEventListener('click', (event) => {
59
+ if (event.target === overlay) {
60
+ cleanup(false)
61
+ }
62
+ })
63
+
64
+ actions.appendChild(cancelBtn)
65
+ actions.appendChild(deleteBtn)
66
+ modal.appendChild(title)
67
+ modal.appendChild(message)
68
+ modal.appendChild(actions)
69
+ overlay.appendChild(modal)
70
+ document.body.appendChild(overlay)
71
+ })
72
+ }
73
+
74
+ export async function deleteDeviceFromConfig(deviceId: string, deviceName: string): Promise<void> {
75
+ uiLog.info('Delete button clicked for device:', deviceId, deviceName)
76
+
77
+ const confirmed = await confirmDeleteDialog(deviceName || deviceId)
78
+ if (!confirmed) {
79
+ uiLog.info('Delete cancelled by user')
80
+ return
81
+ }
82
+
83
+ try {
84
+ showBusyUi()
85
+ uiLog.info('Sending delete request for deviceId:', deviceId)
86
+ const resp = await apiDeleteDevice(deviceId)
87
+ uiLog.info('Delete response:', resp)
88
+
89
+ if (!resp || resp.success === false) {
90
+ throw new Error(resp?.data?.message || 'Failed to delete device')
91
+ }
92
+
93
+ uiLog.info('Syncing parent config from disk...')
94
+ const synced = await syncParentPluginConfigFromDisk(true)
95
+ if (!synced) {
96
+ toastWarning('Device deleted, but configuration sync failed')
97
+ }
98
+
99
+ uiLog.info('Refreshing device list...')
100
+ // Refresh device list
101
+ const list = await fetchDevices()
102
+ uiLog.info('Rendering devices:', list.length)
103
+ renderDeviceList(list)
104
+
105
+ uiLog.info('✓ Device deleted successfully')
106
+ toastSuccess(`Device "${deviceName || deviceId}" deleted successfully`)
107
+ } catch (e) {
108
+ uiLog.error('Delete error:', e)
109
+ toastError(e instanceof Error ? e.message : 'Failed to delete device')
110
+ } finally {
111
+ hideBusyUi()
112
+ }
113
+ }
114
+
115
+ async function confirmDeleteAllDialog(deviceCount: number): Promise<boolean> {
116
+ return new Promise((resolve) => {
117
+ const overlay = document.createElement('div')
118
+ overlay.style.position = 'fixed'
119
+ overlay.style.top = '0'
120
+ overlay.style.left = '0'
121
+ overlay.style.width = '100%'
122
+ overlay.style.height = '100%'
123
+ overlay.style.background = 'rgba(0,0,0,0.7)'
124
+ overlay.style.display = 'flex'
125
+ overlay.style.alignItems = 'center'
126
+ overlay.style.justifyContent = 'center'
127
+ overlay.style.zIndex = '10000'
128
+
129
+ const modal = document.createElement('div')
130
+ modal.style.background = getComputedStyle(document.body).backgroundColor
131
+ modal.style.color = getComputedStyle(document.body).color
132
+ modal.style.padding = '20px'
133
+ modal.style.borderRadius = '10px'
134
+ modal.style.minWidth = '380px'
135
+ modal.style.maxWidth = '90vw'
136
+ modal.style.boxShadow = '0 12px 40px rgba(0,0,0,0.35)'
137
+ modal.style.borderTop = '3px solid #ef4444'
138
+
139
+ const title = document.createElement('h3')
140
+ title.textContent = '⚠️ Remove All Devices'
141
+ title.style.margin = '0 0 12px 0'
142
+ title.style.color = '#ef4444'
143
+
144
+ const message = document.createElement('p')
145
+ message.innerHTML = `Are you sure you want to remove <strong>all ${deviceCount} device(s)</strong> from your configuration?<br><br>This action cannot be undone.`
146
+ message.style.margin = '0 0 18px 0'
147
+ message.style.lineHeight = '1.5'
148
+
149
+ const actions = document.createElement('div')
150
+ actions.style.display = 'flex'
151
+ actions.style.justifyContent = 'flex-end'
152
+ actions.style.gap = '10px'
153
+
154
+ const cancelBtn = document.createElement('button')
155
+ cancelBtn.textContent = 'Cancel'
156
+ cancelBtn.style.background = '#6b7280'
157
+ cancelBtn.style.padding = '8px 16px'
158
+ cancelBtn.style.fontSize = '13px'
159
+ cancelBtn.className = 'secondary'
160
+
161
+ const deleteBtn = document.createElement('button')
162
+ deleteBtn.textContent = 'Remove All'
163
+ deleteBtn.style.background = '#ef4444'
164
+ deleteBtn.style.padding = '8px 20px'
165
+ deleteBtn.style.fontSize = '13px'
166
+
167
+ const cleanup = (result: boolean) => {
168
+ overlay.remove()
169
+ resolve(result)
170
+ }
171
+
172
+ cancelBtn.onclick = () => cleanup(false)
173
+ deleteBtn.onclick = () => cleanup(true)
174
+ overlay.addEventListener('click', (event) => {
175
+ if (event.target === overlay) {
176
+ cleanup(false)
177
+ }
178
+ })
179
+
180
+ actions.appendChild(cancelBtn)
181
+ actions.appendChild(deleteBtn)
182
+ modal.appendChild(title)
183
+ modal.appendChild(message)
184
+ modal.appendChild(actions)
185
+ overlay.appendChild(modal)
186
+ document.body.appendChild(overlay)
187
+ })
188
+ }
189
+
190
+ export async function deleteAllDevicesFromConfig(): Promise<void> {
191
+ uiLog.info('Remove All Devices button clicked')
192
+
193
+ try {
194
+ // Get current device count
195
+ const list = await fetchDevices()
196
+ if (!list || list.length === 0) {
197
+ toastWarning('No devices to remove')
198
+ return
199
+ }
200
+
201
+ const confirmed = await confirmDeleteAllDialog(list.length)
202
+ if (!confirmed) {
203
+ uiLog.info('Remove all cancelled by user')
204
+ return
205
+ }
206
+
207
+ showBusyUi()
208
+ uiLog.info('Sending delete all devices request')
209
+ const resp = await apiDeleteAllDevices()
210
+ uiLog.info('Delete all response:', resp)
211
+
212
+ if (!resp || resp.success === false) {
213
+ throw new Error(resp?.data?.message || 'Failed to delete all devices')
214
+ }
215
+
216
+ uiLog.info('Syncing parent config from disk...')
217
+ const synced = await syncParentPluginConfigFromDisk(true)
218
+ if (!synced) {
219
+ toastWarning('Devices deleted, but configuration sync failed')
220
+ }
221
+
222
+ uiLog.info('Refreshing device list...')
223
+ const updatedList = await fetchDevices()
224
+ uiLog.info('Rendering devices:', updatedList.length)
225
+ renderDeviceList(updatedList)
226
+
227
+ const deletedCount = resp?.deletedCount || list.length
228
+ uiLog.info(`✓ Removed ${deletedCount} device(s) successfully`)
229
+ toastSuccess(`Removed ${deletedCount} device(s) successfully`)
230
+ } catch (e) {
231
+ uiLog.error('Delete all error:', e)
232
+ toastError(e instanceof Error ? e.message : 'Failed to delete all devices')
233
+ } finally {
234
+ hideBusyUi()
235
+ }
236
+ }