@switchbot/homebridge-switchbot 5.0.0-beta.15 → 5.0.0-beta.150

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 (610) hide show
  1. package/.changeset/config.json +14 -0
  2. package/.github/ISSUE_TEMPLATE/e2e-verification.md +36 -0
  3. package/.github/copilot-instructions.md +39 -0
  4. package/.github/workflows/ci.yml +32 -0
  5. package/.github/workflows/manual-e2e.yml +115 -0
  6. package/.github/workflows/release.yml +0 -4
  7. package/.husky/pre-push +15 -0
  8. package/CHANGELOG.md +35 -0
  9. package/E2E-VERIFICATION.md +121 -0
  10. package/MIGRATION.md +54 -0
  11. package/README.md +136 -4
  12. package/TODO.md +263 -0
  13. package/config.schema.json +284 -14787
  14. package/dist/SwitchBotHAPPlatform.d.ts +133 -0
  15. package/dist/SwitchBotHAPPlatform.d.ts.map +1 -0
  16. package/dist/SwitchBotHAPPlatform.js +555 -0
  17. package/dist/SwitchBotHAPPlatform.js.map +1 -0
  18. package/dist/SwitchBotMatterPlatform.d.ts +141 -0
  19. package/dist/SwitchBotMatterPlatform.d.ts.map +1 -0
  20. package/dist/SwitchBotMatterPlatform.js +536 -0
  21. package/dist/SwitchBotMatterPlatform.js.map +1 -0
  22. package/dist/device-types.d.ts +31 -0
  23. package/dist/device-types.d.ts.map +1 -0
  24. package/dist/device-types.js +246 -0
  25. package/dist/device-types.js.map +1 -0
  26. package/dist/deviceCommandMapper.d.ts +10 -0
  27. package/dist/deviceCommandMapper.d.ts.map +1 -0
  28. package/dist/deviceCommandMapper.js +319 -0
  29. package/dist/deviceCommandMapper.js.map +1 -0
  30. package/dist/deviceFactory.d.ts +14 -0
  31. package/dist/deviceFactory.d.ts.map +1 -0
  32. package/dist/deviceFactory.js +159 -0
  33. package/dist/deviceFactory.js.map +1 -0
  34. package/dist/devices/deviceBase.d.ts +50 -0
  35. package/dist/devices/deviceBase.d.ts.map +1 -0
  36. package/dist/devices/deviceBase.js +119 -0
  37. package/dist/devices/deviceBase.js.map +1 -0
  38. package/dist/devices/genericDevice.d.ts +320 -0
  39. package/dist/devices/genericDevice.d.ts.map +1 -0
  40. package/dist/devices/genericDevice.js +1363 -0
  41. package/dist/devices/genericDevice.js.map +1 -0
  42. package/dist/errors.d.ts +38 -0
  43. package/dist/errors.d.ts.map +1 -0
  44. package/dist/errors.js +32 -0
  45. package/dist/errors.js.map +1 -0
  46. package/dist/homebridge-ui/endpoints/config.d.ts +3 -0
  47. package/dist/homebridge-ui/endpoints/config.d.ts.map +1 -0
  48. package/dist/homebridge-ui/endpoints/config.js +90 -0
  49. package/dist/homebridge-ui/endpoints/config.js.map +1 -0
  50. package/dist/homebridge-ui/endpoints/devices.d.ts +6 -0
  51. package/dist/homebridge-ui/endpoints/devices.d.ts.map +1 -0
  52. package/dist/homebridge-ui/endpoints/devices.js +81 -0
  53. package/dist/homebridge-ui/endpoints/devices.js.map +1 -0
  54. package/dist/homebridge-ui/endpoints/discovery.d.ts +7 -0
  55. package/dist/homebridge-ui/endpoints/discovery.d.ts.map +1 -0
  56. package/dist/homebridge-ui/endpoints/discovery.js +212 -0
  57. package/dist/homebridge-ui/endpoints/discovery.js.map +1 -0
  58. package/dist/homebridge-ui/public/css/styles.css +472 -0
  59. package/dist/homebridge-ui/public/index.html +210 -244
  60. package/dist/homebridge-ui/public/js/advanced-settings.d.ts +3 -0
  61. package/dist/homebridge-ui/public/js/advanced-settings.d.ts.map +1 -0
  62. package/dist/homebridge-ui/public/js/advanced-settings.js +95 -0
  63. package/dist/homebridge-ui/public/js/advanced-settings.js.map +1 -0
  64. package/dist/homebridge-ui/public/js/advanced-settings.ts +94 -0
  65. package/dist/homebridge-ui/public/js/api.d.ts +66 -0
  66. package/dist/homebridge-ui/public/js/api.d.ts.map +1 -0
  67. package/dist/homebridge-ui/public/js/api.js +281 -0
  68. package/dist/homebridge-ui/public/js/api.js.map +1 -0
  69. package/dist/homebridge-ui/public/js/api.ts +342 -0
  70. package/dist/homebridge-ui/public/js/app.d.ts +2 -0
  71. package/dist/homebridge-ui/public/js/app.d.ts.map +1 -0
  72. package/dist/homebridge-ui/public/js/app.js +3863 -0
  73. package/dist/homebridge-ui/public/js/app.js.map +7 -0
  74. package/dist/homebridge-ui/public/js/app.ts +22 -0
  75. package/dist/homebridge-ui/public/js/constants.d.ts +2 -0
  76. package/dist/homebridge-ui/public/js/constants.d.ts.map +1 -0
  77. package/dist/homebridge-ui/public/js/constants.js +2 -0
  78. package/dist/homebridge-ui/public/js/constants.js.map +1 -0
  79. package/dist/homebridge-ui/public/js/constants.ts +1 -0
  80. package/dist/homebridge-ui/public/js/credentials.d.ts +3 -0
  81. package/dist/homebridge-ui/public/js/credentials.d.ts.map +1 -0
  82. package/dist/homebridge-ui/public/js/credentials.js +99 -0
  83. package/dist/homebridge-ui/public/js/credentials.js.map +1 -0
  84. package/dist/homebridge-ui/public/js/credentials.ts +105 -0
  85. package/dist/homebridge-ui/public/js/devices-delete.d.ts +3 -0
  86. package/dist/homebridge-ui/public/js/devices-delete.d.ts.map +1 -0
  87. package/dist/homebridge-ui/public/js/devices-delete.js +199 -0
  88. package/dist/homebridge-ui/public/js/devices-delete.js.map +1 -0
  89. package/dist/homebridge-ui/public/js/devices-delete.ts +227 -0
  90. package/dist/homebridge-ui/public/js/devices.d.ts +9 -0
  91. package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -0
  92. package/dist/homebridge-ui/public/js/devices.js +96 -0
  93. package/dist/homebridge-ui/public/js/devices.js.map +1 -0
  94. package/dist/homebridge-ui/public/js/devices.ts +105 -0
  95. package/dist/homebridge-ui/public/js/discovery.d.ts +4 -0
  96. package/dist/homebridge-ui/public/js/discovery.d.ts.map +1 -0
  97. package/dist/homebridge-ui/public/js/discovery.js +1374 -0
  98. package/dist/homebridge-ui/public/js/discovery.js.map +1 -0
  99. package/dist/homebridge-ui/public/js/discovery.ts +1498 -0
  100. package/dist/homebridge-ui/public/js/logger.d.ts +7 -0
  101. package/dist/homebridge-ui/public/js/logger.d.ts.map +1 -0
  102. package/dist/homebridge-ui/public/js/logger.js +17 -0
  103. package/dist/homebridge-ui/public/js/logger.js.map +1 -0
  104. package/dist/homebridge-ui/public/js/logger.ts +17 -0
  105. package/dist/homebridge-ui/public/js/modal.d.ts +5 -0
  106. package/dist/homebridge-ui/public/js/modal.d.ts.map +1 -0
  107. package/dist/homebridge-ui/public/js/modal.js +26 -0
  108. package/dist/homebridge-ui/public/js/modal.js.map +1 -0
  109. package/dist/homebridge-ui/public/js/modal.ts +28 -0
  110. package/dist/homebridge-ui/public/js/modals.d.ts +15 -0
  111. package/dist/homebridge-ui/public/js/modals.d.ts.map +1 -0
  112. package/dist/homebridge-ui/public/js/modals.js +673 -0
  113. package/dist/homebridge-ui/public/js/modals.js.map +1 -0
  114. package/dist/homebridge-ui/public/js/modals.ts +761 -0
  115. package/dist/homebridge-ui/public/js/render.d.ts +71 -0
  116. package/dist/homebridge-ui/public/js/render.d.ts.map +1 -0
  117. package/dist/homebridge-ui/public/js/render.js +953 -0
  118. package/dist/homebridge-ui/public/js/render.js.map +1 -0
  119. package/dist/homebridge-ui/public/js/render.ts +1077 -0
  120. package/dist/homebridge-ui/public/js/toast.d.ts +6 -0
  121. package/dist/homebridge-ui/public/js/toast.d.ts.map +1 -0
  122. package/dist/homebridge-ui/public/js/toast.js +29 -0
  123. package/dist/homebridge-ui/public/js/toast.js.map +1 -0
  124. package/dist/homebridge-ui/public/js/toast.ts +37 -0
  125. package/dist/homebridge-ui/public/js/types.d.ts +23 -0
  126. package/dist/homebridge-ui/public/js/types.d.ts.map +1 -0
  127. package/dist/homebridge-ui/public/js/types.js +2 -0
  128. package/dist/homebridge-ui/public/js/types.js.map +1 -0
  129. package/dist/homebridge-ui/public/js/types.ts +26 -0
  130. package/dist/homebridge-ui/server.js +9 -41
  131. package/dist/homebridge-ui/server.js.map +1 -1
  132. package/dist/homebridge-ui/utils/config-parser.d.ts +35 -0
  133. package/dist/homebridge-ui/utils/config-parser.d.ts.map +1 -0
  134. package/dist/homebridge-ui/utils/config-parser.js +87 -0
  135. package/dist/homebridge-ui/utils/config-parser.js.map +1 -0
  136. package/dist/homebridge-ui/utils/device-migration.d.ts +35 -0
  137. package/dist/homebridge-ui/utils/device-migration.d.ts.map +1 -0
  138. package/dist/homebridge-ui/utils/device-migration.js +111 -0
  139. package/dist/homebridge-ui/utils/device-migration.js.map +1 -0
  140. package/dist/homebridge-ui/utils/logger.d.ts +7 -0
  141. package/dist/homebridge-ui/utils/logger.d.ts.map +1 -0
  142. package/dist/homebridge-ui/utils/logger.js +17 -0
  143. package/dist/homebridge-ui/utils/logger.js.map +1 -0
  144. package/dist/index.d.ts +10 -0
  145. package/dist/index.d.ts.map +1 -1
  146. package/dist/index.js +12 -4
  147. package/dist/index.js.map +1 -1
  148. package/dist/settings.d.ts +11 -249
  149. package/dist/settings.d.ts.map +1 -1
  150. package/dist/settings.js +6 -30
  151. package/dist/settings.js.map +1 -1
  152. package/dist/switchbotClient.d.ts +28 -0
  153. package/dist/switchbotClient.d.ts.map +1 -0
  154. package/dist/switchbotClient.js +175 -0
  155. package/dist/switchbotClient.js.map +1 -0
  156. package/dist/utils.d.ts +92 -115
  157. package/dist/utils.d.ts.map +1 -1
  158. package/dist/utils.js +1117 -902
  159. package/dist/utils.js.map +1 -1
  160. package/docs/assets/highlight.css +28 -0
  161. package/docs/assets/icons.js +1 -1
  162. package/docs/assets/icons.svg +1 -1
  163. package/docs/assets/main.js +2 -2
  164. package/docs/assets/style.css +3 -3
  165. package/docs/index.html +148 -13
  166. package/docs/variables/default.html +3 -1
  167. package/eslint.config.js +7 -8
  168. package/nodemon.json +2 -2
  169. package/package.json +36 -32
  170. package/scripts/build-ui.js +37 -0
  171. package/scripts/e2e/README.md +25 -0
  172. package/scripts/e2e/curtain-e2e.sh +70 -0
  173. package/scripts/e2e/fan-e2e.sh +75 -0
  174. package/scripts/e2e/light-advanced-e2e.sh +97 -0
  175. package/scripts/e2e/light-e2e.sh +75 -0
  176. package/scripts/e2e/list-accessories.sh +19 -0
  177. package/scripts/e2e/lock-e2e.sh +65 -0
  178. package/scripts/free-dev-ports.mjs +105 -0
  179. package/scripts/generate-matter-maps.js +77 -0
  180. package/scripts/run-e2e-local.sh +14 -0
  181. package/scripts/sync-device-types.mjs +31 -0
  182. package/src/SwitchBotHAPPlatform.ts +558 -0
  183. package/src/SwitchBotMatterPlatform.ts +538 -0
  184. package/src/device-types.ts +261 -0
  185. package/src/deviceCommandMapper.ts +333 -0
  186. package/src/deviceFactory.ts +201 -0
  187. package/src/devices/deviceBase.ts +141 -0
  188. package/src/devices/genericDevice.ts +1337 -0
  189. package/src/errors.ts +35 -0
  190. package/src/homebridge-ui/endpoints/config.ts +110 -0
  191. package/src/homebridge-ui/endpoints/devices.ts +88 -0
  192. package/src/homebridge-ui/endpoints/discovery.ts +233 -0
  193. package/src/homebridge-ui/public/css/styles.css +472 -0
  194. package/src/homebridge-ui/public/index.html +210 -244
  195. package/src/homebridge-ui/public/js/advanced-settings.ts +94 -0
  196. package/src/homebridge-ui/public/js/api.ts +342 -0
  197. package/src/homebridge-ui/public/js/app.ts +22 -0
  198. package/src/homebridge-ui/public/js/constants.ts +1 -0
  199. package/src/homebridge-ui/public/js/credentials.ts +105 -0
  200. package/src/homebridge-ui/public/js/devices-delete.ts +227 -0
  201. package/src/homebridge-ui/public/js/devices.ts +105 -0
  202. package/src/homebridge-ui/public/js/discovery.ts +1498 -0
  203. package/src/homebridge-ui/public/js/logger.ts +17 -0
  204. package/src/homebridge-ui/public/js/modal.ts +28 -0
  205. package/src/homebridge-ui/public/js/modals.ts +761 -0
  206. package/src/homebridge-ui/public/js/render.ts +1077 -0
  207. package/src/homebridge-ui/public/js/toast.ts +37 -0
  208. package/src/homebridge-ui/public/js/types.ts +26 -0
  209. package/src/homebridge-ui/server.ts +9 -43
  210. package/src/homebridge-ui/utils/config-parser.ts +108 -0
  211. package/src/homebridge-ui/utils/device-migration.ts +144 -0
  212. package/src/homebridge-ui/utils/logger.ts +17 -0
  213. package/src/index.ts +12 -4
  214. package/src/settings.ts +14 -277
  215. package/src/switchbotClient.ts +181 -0
  216. package/src/utils.ts +1106 -900
  217. package/test/client/switchbot-client-debounce.spec.ts +35 -0
  218. package/test/client/switchbot-client-openapi.spec.ts +19 -0
  219. package/test/client/switchbotClient.spec.ts +23 -0
  220. package/test/device/device-mapping.spec.ts +23 -0
  221. package/test/device/deviceBase.spec.ts +26 -0
  222. package/test/device/deviceFactory-edge.spec.ts +15 -0
  223. package/test/device/deviceFactory.spec.ts +33 -0
  224. package/test/device/fan-swing.spec.ts +34 -0
  225. package/test/device/genericDevice-blepoll.spec.ts +47 -0
  226. package/test/device/irdevice.spec.ts +9 -0
  227. package/test/device/lock-users.spec.ts +35 -0
  228. package/test/device/matter-descriptors.spec.ts +22 -0
  229. package/test/device/matter-device-state.spec.ts +37 -0
  230. package/test/e2e/run-e2e.spec.ts +48 -0
  231. package/test/errors/errors.spec.ts +10 -0
  232. package/test/helpers/matter-harness.ts +64 -0
  233. package/test/homebridge-ui/server.spec.ts +9 -0
  234. package/test/platform/accessory-restore.spec.ts +37 -0
  235. package/test/platform/matter-childbridge.spec.ts +34 -0
  236. package/test/platform/matter-integration.spec.ts +33 -0
  237. package/test/platform/platform-edge.spec.ts +73 -0
  238. package/test/platform/platform.integration.spec.ts +34 -0
  239. package/test/utils/utils-extra.spec.ts +10 -0
  240. package/test/utils/utils.spec.ts +53 -0
  241. package/todo/TODO.md +80 -0
  242. package/vitest.config.ts +7 -0
  243. package/coverage/base.css +0 -224
  244. package/coverage/block-navigation.js +0 -87
  245. package/coverage/clover.xml +0 -15847
  246. package/coverage/coverage-final.json +0 -42
  247. package/coverage/docs/assets/dmt/dmt-component-data.js.html +0 -85
  248. package/coverage/docs/assets/dmt/dmt-components.js.html +0 -286
  249. package/coverage/docs/assets/dmt/index.html +0 -131
  250. package/coverage/docs/assets/hierarchy.js.html +0 -85
  251. package/coverage/docs/assets/icons.js.html +0 -136
  252. package/coverage/docs/assets/index.html +0 -146
  253. package/coverage/docs/assets/main.js.html +0 -265
  254. package/coverage/favicon.png +0 -0
  255. package/coverage/index.html +0 -191
  256. package/coverage/prettify.css +0 -1
  257. package/coverage/prettify.js +0 -2
  258. package/coverage/sort-arrow-sprite.png +0 -0
  259. package/coverage/sorter.js +0 -196
  260. package/coverage/src/device/blindtilt.ts.html +0 -3238
  261. package/coverage/src/device/bot.ts.html +0 -2803
  262. package/coverage/src/device/ceilinglight.ts.html +0 -2338
  263. package/coverage/src/device/colorbulb.ts.html +0 -2824
  264. package/coverage/src/device/contact.ts.html +0 -1465
  265. package/coverage/src/device/curtain.ts.html +0 -2869
  266. package/coverage/src/device/device.ts.html +0 -2500
  267. package/coverage/src/device/fan.ts.html +0 -2242
  268. package/coverage/src/device/hub.ts.html +0 -1408
  269. package/coverage/src/device/humidifier.ts.html +0 -2116
  270. package/coverage/src/device/index.html +0 -416
  271. package/coverage/src/device/iosensor.ts.html +0 -1375
  272. package/coverage/src/device/lightstrip.ts.html +0 -2617
  273. package/coverage/src/device/lock.ts.html +0 -1963
  274. package/coverage/src/device/meter.ts.html +0 -1372
  275. package/coverage/src/device/meterplus.ts.html +0 -1384
  276. package/coverage/src/device/meterpro.ts.html +0 -1618
  277. package/coverage/src/device/motion.ts.html +0 -1264
  278. package/coverage/src/device/plug.ts.html +0 -1372
  279. package/coverage/src/device/relayswitch.ts.html +0 -2284
  280. package/coverage/src/device/robotvacuumcleaner.ts.html +0 -1810
  281. package/coverage/src/device/waterdetector.ts.html +0 -1294
  282. package/coverage/src/homebridge-ui/index.html +0 -116
  283. package/coverage/src/homebridge-ui/server.ts.html +0 -229
  284. package/coverage/src/index.html +0 -161
  285. package/coverage/src/index.ts.html +0 -124
  286. package/coverage/src/irdevice/airconditioner.ts.html +0 -1687
  287. package/coverage/src/irdevice/airpurifier.ts.html +0 -844
  288. package/coverage/src/irdevice/camera.ts.html +0 -475
  289. package/coverage/src/irdevice/fan.ts.html +0 -766
  290. package/coverage/src/irdevice/index.html +0 -251
  291. package/coverage/src/irdevice/irdevice.ts.html +0 -1117
  292. package/coverage/src/irdevice/light.ts.html +0 -826
  293. package/coverage/src/irdevice/other.ts.html +0 -2458
  294. package/coverage/src/irdevice/tv.ts.html +0 -1222
  295. package/coverage/src/irdevice/vacuumcleaner.ts.html +0 -466
  296. package/coverage/src/irdevice/waterheater.ts.html +0 -469
  297. package/coverage/src/platform.ts.html +0 -8776
  298. package/coverage/src/settings.ts.html +0 -934
  299. package/coverage/src/utils.ts.html +0 -2092
  300. package/dist/baseMatterAccessory.test.d.ts +0 -2
  301. package/dist/baseMatterAccessory.test.d.ts.map +0 -1
  302. package/dist/baseMatterAccessory.test.js +0 -71
  303. package/dist/baseMatterAccessory.test.js.map +0 -1
  304. package/dist/devices-hap/airpurifier.d.ts +0 -54
  305. package/dist/devices-hap/airpurifier.d.ts.map +0 -1
  306. package/dist/devices-hap/airpurifier.js +0 -527
  307. package/dist/devices-hap/airpurifier.js.map +0 -1
  308. package/dist/devices-hap/blindtilt.d.ts +0 -90
  309. package/dist/devices-hap/blindtilt.d.ts.map +0 -1
  310. package/dist/devices-hap/blindtilt.js +0 -974
  311. package/dist/devices-hap/blindtilt.js.map +0 -1
  312. package/dist/devices-hap/bot.d.ts +0 -102
  313. package/dist/devices-hap/bot.d.ts.map +0 -1
  314. package/dist/devices-hap/bot.js +0 -811
  315. package/dist/devices-hap/bot.js.map +0 -1
  316. package/dist/devices-hap/ceilinglight.d.ts +0 -85
  317. package/dist/devices-hap/ceilinglight.d.ts.map +0 -1
  318. package/dist/devices-hap/ceilinglight.js +0 -701
  319. package/dist/devices-hap/ceilinglight.js.map +0 -1
  320. package/dist/devices-hap/colorbulb.d.ts +0 -88
  321. package/dist/devices-hap/colorbulb.d.ts.map +0 -1
  322. package/dist/devices-hap/colorbulb.js +0 -881
  323. package/dist/devices-hap/colorbulb.js.map +0 -1
  324. package/dist/devices-hap/contact.d.ts +0 -44
  325. package/dist/devices-hap/contact.d.ts.map +0 -1
  326. package/dist/devices-hap/contact.js +0 -409
  327. package/dist/devices-hap/contact.js.map +0 -1
  328. package/dist/devices-hap/curtain.d.ts +0 -73
  329. package/dist/devices-hap/curtain.d.ts.map +0 -1
  330. package/dist/devices-hap/curtain.js +0 -869
  331. package/dist/devices-hap/curtain.js.map +0 -1
  332. package/dist/devices-hap/device.d.ts +0 -98
  333. package/dist/devices-hap/device.d.ts.map +0 -1
  334. package/dist/devices-hap/device.js +0 -831
  335. package/dist/devices-hap/device.js.map +0 -1
  336. package/dist/devices-hap/fan.d.ts +0 -69
  337. package/dist/devices-hap/fan.d.ts.map +0 -1
  338. package/dist/devices-hap/fan.js +0 -649
  339. package/dist/devices-hap/fan.js.map +0 -1
  340. package/dist/devices-hap/hub.d.ts +0 -37
  341. package/dist/devices-hap/hub.d.ts.map +0 -1
  342. package/dist/devices-hap/hub.js +0 -392
  343. package/dist/devices-hap/hub.js.map +0 -1
  344. package/dist/devices-hap/humidifier.d.ts +0 -68
  345. package/dist/devices-hap/humidifier.d.ts.map +0 -1
  346. package/dist/devices-hap/humidifier.js +0 -628
  347. package/dist/devices-hap/humidifier.js.map +0 -1
  348. package/dist/devices-hap/iosensor.d.ts +0 -42
  349. package/dist/devices-hap/iosensor.d.ts.map +0 -1
  350. package/dist/devices-hap/iosensor.js +0 -382
  351. package/dist/devices-hap/iosensor.js.map +0 -1
  352. package/dist/devices-hap/lightstrip.d.ts +0 -79
  353. package/dist/devices-hap/lightstrip.d.ts.map +0 -1
  354. package/dist/devices-hap/lightstrip.js +0 -797
  355. package/dist/devices-hap/lightstrip.js.map +0 -1
  356. package/dist/devices-hap/lock.d.ts +0 -53
  357. package/dist/devices-hap/lock.d.ts.map +0 -1
  358. package/dist/devices-hap/lock.js +0 -561
  359. package/dist/devices-hap/lock.js.map +0 -1
  360. package/dist/devices-hap/meter.d.ts +0 -37
  361. package/dist/devices-hap/meter.d.ts.map +0 -1
  362. package/dist/devices-hap/meter.js +0 -379
  363. package/dist/devices-hap/meter.js.map +0 -1
  364. package/dist/devices-hap/meterplus.d.ts +0 -42
  365. package/dist/devices-hap/meterplus.d.ts.map +0 -1
  366. package/dist/devices-hap/meterplus.js +0 -384
  367. package/dist/devices-hap/meterplus.js.map +0 -1
  368. package/dist/devices-hap/meterpro.d.ts +0 -43
  369. package/dist/devices-hap/meterpro.d.ts.map +0 -1
  370. package/dist/devices-hap/meterpro.js +0 -468
  371. package/dist/devices-hap/meterpro.js.map +0 -1
  372. package/dist/devices-hap/motion.d.ts +0 -42
  373. package/dist/devices-hap/motion.d.ts.map +0 -1
  374. package/dist/devices-hap/motion.js +0 -345
  375. package/dist/devices-hap/motion.js.map +0 -1
  376. package/dist/devices-hap/plug.d.ts +0 -49
  377. package/dist/devices-hap/plug.d.ts.map +0 -1
  378. package/dist/devices-hap/plug.js +0 -395
  379. package/dist/devices-hap/plug.js.map +0 -1
  380. package/dist/devices-hap/relayswitch.d.ts +0 -96
  381. package/dist/devices-hap/relayswitch.d.ts.map +0 -1
  382. package/dist/devices-hap/relayswitch.js +0 -642
  383. package/dist/devices-hap/relayswitch.js.map +0 -1
  384. package/dist/devices-hap/robotvacuumcleaner.d.ts +0 -54
  385. package/dist/devices-hap/robotvacuumcleaner.d.ts.map +0 -1
  386. package/dist/devices-hap/robotvacuumcleaner.js +0 -523
  387. package/dist/devices-hap/robotvacuumcleaner.js.map +0 -1
  388. package/dist/devices-hap/waterdetector.d.ts +0 -41
  389. package/dist/devices-hap/waterdetector.d.ts.map +0 -1
  390. package/dist/devices-hap/waterdetector.js +0 -356
  391. package/dist/devices-hap/waterdetector.js.map +0 -1
  392. package/dist/devices-matter/BaseMatterAccessory.d.ts +0 -78
  393. package/dist/devices-matter/BaseMatterAccessory.d.ts.map +0 -1
  394. package/dist/devices-matter/BaseMatterAccessory.js +0 -244
  395. package/dist/devices-matter/BaseMatterAccessory.js.map +0 -1
  396. package/dist/devices-matter/ColorLightAccessory.d.ts +0 -20
  397. package/dist/devices-matter/ColorLightAccessory.d.ts.map +0 -1
  398. package/dist/devices-matter/ColorLightAccessory.js +0 -95
  399. package/dist/devices-matter/ColorLightAccessory.js.map +0 -1
  400. package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts +0 -18
  401. package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts.map +0 -1
  402. package/dist/devices-matter/ColorTemperatureLightAccessory.js +0 -76
  403. package/dist/devices-matter/ColorTemperatureLightAccessory.js.map +0 -1
  404. package/dist/devices-matter/ContactSensorAccessory.d.ts +0 -12
  405. package/dist/devices-matter/ContactSensorAccessory.d.ts.map +0 -1
  406. package/dist/devices-matter/ContactSensorAccessory.js +0 -34
  407. package/dist/devices-matter/ContactSensorAccessory.js.map +0 -1
  408. package/dist/devices-matter/DimmableLightAccessory.d.ts +0 -58
  409. package/dist/devices-matter/DimmableLightAccessory.d.ts.map +0 -1
  410. package/dist/devices-matter/DimmableLightAccessory.js +0 -167
  411. package/dist/devices-matter/DimmableLightAccessory.js.map +0 -1
  412. package/dist/devices-matter/DoorLockAccessory.d.ts +0 -14
  413. package/dist/devices-matter/DoorLockAccessory.d.ts.map +0 -1
  414. package/dist/devices-matter/DoorLockAccessory.js +0 -50
  415. package/dist/devices-matter/DoorLockAccessory.js.map +0 -1
  416. package/dist/devices-matter/ExtendedColorLightAccessory.d.ts +0 -21
  417. package/dist/devices-matter/ExtendedColorLightAccessory.d.ts.map +0 -1
  418. package/dist/devices-matter/ExtendedColorLightAccessory.js +0 -106
  419. package/dist/devices-matter/ExtendedColorLightAccessory.js.map +0 -1
  420. package/dist/devices-matter/FanAccessory.d.ts +0 -16
  421. package/dist/devices-matter/FanAccessory.d.ts.map +0 -1
  422. package/dist/devices-matter/FanAccessory.js +0 -81
  423. package/dist/devices-matter/FanAccessory.js.map +0 -1
  424. package/dist/devices-matter/HumiditySensorAccessory.d.ts +0 -12
  425. package/dist/devices-matter/HumiditySensorAccessory.d.ts.map +0 -1
  426. package/dist/devices-matter/HumiditySensorAccessory.js +0 -34
  427. package/dist/devices-matter/HumiditySensorAccessory.js.map +0 -1
  428. package/dist/devices-matter/LeakSensorAccessory.d.ts +0 -12
  429. package/dist/devices-matter/LeakSensorAccessory.d.ts.map +0 -1
  430. package/dist/devices-matter/LeakSensorAccessory.js +0 -33
  431. package/dist/devices-matter/LeakSensorAccessory.js.map +0 -1
  432. package/dist/devices-matter/LightSensorAccessory.d.ts +0 -12
  433. package/dist/devices-matter/LightSensorAccessory.d.ts.map +0 -1
  434. package/dist/devices-matter/LightSensorAccessory.js +0 -34
  435. package/dist/devices-matter/LightSensorAccessory.js.map +0 -1
  436. package/dist/devices-matter/OccupancySensorAccessory.d.ts +0 -12
  437. package/dist/devices-matter/OccupancySensorAccessory.d.ts.map +0 -1
  438. package/dist/devices-matter/OccupancySensorAccessory.js +0 -39
  439. package/dist/devices-matter/OccupancySensorAccessory.js.map +0 -1
  440. package/dist/devices-matter/OnOffLightAccessory.d.ts +0 -38
  441. package/dist/devices-matter/OnOffLightAccessory.d.ts.map +0 -1
  442. package/dist/devices-matter/OnOffLightAccessory.js +0 -110
  443. package/dist/devices-matter/OnOffLightAccessory.js.map +0 -1
  444. package/dist/devices-matter/OnOffOutletAccessory.d.ts +0 -14
  445. package/dist/devices-matter/OnOffOutletAccessory.d.ts.map +0 -1
  446. package/dist/devices-matter/OnOffOutletAccessory.js +0 -43
  447. package/dist/devices-matter/OnOffOutletAccessory.js.map +0 -1
  448. package/dist/devices-matter/OnOffSwitchAccessory.d.ts +0 -14
  449. package/dist/devices-matter/OnOffSwitchAccessory.d.ts.map +0 -1
  450. package/dist/devices-matter/OnOffSwitchAccessory.js +0 -42
  451. package/dist/devices-matter/OnOffSwitchAccessory.js.map +0 -1
  452. package/dist/devices-matter/RoboticVacuumAccessory.d.ts +0 -68
  453. package/dist/devices-matter/RoboticVacuumAccessory.d.ts.map +0 -1
  454. package/dist/devices-matter/RoboticVacuumAccessory.js +0 -334
  455. package/dist/devices-matter/RoboticVacuumAccessory.js.map +0 -1
  456. package/dist/devices-matter/SmokeCOAlarmAccessory.d.ts +0 -11
  457. package/dist/devices-matter/SmokeCOAlarmAccessory.d.ts.map +0 -1
  458. package/dist/devices-matter/SmokeCOAlarmAccessory.js +0 -49
  459. package/dist/devices-matter/SmokeCOAlarmAccessory.js.map +0 -1
  460. package/dist/devices-matter/TemperatureSensorAccessory.d.ts +0 -12
  461. package/dist/devices-matter/TemperatureSensorAccessory.d.ts.map +0 -1
  462. package/dist/devices-matter/TemperatureSensorAccessory.js +0 -36
  463. package/dist/devices-matter/TemperatureSensorAccessory.js.map +0 -1
  464. package/dist/devices-matter/ThermostatAccessory.d.ts +0 -19
  465. package/dist/devices-matter/ThermostatAccessory.d.ts.map +0 -1
  466. package/dist/devices-matter/ThermostatAccessory.js +0 -95
  467. package/dist/devices-matter/ThermostatAccessory.js.map +0 -1
  468. package/dist/devices-matter/VenetianBlindAccessory.d.ts +0 -19
  469. package/dist/devices-matter/VenetianBlindAccessory.d.ts.map +0 -1
  470. package/dist/devices-matter/VenetianBlindAccessory.js +0 -99
  471. package/dist/devices-matter/VenetianBlindAccessory.js.map +0 -1
  472. package/dist/devices-matter/WindowBlindAccessory.d.ts +0 -17
  473. package/dist/devices-matter/WindowBlindAccessory.d.ts.map +0 -1
  474. package/dist/devices-matter/WindowBlindAccessory.js +0 -80
  475. package/dist/devices-matter/WindowBlindAccessory.js.map +0 -1
  476. package/dist/devices-matter/custom/PowerStripAccessory.d.ts +0 -97
  477. package/dist/devices-matter/custom/PowerStripAccessory.d.ts.map +0 -1
  478. package/dist/devices-matter/custom/PowerStripAccessory.js +0 -265
  479. package/dist/devices-matter/custom/PowerStripAccessory.js.map +0 -1
  480. package/dist/devices-matter/custom/index.d.ts +0 -8
  481. package/dist/devices-matter/custom/index.d.ts.map +0 -1
  482. package/dist/devices-matter/custom/index.js +0 -8
  483. package/dist/devices-matter/custom/index.js.map +0 -1
  484. package/dist/devices-matter/index.d.ts +0 -29
  485. package/dist/devices-matter/index.d.ts.map +0 -1
  486. package/dist/devices-matter/index.js +0 -28
  487. package/dist/devices-matter/index.js.map +0 -1
  488. package/dist/index.test.d.ts +0 -2
  489. package/dist/index.test.d.ts.map +0 -1
  490. package/dist/index.test.js +0 -19
  491. package/dist/index.test.js.map +0 -1
  492. package/dist/irdevice/airconditioner.d.ts +0 -61
  493. package/dist/irdevice/airconditioner.d.ts.map +0 -1
  494. package/dist/irdevice/airconditioner.js +0 -472
  495. package/dist/irdevice/airconditioner.js.map +0 -1
  496. package/dist/irdevice/airpurifier.d.ts +0 -50
  497. package/dist/irdevice/airpurifier.d.ts.map +0 -1
  498. package/dist/irdevice/airpurifier.js +0 -213
  499. package/dist/irdevice/airpurifier.js.map +0 -1
  500. package/dist/irdevice/camera.d.ts +0 -32
  501. package/dist/irdevice/camera.d.ts.map +0 -1
  502. package/dist/irdevice/camera.js +0 -107
  503. package/dist/irdevice/camera.js.map +0 -1
  504. package/dist/irdevice/fan.d.ts +0 -36
  505. package/dist/irdevice/fan.d.ts.map +0 -1
  506. package/dist/irdevice/fan.js +0 -200
  507. package/dist/irdevice/fan.js.map +0 -1
  508. package/dist/irdevice/irdevice.d.ts +0 -68
  509. package/dist/irdevice/irdevice.d.ts.map +0 -1
  510. package/dist/irdevice/irdevice.js +0 -398
  511. package/dist/irdevice/irdevice.js.map +0 -1
  512. package/dist/irdevice/light.d.ts +0 -36
  513. package/dist/irdevice/light.d.ts.map +0 -1
  514. package/dist/irdevice/light.js +0 -206
  515. package/dist/irdevice/light.js.map +0 -1
  516. package/dist/irdevice/other.d.ts +0 -57
  517. package/dist/irdevice/other.d.ts.map +0 -1
  518. package/dist/irdevice/other.js +0 -778
  519. package/dist/irdevice/other.js.map +0 -1
  520. package/dist/irdevice/tv.d.ts +0 -45
  521. package/dist/irdevice/tv.d.ts.map +0 -1
  522. package/dist/irdevice/tv.js +0 -327
  523. package/dist/irdevice/tv.js.map +0 -1
  524. package/dist/irdevice/vacuumcleaner.d.ts +0 -28
  525. package/dist/irdevice/vacuumcleaner.d.ts.map +0 -1
  526. package/dist/irdevice/vacuumcleaner.js +0 -104
  527. package/dist/irdevice/vacuumcleaner.js.map +0 -1
  528. package/dist/irdevice/waterheater.d.ts +0 -30
  529. package/dist/irdevice/waterheater.d.ts.map +0 -1
  530. package/dist/irdevice/waterheater.js +0 -105
  531. package/dist/irdevice/waterheater.js.map +0 -1
  532. package/dist/platform-hap.d.ts +0 -145
  533. package/dist/platform-hap.d.ts.map +0 -1
  534. package/dist/platform-hap.js +0 -2823
  535. package/dist/platform-hap.js.map +0 -1
  536. package/dist/platform-matter.d.ts +0 -131
  537. package/dist/platform-matter.d.ts.map +0 -1
  538. package/dist/platform-matter.js +0 -1002
  539. package/dist/platform-matter.js.map +0 -1
  540. package/dist/utils.test.d.ts +0 -2
  541. package/dist/utils.test.d.ts.map +0 -1
  542. package/dist/utils.test.js +0 -95
  543. package/dist/utils.test.js.map +0 -1
  544. package/dist/verifyconfig.test.d.ts +0 -2
  545. package/dist/verifyconfig.test.d.ts.map +0 -1
  546. package/dist/verifyconfig.test.js +0 -167
  547. package/dist/verifyconfig.test.js.map +0 -1
  548. package/src/baseMatterAccessory.test.ts +0 -88
  549. package/src/custom.d.ts +0 -7
  550. package/src/devices-hap/airpurifier.ts +0 -563
  551. package/src/devices-hap/blindtilt.ts +0 -1049
  552. package/src/devices-hap/bot.ts +0 -900
  553. package/src/devices-hap/ceilinglight.ts +0 -742
  554. package/src/devices-hap/colorbulb.ts +0 -904
  555. package/src/devices-hap/contact.ts +0 -457
  556. package/src/devices-hap/curtain.ts +0 -944
  557. package/src/devices-hap/device.ts +0 -884
  558. package/src/devices-hap/fan.ts +0 -711
  559. package/src/devices-hap/hub.ts +0 -439
  560. package/src/devices-hap/humidifier.ts +0 -669
  561. package/src/devices-hap/iosensor.ts +0 -427
  562. package/src/devices-hap/lightstrip.ts +0 -836
  563. package/src/devices-hap/lock.ts +0 -620
  564. package/src/devices-hap/meter.ts +0 -426
  565. package/src/devices-hap/meterplus.ts +0 -430
  566. package/src/devices-hap/meterpro.ts +0 -522
  567. package/src/devices-hap/motion.ts +0 -390
  568. package/src/devices-hap/plug.ts +0 -423
  569. package/src/devices-hap/relayswitch.ts +0 -727
  570. package/src/devices-hap/robotvacuumcleaner.ts +0 -568
  571. package/src/devices-hap/waterdetector.ts +0 -400
  572. package/src/devices-matter/BaseMatterAccessory.ts +0 -273
  573. package/src/devices-matter/ColorLightAccessory.ts +0 -110
  574. package/src/devices-matter/ColorTemperatureLightAccessory.ts +0 -90
  575. package/src/devices-matter/ContactSensorAccessory.ts +0 -41
  576. package/src/devices-matter/DimmableLightAccessory.ts +0 -192
  577. package/src/devices-matter/DoorLockAccessory.ts +0 -60
  578. package/src/devices-matter/ExtendedColorLightAccessory.ts +0 -122
  579. package/src/devices-matter/FanAccessory.ts +0 -95
  580. package/src/devices-matter/HumiditySensorAccessory.ts +0 -41
  581. package/src/devices-matter/LeakSensorAccessory.ts +0 -40
  582. package/src/devices-matter/LightSensorAccessory.ts +0 -41
  583. package/src/devices-matter/OccupancySensorAccessory.ts +0 -48
  584. package/src/devices-matter/OnOffLightAccessory.ts +0 -125
  585. package/src/devices-matter/OnOffOutletAccessory.ts +0 -51
  586. package/src/devices-matter/OnOffSwitchAccessory.ts +0 -51
  587. package/src/devices-matter/RoboticVacuumAccessory.ts +0 -407
  588. package/src/devices-matter/SmokeCOAlarmAccessory.ts +0 -59
  589. package/src/devices-matter/TemperatureSensorAccessory.ts +0 -43
  590. package/src/devices-matter/ThermostatAccessory.ts +0 -110
  591. package/src/devices-matter/VenetianBlindAccessory.ts +0 -115
  592. package/src/devices-matter/WindowBlindAccessory.ts +0 -92
  593. package/src/devices-matter/custom/PowerStripAccessory.ts +0 -309
  594. package/src/devices-matter/custom/index.ts +0 -8
  595. package/src/devices-matter/index.ts +0 -29
  596. package/src/index.test.ts +0 -24
  597. package/src/irdevice/airconditioner.ts +0 -533
  598. package/src/irdevice/airpurifier.ts +0 -252
  599. package/src/irdevice/camera.ts +0 -129
  600. package/src/irdevice/fan.ts +0 -226
  601. package/src/irdevice/irdevice.ts +0 -435
  602. package/src/irdevice/light.ts +0 -246
  603. package/src/irdevice/other.ts +0 -790
  604. package/src/irdevice/tv.ts +0 -378
  605. package/src/irdevice/vacuumcleaner.ts +0 -126
  606. package/src/irdevice/waterheater.ts +0 -127
  607. package/src/platform-hap.ts +0 -2952
  608. package/src/platform-matter.ts +0 -1129
  609. package/src/utils.test.ts +0 -96
  610. package/src/verifyconfig.test.ts +0 -198
@@ -0,0 +1,538 @@
1
+ import type { SwitchBotPluginConfig } from './settings.js'
2
+ import type { API, Logger, PlatformConfig } from 'homebridge'
3
+
4
+ import { createDevice } from './deviceFactory.js'
5
+ import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'
6
+ import { SwitchBotClient } from './switchbotClient.js'
7
+ import { createMatterHandlers, DEVICE_MATTER_CLUSTERS, DEVICE_MATTER_SUPPORTED, normalizeTypeForMatter, resolveMatterDeviceType } from './utils.js'
8
+
9
+ /**
10
+ * Homebridge platform class for SwitchBot Matter integration.
11
+ * Handles device discovery, registration, polling, and accessory lifecycle for Matter-enabled SwitchBot devices.
12
+ *
13
+ * @class SwitchBotMatterPlatform
14
+ * @param {Logger} log - Homebridge logger instance
15
+ * @param {PlatformConfig} config - Platform configuration object
16
+ * @param {API} [api] - Optional Homebridge API instance
17
+ * @property {API | undefined} api - Homebridge API instance
18
+ * @property {Logger} log - Homebridge logger instance
19
+ * @property {SwitchBotPluginConfig} config - Parsed plugin config
20
+ * @property {any[]} devices - All created device instances
21
+ * @property {Map<string, any>} accessories - Map of accessory UUID to accessory object
22
+ * @property {string} lastConfigHash - Hash of last loaded config for change detection
23
+ * @property {NodeJS.Timeout | null} configReloadInterval - Interval for periodic config reload
24
+ * @property {Map<string, NodeJS.Timeout>} openApiPollTimers - Timers for per-device OpenAPI polling
25
+ * @property {NodeJS.Timeout | null} openApiBatchTimer - Timer for batched OpenAPI polling
26
+ * @property {number} openApiRequestsToday - Count of OpenAPI requests made today
27
+ * @property {number} openApiLastReset - Timestamp (ms) of last OpenAPI daily counter reset
28
+ */
29
+ export class SwitchBotMatterPlatform {
30
+ /** Homebridge API instance */
31
+ api: API | undefined
32
+ /** Homebridge logger instance */
33
+ log: Logger
34
+ /** Parsed plugin config */
35
+ config: SwitchBotPluginConfig
36
+ /** All created device instances */
37
+ devices: any[] = []
38
+ /** Map of accessory UUID to accessory object */
39
+ accessories: Map<string, any>
40
+ /** Hash of last loaded config for change detection */
41
+ private lastConfigHash: string = ''
42
+ /** Interval for periodic config reload */
43
+ private configReloadInterval: NodeJS.Timeout | null = null
44
+ /** Timers for per-device OpenAPI polling */
45
+ private openApiPollTimers: Map<string, NodeJS.Timeout> = new Map()
46
+ /** Timer for batched OpenAPI polling */
47
+ private openApiBatchTimer: NodeJS.Timeout | null = null
48
+ /** Count of OpenAPI requests made today */
49
+ private openApiRequestsToday = 0
50
+ /** Timestamp (ms) of last OpenAPI daily counter reset */
51
+ private openApiLastReset = 0
52
+
53
+ /**
54
+ * Construct the SwitchBot Matter platform.
55
+ * @param log Homebridge logger
56
+ * @param config Platform config
57
+ * @param api Homebridge API instance
58
+ */
59
+ constructor(log: Logger, config: PlatformConfig, api?: API) {
60
+ this.log = log
61
+ // Ensure both log and logger are set for downstream device constructors
62
+ this.config = { ...(config as any), log, logger: log }
63
+ this.api = api
64
+ this.accessories = new Map()
65
+ this.log.info('SwitchBot Matter platform initialized')
66
+
67
+ // Create/shared SwitchBot client and attach to config so child devices reuse it.
68
+ try {
69
+ const client = new SwitchBotClient(this.config)
70
+ void client.init();
71
+ (this.config as any)._client = client
72
+ } catch (e) {
73
+ this.log.debug('Failed to create shared SwitchBot client', e)
74
+ }
75
+
76
+ // Wait for Homebridge to finish launching to create/register accessories
77
+ if (this.api && typeof (this.api as any).on === 'function') {
78
+ (this.api as any).on('didFinishLaunching', async () => {
79
+ await this.loadDevices()
80
+ this._setupOpenApiPolling()
81
+ // Start periodic config reload to pick up UI changes
82
+ this.configReloadInterval = setInterval(() => {
83
+ void this.checkAndReloadDevices()
84
+ }, 10000) // Check every 10 seconds
85
+ // Listen for Homebridge shutdown to clear interval
86
+ if (typeof (this.api as any).on === 'function') {
87
+ (this.api as any).on('shutdown', () => {
88
+ this.shutdown()
89
+ })
90
+ }
91
+ })
92
+ } else {
93
+ void this.loadDevices()
94
+ this._setupOpenApiPolling()
95
+ // Start periodic config reload to pick up UI changes
96
+ this.configReloadInterval = setInterval(() => {
97
+ void this.checkAndReloadDevices()
98
+ }, 10000) // Check every 10 seconds
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Discover and create all device instances from config.
104
+ * Populates this.devices and logs registration status for each device.
105
+ *
106
+ * @returns {Promise<void>} Resolves when all devices are loaded and registered
107
+ */
108
+ async loadDevices(): Promise<void> {
109
+ const newHash = this.getConfigHash()
110
+ if (newHash === this.lastConfigHash) {
111
+ this.log.debug('Config unchanged, skipping device reload')
112
+ return
113
+ }
114
+
115
+ const devices = (this.config as any)?.devices ?? []
116
+ const createdDevices: { created: any, d: any, type: string, useMatter: boolean, matterAvailable: boolean }[] = []
117
+ for (const raw of devices) {
118
+ // Normalize config keys from UI schema to internal shape (for cross-platform consistency)
119
+ const d: any = {
120
+ id: raw.deviceId ?? raw.id,
121
+ name: raw.configDeviceName ?? raw.name,
122
+ type: raw.configDeviceType ?? raw.type ?? raw.deviceType ?? 'unknown',
123
+ encryptionKey: raw.encryptionKey,
124
+ keyId: raw.keyId,
125
+ _raw: raw,
126
+ }
127
+ const type: string = normalizeTypeForMatter(d.type)
128
+ const deviceOpts: any = { id: d.id, type, name: d.name, encryptionKey: d.encryptionKey, keyId: d.keyId, log: this.log }
129
+ this.log.debug(`[Matter/Debug] Device options for ${d.name ?? d.id}:`, JSON.stringify(deviceOpts, null, 2))
130
+ const matterSupported = !!DEVICE_MATTER_SUPPORTED[(type || '').toLowerCase()]
131
+ const matterAvailable = !!(this.api?.isMatterAvailable?.() && this.api?.isMatterEnabled?.())
132
+ const matterEnabled = matterAvailable || !!this.config.enableMatter
133
+ const useMatter = matterEnabled && matterSupported
134
+ try {
135
+ const created = await createDevice(deviceOpts, this.config, useMatter)
136
+ this.devices.push(created)
137
+ createdDevices.push({ created, d, type, useMatter, matterAvailable })
138
+ if (useMatter) {
139
+ this.log.info(`Prepared Matter accessory for ${d.id} (${type})${matterAvailable ? ' (auto-detected)' : ' (manually enabled)'}`)
140
+ } else {
141
+ if (!matterEnabled) {
142
+ this.log.info(`Skipping Matter for ${d.id} (${type}) - Matter not available on this bridge`)
143
+ } else if (!matterSupported) {
144
+ this.log.info(`Skipping Matter for ${d.id} (${type}) - device type not supported`)
145
+ } else {
146
+ this.log.info(`Skipping Matter for ${d.id} (${type}) - not supported`)
147
+ }
148
+ }
149
+ } catch (e: any) {
150
+ this.log.error(`Failed to create Matter device ${d.id}:`, e instanceof Error ? e.stack || e.message : e)
151
+ }
152
+ }
153
+ // Register Matter accessories after device creation for symmetry with HAP platform
154
+ await this.registerMatterAccessories(createdDevices)
155
+ this.lastConfigHash = newHash
156
+ }
157
+
158
+ /**
159
+ * Registers all Matter accessories with the Homebridge Matter API.
160
+ *
161
+ * This method is called after all device instances have been created. It handles both new and restored
162
+ * accessories, updating their context, clusters, and other Matter-specific metadata as needed. Accessories
163
+ * are registered with Homebridge using the Matter API, and the internal accessory map is updated accordingly.
164
+ *
165
+ * @param {Array<{created: any, d: any, type: string, useMatter: boolean, matterAvailable: boolean}>} createdDevices - Array of device descriptors:
166
+ * - created: The created device instance
167
+ * - d: The normalized device config object
168
+ * - type: The normalized device type string
169
+ * - useMatter: Whether Matter is enabled for this device
170
+ * - matterAvailable: Whether Matter is available on this bridge
171
+ * @returns {Promise<void>} Resolves when registration is complete
172
+ *
173
+ * Differences from HAP registration:
174
+ * - Uses the Matter API (not HAP API) for accessory registration.
175
+ * - Adds Matter clusters and handlers to each accessory based on the device descriptor.
176
+ * - Only registers accessories where Matter is enabled and supported.
177
+ * - Accessory context and cluster wiring are Matter-specific.
178
+ *
179
+ * If the Homebridge Matter API is not available, registration is skipped and a log message is emitted.
180
+ * Accessories that are not enabled for Matter are ignored.
181
+ */
182
+ async registerMatterAccessories(createdDevices: { created: any, d: any, type: string, useMatter: boolean, matterAvailable: boolean }[]): Promise<void> {
183
+ if (!this.api || !(this.api as any).matter || typeof (this.api as any).matter.registerPlatformAccessories !== 'function') {
184
+ this.log.info('Homebridge Matter API not available; skipping Matter accessory registration')
185
+ return
186
+ }
187
+ const matterApi = (this.api as any).matter
188
+ const accessoriesToRegister: any[] = []
189
+ for (const { created, d, type, useMatter, matterAvailable } of createdDevices) {
190
+ // Only register accessories where Matter is enabled and supported
191
+ if (!useMatter) {
192
+ // Log reason for skipping registration
193
+ if (!matterAvailable) {
194
+ this.log.info(`Skipping Matter registration for ${d.id} (${type}) - Matter API not available on this bridge`)
195
+ } else {
196
+ this.log.info(`Skipping Matter registration for ${d.id} (${type}) - device type not supported for Matter`)
197
+ }
198
+ continue
199
+ }
200
+ try {
201
+ // Prepare accessory descriptor from device
202
+ const createdDesc = await created.createAccessory(this.api)
203
+ const uuid = matterApi.uuid.generate(`${d.id}`)
204
+ // Reuse cached accessory if available by uuid
205
+ let accessory: any = this.accessories.get(uuid)
206
+ // Reuse cached accessory if available by uuid or deviceId
207
+ if (!accessory) {
208
+ for (const [, a] of this.accessories.entries()) {
209
+ try {
210
+ if (a && a.context && a.context.deviceId === d.id) {
211
+ accessory = a
212
+ break
213
+ }
214
+ } catch (e) {
215
+ // ignore
216
+ }
217
+ }
218
+ }
219
+ if (!accessory) {
220
+ // Create new accessory object for Matter
221
+ let clusters = DEVICE_MATTER_CLUSTERS[type.toLowerCase()]
222
+ if (!clusters) {
223
+ clusters = createdDesc.clusters || { onOff: { onOff: false } }
224
+ }
225
+ const deviceType = resolveMatterDeviceType(matterApi, type, createdDesc.deviceType, clusters)
226
+ accessory = {
227
+ UUID: uuid,
228
+ displayName: createdDesc.name || d.name || type,
229
+ deviceType,
230
+ manufacturer: createdDesc.manufacturer || 'SwitchBot',
231
+ model: createdDesc.model || type,
232
+ serialNumber: createdDesc.serialNumber || d.id,
233
+ reachable: createdDesc.reachable !== false,
234
+ firmwareRevision: createdDesc.firmwareRevision || '1.0.0',
235
+ hardwareRevision: createdDesc.hardwareRevision || '',
236
+ clusters,
237
+ handlers: createdDesc.handlers || createMatterHandlers(this.log, d.id, type, (this.config as any)?._client) || undefined,
238
+ context: { deviceId: d.id, type, created: true },
239
+ }
240
+ accessoriesToRegister.push(accessory)
241
+ this.accessories.set(uuid, accessory)
242
+ } else {
243
+ // Ensure context and update properties for restored accessory
244
+ let clusters = DEVICE_MATTER_CLUSTERS[type.toLowerCase()]
245
+ if (!clusters) {
246
+ clusters = accessory.clusters || createdDesc.clusters || { onOff: { onOff: false } }
247
+ }
248
+ const deviceType = resolveMatterDeviceType(matterApi, type, accessory.deviceType || createdDesc.deviceType, clusters)
249
+ accessory.context = accessory.context || {}
250
+ accessory.context.deviceId = accessory.context.deviceId || d.id
251
+ accessory.context.type = accessory.context.type || type
252
+ accessory.deviceType = deviceType
253
+ accessory.manufacturer = accessory.manufacturer || createdDesc.manufacturer || 'SwitchBot'
254
+ accessory.model = accessory.model || createdDesc.model || type
255
+ accessory.serialNumber = accessory.serialNumber || createdDesc.serialNumber || d.id
256
+ accessory.reachable = accessory.reachable !== false
257
+ accessory.firmwareRevision = accessory.firmwareRevision || createdDesc.firmwareRevision || '1.0.0'
258
+ accessory.hardwareRevision = accessory.hardwareRevision || createdDesc.hardwareRevision || ''
259
+ accessory.clusters = clusters
260
+ accessory.handlers = createdDesc.handlers || createMatterHandlers(this.log, d.id, type, (this.config as any)?._client) || undefined
261
+ accessory.displayName = createdDesc.name || d.name || type
262
+ accessory.UUID = accessory.UUID || accessory.uuid || uuid
263
+ accessoriesToRegister.push(accessory)
264
+ this.accessories.set(accessory.UUID || uuid, accessory)
265
+ }
266
+ this.log.info(`Created/updated Matter accessory ${d.id} (${type})`)
267
+ } catch (e) {
268
+ this.log.warn(`Matter accessory creation failed for ${d.id} (${type})`, e)
269
+ }
270
+ }
271
+ if (accessoriesToRegister.length > 0) {
272
+ try {
273
+ await matterApi.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, accessoriesToRegister)
274
+ this.log.info(`Registered ${accessoriesToRegister.length} Matter accessory(ies) with Homebridge`)
275
+ } catch (e) {
276
+ this.log.warn('Failed to register Matter accessories', e)
277
+ }
278
+ } else {
279
+ this.log.info('No Matter accessories to register')
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Returns the timestamp (ms) of the last OpenAPI daily counter reset.
285
+ * @returns {number} Timestamp in ms
286
+ */
287
+ getOpenApiLastReset(): number {
288
+ return this.openApiLastReset
289
+ }
290
+
291
+ /**
292
+ * Logs the last OpenAPI reset time in a human-readable format.
293
+ * @returns {void}
294
+ */
295
+ logOpenApiLastReset(): void {
296
+ if (this.openApiLastReset) {
297
+ const date = new Date(this.openApiLastReset)
298
+ this.log.info(`[OpenAPI] Last daily counter reset: ${date.toLocaleString()}`)
299
+ } else {
300
+ this.log.info('[OpenAPI] Daily counter has not been reset yet.')
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Compute a hash of the current device config for change detection.
306
+ * @returns {string} JSON string hash of device config
307
+ */
308
+ private getConfigHash(): string {
309
+ // Create a simple hash of current device config to detect changes
310
+ const devices = (this.config as any)?.devices ?? []
311
+ return JSON.stringify(devices.map((d: any) => ({
312
+ id: d.deviceId ?? d.id,
313
+ type: d.configDeviceType ?? d.type,
314
+ name: d.configDeviceName ?? d.name,
315
+ })))
316
+ }
317
+
318
+ /**
319
+ * Reload devices if config has changed since last load.
320
+ * Unregisters accessories and removes devices no longer in config.
321
+ * Calls loadDevices to repopulate devices and accessories.
322
+ *
323
+ * @returns {Promise<void>} Resolves when reload is complete
324
+ */
325
+ private async checkAndReloadDevices(): Promise<void> {
326
+ const currentHash = this.getConfigHash()
327
+ if (currentHash !== this.lastConfigHash) {
328
+ this.log.info('[SwitchBot] Detected config changes, reloading devices...')
329
+ // Identify device IDs in new config
330
+ const devicesInConfig = new Set(((this.config as any)?.devices ?? []).map((d: any) => d.deviceId ?? d.id))
331
+ // Find accessories to remove (not in config)
332
+ const accessoriesToRemove: any[] = []
333
+ for (const [uuid, accessory] of this.accessories.entries()) {
334
+ const deviceId = accessory?.context?.deviceId
335
+ if (deviceId && !devicesInConfig.has(deviceId)) {
336
+ accessoriesToRemove.push({ accessory, uuid })
337
+ }
338
+ }
339
+ // Unregister removed accessories from Homebridge (Matter API)
340
+ if (accessoriesToRemove.length > 0 && this.api && (this.api as any).matter && (this.api as any).matter.unregisterPlatformAccessories) {
341
+ try {
342
+ (this.api as any).matter.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, accessoriesToRemove.map(a => a.accessory))
343
+ this.log.info(`Unregistered ${accessoriesToRemove.length} Matter accessory(ies) removed from config`)
344
+ for (const { uuid } of accessoriesToRemove) {
345
+ this.accessories.delete(uuid)
346
+ }
347
+ } catch (e) {
348
+ this.log.warn('Failed to unregister removed Matter accessories', e)
349
+ }
350
+ }
351
+ // Remove devices from this.devices that are no longer in config
352
+ this.devices = this.devices.filter((dev: any) => devicesInConfig.has(dev?.id))
353
+ await this.loadDevices()
354
+ this.lastConfigHash = currentHash
355
+ }
356
+ }
357
+
358
+ /**
359
+ * Cleanup method to clear config reload interval on shutdown.
360
+ * Called by Homebridge on shutdown event.
361
+ * @returns {void}
362
+ */
363
+ shutdown(): void {
364
+ if (this.configReloadInterval) {
365
+ clearInterval(this.configReloadInterval)
366
+ this.configReloadInterval = null
367
+ this.log.info('Cleared config reload interval on shutdown')
368
+ }
369
+ }
370
+
371
+ /**
372
+ * Setup OpenAPI polling for all devices according to config (global, per-device, batch, rate limit).
373
+ * Handles daily request limits, per-device and batch polling, and resets.
374
+ *
375
+ * @returns {void}
376
+ */
377
+ private _setupOpenApiPolling(): void {
378
+ // Clear any existing timers
379
+ for (const t of this.openApiPollTimers.values()) clearInterval(t)
380
+ this.openApiPollTimers.clear()
381
+ if (this.openApiBatchTimer) {
382
+ clearInterval(this.openApiBatchTimer)
383
+ }
384
+ this.openApiBatchTimer = null
385
+
386
+ const cfg = this.config as any
387
+ const devices = cfg.devices ?? []
388
+ const globalRate = Math.max(Number(cfg.openApiRefreshRate) || 300, 30)
389
+ const batchEnabled = cfg.matterBatchEnabled !== false
390
+ const batchRate = Math.max(Number(cfg.matterBatchRefreshRate) || globalRate, 30)
391
+ const batchConcurrency = Math.max(Number(cfg.matterBatchConcurrency) || 5, 1)
392
+ const batchJitter = Math.max(Number(cfg.matterBatchJitter) || 0, 0)
393
+ const dailyLimit = Math.max(Number(cfg.dailyApiLimit) || 10000, 1000)
394
+ const dailyReserve = Math.max(Number(cfg.dailyApiReserveForCommands) || 1000, 0)
395
+ const resetAtLocalMidnight = !!cfg.dailyApiResetLocalMidnight
396
+ const webhookOnlyOnReserve = !!cfg.webhookOnlyOnReserve
397
+
398
+ // Helper to reset daily counter
399
+ const resetCounter = () => {
400
+ this.openApiRequestsToday = 0
401
+ this.openApiLastReset = Date.now()
402
+ this.log.info('[OpenAPI] Daily request counter reset')
403
+ }
404
+ // Schedule reset at midnight
405
+ const scheduleMidnightReset = () => {
406
+ const now = new Date()
407
+ let nextReset: Date
408
+ if (resetAtLocalMidnight) {
409
+ nextReset = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 1)
410
+ } else {
411
+ nextReset = new Date(now)
412
+ nextReset.setUTCHours(24, 0, 1, 0)
413
+ }
414
+ const ms = nextReset.getTime() - now.getTime()
415
+ setTimeout(() => {
416
+ resetCounter()
417
+ scheduleMidnightReset()
418
+ }, ms)
419
+ }
420
+ scheduleMidnightReset()
421
+
422
+ // Helper to check if polling is allowed
423
+ const canPoll = () => {
424
+ if (this.openApiRequestsToday + dailyReserve >= dailyLimit) {
425
+ if (!webhookOnlyOnReserve) {
426
+ this.log.warn('[OpenAPI] Daily request limit reached, pausing background polling')
427
+ }
428
+ return false
429
+ }
430
+ return true
431
+ }
432
+
433
+ // Per-device polling (devices with per-device refreshRate)
434
+ for (const dev of devices) {
435
+ const id = dev.deviceId ?? dev.id
436
+ const enabled = dev.enabled !== false
437
+ if (!id || !enabled) {
438
+ continue
439
+ }
440
+ const perDeviceRate = dev.refreshRate ? Math.max(Number(dev.refreshRate), 30) : null
441
+ if (perDeviceRate) {
442
+ // Individual polling interval for this device
443
+ const timer = setInterval(async () => {
444
+ if (!canPoll()) {
445
+ return
446
+ }
447
+ try {
448
+ const client = (this.config as any)._client
449
+ if (client && typeof client.getDevice === 'function') {
450
+ await client.getDevice(id)
451
+ this.openApiRequestsToday++
452
+ this.log.debug(`[OpenAPI] Polled device ${id} (per-device interval ${perDeviceRate}s) [${this.openApiRequestsToday}/${dailyLimit}]`)
453
+ }
454
+ } catch (e) {
455
+ this.log.debug(`[OpenAPI] Polling failed for device ${id}:`, (e as Error)?.message)
456
+ }
457
+ }, perDeviceRate * 1000)
458
+ this.openApiPollTimers.set(id, timer)
459
+ }
460
+ }
461
+
462
+ // Batched polling for all other devices
463
+ if (batchEnabled) {
464
+ // Devices not already polled individually
465
+ const batchDevices = devices.filter((dev: any) => {
466
+ const id = dev.deviceId ?? dev.id
467
+ const enabled = dev.enabled !== false
468
+ const perDeviceRate = dev.refreshRate ? Math.max(Number(dev.refreshRate), 30) : null
469
+ return id && enabled && !perDeviceRate
470
+ })
471
+ // Optional jitter before first batch
472
+ const startBatch = () => {
473
+ this.openApiBatchTimer = setInterval(async () => {
474
+ if (!canPoll()) {
475
+ return
476
+ }
477
+ const client = (this.config as any)._client
478
+ if (!client || typeof client.getDevice !== 'function') {
479
+ return
480
+ }
481
+ // Limit concurrency
482
+ const chunks: any[][] = []
483
+ for (let i = 0; i < batchDevices.length; i += batchConcurrency) {
484
+ chunks.push(batchDevices.slice(i, i + batchConcurrency))
485
+ }
486
+ for (const chunk of chunks) {
487
+ await Promise.all(chunk.map(async (dev: any) => {
488
+ try {
489
+ await client.getDevice(dev.deviceId ?? dev.id)
490
+ this.openApiRequestsToday++
491
+ this.log.debug(`[OpenAPI] Batched poll device ${dev.deviceId ?? dev.id} [${this.openApiRequestsToday}/${dailyLimit}]`)
492
+ } catch (e) {
493
+ this.log.debug(`[OpenAPI] Batched polling failed for device ${dev.deviceId ?? dev.id}:`, (e as Error)?.message)
494
+ }
495
+ }))
496
+ }
497
+ }, batchRate * 1000)
498
+ }
499
+ if (batchJitter > 0) {
500
+ setTimeout(startBatch, Math.floor(Math.random() * batchJitter * 1000))
501
+ } else {
502
+ startBatch()
503
+ }
504
+ }
505
+ }
506
+
507
+ /**
508
+ * Called by Homebridge to restore cached Matter accessories on startup.
509
+ * @param {any} accessory - The cached accessory object
510
+ * @returns {Promise<void>} Resolves when accessory is restored
511
+ */
512
+ async configureAccessory(accessory: any): Promise<void> {
513
+ try {
514
+ const uuid = accessory.UUID || accessory.UUID
515
+ this.accessories.set(uuid, accessory)
516
+ this.log.info(`Restored cached Matter accessory ${accessory.displayName || uuid}`)
517
+ } catch (e) {
518
+ this.log.warn('configureAccessory failed to restore Matter accessory', e)
519
+ }
520
+ }
521
+
522
+ /**
523
+ * Called by Homebridge to restore cached Matter accessories (alternate signature).
524
+ * @param {any} accessory - The cached accessory object
525
+ * @returns {void}
526
+ */
527
+ configureMatterAccessory(accessory: any): void {
528
+ try {
529
+ const uuid = accessory.uuid || accessory.UUID || accessory.uuid
530
+ this.accessories.set(uuid, accessory)
531
+ this.log.info(`Restored cached Matter accessory ${accessory.displayName || uuid}`)
532
+ } catch (e) {
533
+ this.log.warn('configureMatterAccessory failed to restore Matter accessory', e)
534
+ }
535
+ }
536
+ }
537
+
538
+ export default SwitchBotMatterPlatform