@switchbot/homebridge-switchbot 5.0.0-beta.70 → 5.0.0-beta.72

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 (534) hide show
  1. package/.github/ISSUE_TEMPLATE/e2e-verification.md +36 -0
  2. package/.github/workflows/ci.yml +61 -0
  3. package/.github/workflows/manual-e2e.yml +103 -0
  4. package/CHANGELOG.md +20 -0
  5. package/E2E-VERIFICATION.md +121 -0
  6. package/MIGRATION.md +44 -0
  7. package/README.md +11 -0
  8. package/config.schema.json +99 -1940
  9. package/dist/deviceFactory.d.ts +13 -0
  10. package/dist/deviceFactory.d.ts.map +1 -0
  11. package/dist/deviceFactory.js +81 -0
  12. package/dist/deviceFactory.js.map +1 -0
  13. package/dist/devices/deviceBase.d.ts +50 -0
  14. package/dist/devices/deviceBase.d.ts.map +1 -0
  15. package/dist/devices/deviceBase.js +119 -0
  16. package/dist/devices/deviceBase.js.map +1 -0
  17. package/dist/devices/genericDevice.d.ts +283 -0
  18. package/dist/devices/genericDevice.d.ts.map +1 -0
  19. package/dist/devices/genericDevice.js +1035 -0
  20. package/dist/devices/genericDevice.js.map +1 -0
  21. package/dist/homebridge-ui/public/index.html +72 -440
  22. package/dist/homebridge-ui/server.d.ts +3 -1
  23. package/dist/homebridge-ui/server.d.ts.map +1 -1
  24. package/dist/homebridge-ui/server.js +47 -10
  25. package/dist/homebridge-ui/server.js.map +1 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +1 -3
  28. package/dist/index.js.map +1 -1
  29. package/dist/platform.d.ts +27 -0
  30. package/dist/platform.d.ts.map +1 -0
  31. package/dist/platform.js +404 -0
  32. package/dist/platform.js.map +1 -0
  33. package/dist/settings.d.ts +10 -317
  34. package/dist/settings.d.ts.map +1 -1
  35. package/dist/settings.js +5 -30
  36. package/dist/settings.js.map +1 -1
  37. package/dist/switchbotClient.d.ts +32 -0
  38. package/dist/switchbotClient.d.ts.map +1 -0
  39. package/dist/switchbotClient.js +259 -0
  40. package/dist/switchbotClient.js.map +1 -0
  41. package/dist/utils.d.ts +36 -248
  42. package/dist/utils.d.ts.map +1 -1
  43. package/dist/utils.js +38 -1367
  44. package/dist/utils.js.map +1 -1
  45. package/docs/assets/icons.js +1 -1
  46. package/docs/assets/icons.svg +1 -1
  47. package/docs/assets/style.css +3 -3
  48. package/docs/index.html +50 -15
  49. package/docs/variables/default.html +1 -1
  50. package/package.json +19 -18
  51. package/scripts/e2e/README.md +25 -0
  52. package/scripts/e2e/curtain-e2e.sh +70 -0
  53. package/scripts/e2e/fan-e2e.sh +75 -0
  54. package/scripts/e2e/light-advanced-e2e.sh +97 -0
  55. package/scripts/e2e/light-e2e.sh +75 -0
  56. package/scripts/e2e/list-accessories.sh +19 -0
  57. package/scripts/e2e/lock-e2e.sh +65 -0
  58. package/scripts/generate-matter-maps.js +60 -0
  59. package/scripts/run-e2e-local.sh +14 -0
  60. package/src/deviceFactory.ts +122 -0
  61. package/src/devices/deviceBase.ts +141 -0
  62. package/src/devices/genericDevice.ts +965 -0
  63. package/src/homebridge-ui/public/index.html +72 -440
  64. package/src/homebridge-ui/server.ts +52 -10
  65. package/src/index.ts +1 -3
  66. package/src/platform.ts +395 -0
  67. package/src/settings.ts +12 -352
  68. package/src/switchbotClient.ts +266 -0
  69. package/src/utils.ts +47 -1456
  70. package/test/accessory-restore.spec.ts +73 -0
  71. package/test/device-mapping.spec.ts +37 -0
  72. package/test/deviceFactory.spec.ts +18 -0
  73. package/test/e2e/run-e2e.spec.ts +50 -0
  74. package/test/fan-swing.spec.ts +29 -0
  75. package/test/helpers/matter-harness.ts +53 -0
  76. package/test/lock-users.spec.ts +44 -0
  77. package/test/matter-childbridge.spec.ts +55 -0
  78. package/test/matter-descriptors.spec.ts +97 -0
  79. package/test/matter-device-state.spec.ts +101 -0
  80. package/test/matter-integration.spec.ts +70 -0
  81. package/test/platform.integration.spec.ts +55 -0
  82. package/test/switchbot-client-debounce.spec.ts +131 -0
  83. package/test/switchbot-client-openapi.spec.ts +56 -0
  84. package/test/switchbotClient.spec.ts +10 -0
  85. package/test/utils.spec.ts +20 -0
  86. package/vitest.config.ts +7 -0
  87. package/coverage/base.css +0 -224
  88. package/coverage/block-navigation.js +0 -87
  89. package/coverage/clover.xml +0 -15847
  90. package/coverage/coverage-final.json +0 -42
  91. package/coverage/docs/assets/dmt/dmt-component-data.js.html +0 -85
  92. package/coverage/docs/assets/dmt/dmt-components.js.html +0 -286
  93. package/coverage/docs/assets/dmt/index.html +0 -131
  94. package/coverage/docs/assets/hierarchy.js.html +0 -85
  95. package/coverage/docs/assets/icons.js.html +0 -136
  96. package/coverage/docs/assets/index.html +0 -146
  97. package/coverage/docs/assets/main.js.html +0 -265
  98. package/coverage/favicon.png +0 -0
  99. package/coverage/index.html +0 -191
  100. package/coverage/prettify.css +0 -1
  101. package/coverage/prettify.js +0 -2
  102. package/coverage/sort-arrow-sprite.png +0 -0
  103. package/coverage/sorter.js +0 -196
  104. package/coverage/src/device/blindtilt.ts.html +0 -3238
  105. package/coverage/src/device/bot.ts.html +0 -2803
  106. package/coverage/src/device/ceilinglight.ts.html +0 -2338
  107. package/coverage/src/device/colorbulb.ts.html +0 -2824
  108. package/coverage/src/device/contact.ts.html +0 -1465
  109. package/coverage/src/device/curtain.ts.html +0 -2869
  110. package/coverage/src/device/device.ts.html +0 -2500
  111. package/coverage/src/device/fan.ts.html +0 -2242
  112. package/coverage/src/device/hub.ts.html +0 -1408
  113. package/coverage/src/device/humidifier.ts.html +0 -2116
  114. package/coverage/src/device/index.html +0 -416
  115. package/coverage/src/device/iosensor.ts.html +0 -1375
  116. package/coverage/src/device/lightstrip.ts.html +0 -2617
  117. package/coverage/src/device/lock.ts.html +0 -1963
  118. package/coverage/src/device/meter.ts.html +0 -1372
  119. package/coverage/src/device/meterplus.ts.html +0 -1384
  120. package/coverage/src/device/meterpro.ts.html +0 -1618
  121. package/coverage/src/device/motion.ts.html +0 -1264
  122. package/coverage/src/device/plug.ts.html +0 -1372
  123. package/coverage/src/device/relayswitch.ts.html +0 -2284
  124. package/coverage/src/device/robotvacuumcleaner.ts.html +0 -1810
  125. package/coverage/src/device/waterdetector.ts.html +0 -1294
  126. package/coverage/src/homebridge-ui/index.html +0 -116
  127. package/coverage/src/homebridge-ui/server.ts.html +0 -229
  128. package/coverage/src/index.html +0 -161
  129. package/coverage/src/index.ts.html +0 -124
  130. package/coverage/src/irdevice/airconditioner.ts.html +0 -1687
  131. package/coverage/src/irdevice/airpurifier.ts.html +0 -844
  132. package/coverage/src/irdevice/camera.ts.html +0 -475
  133. package/coverage/src/irdevice/fan.ts.html +0 -766
  134. package/coverage/src/irdevice/index.html +0 -251
  135. package/coverage/src/irdevice/irdevice.ts.html +0 -1117
  136. package/coverage/src/irdevice/light.ts.html +0 -826
  137. package/coverage/src/irdevice/other.ts.html +0 -2458
  138. package/coverage/src/irdevice/tv.ts.html +0 -1222
  139. package/coverage/src/irdevice/vacuumcleaner.ts.html +0 -466
  140. package/coverage/src/irdevice/waterheater.ts.html +0 -469
  141. package/coverage/src/platform.ts.html +0 -8776
  142. package/coverage/src/settings.ts.html +0 -934
  143. package/coverage/src/utils.ts.html +0 -2092
  144. package/dist/devices-hap/airpurifier.d.ts +0 -54
  145. package/dist/devices-hap/airpurifier.d.ts.map +0 -1
  146. package/dist/devices-hap/airpurifier.js +0 -533
  147. package/dist/devices-hap/airpurifier.js.map +0 -1
  148. package/dist/devices-hap/blindtilt.d.ts +0 -90
  149. package/dist/devices-hap/blindtilt.d.ts.map +0 -1
  150. package/dist/devices-hap/blindtilt.js +0 -974
  151. package/dist/devices-hap/blindtilt.js.map +0 -1
  152. package/dist/devices-hap/bot.d.ts +0 -102
  153. package/dist/devices-hap/bot.d.ts.map +0 -1
  154. package/dist/devices-hap/bot.js +0 -822
  155. package/dist/devices-hap/bot.js.map +0 -1
  156. package/dist/devices-hap/ceilinglight.d.ts +0 -85
  157. package/dist/devices-hap/ceilinglight.d.ts.map +0 -1
  158. package/dist/devices-hap/ceilinglight.js +0 -707
  159. package/dist/devices-hap/ceilinglight.js.map +0 -1
  160. package/dist/devices-hap/colorbulb.d.ts +0 -88
  161. package/dist/devices-hap/colorbulb.d.ts.map +0 -1
  162. package/dist/devices-hap/colorbulb.js +0 -921
  163. package/dist/devices-hap/colorbulb.js.map +0 -1
  164. package/dist/devices-hap/contact.d.ts +0 -44
  165. package/dist/devices-hap/contact.d.ts.map +0 -1
  166. package/dist/devices-hap/contact.js +0 -409
  167. package/dist/devices-hap/contact.js.map +0 -1
  168. package/dist/devices-hap/curtain.d.ts +0 -73
  169. package/dist/devices-hap/curtain.d.ts.map +0 -1
  170. package/dist/devices-hap/curtain.js +0 -869
  171. package/dist/devices-hap/curtain.js.map +0 -1
  172. package/dist/devices-hap/device.d.ts +0 -108
  173. package/dist/devices-hap/device.d.ts.map +0 -1
  174. package/dist/devices-hap/device.js +0 -821
  175. package/dist/devices-hap/device.js.map +0 -1
  176. package/dist/devices-hap/fan.d.ts +0 -69
  177. package/dist/devices-hap/fan.d.ts.map +0 -1
  178. package/dist/devices-hap/fan.js +0 -655
  179. package/dist/devices-hap/fan.js.map +0 -1
  180. package/dist/devices-hap/hub.d.ts +0 -37
  181. package/dist/devices-hap/hub.d.ts.map +0 -1
  182. package/dist/devices-hap/hub.js +0 -393
  183. package/dist/devices-hap/hub.js.map +0 -1
  184. package/dist/devices-hap/humidifier.d.ts +0 -73
  185. package/dist/devices-hap/humidifier.d.ts.map +0 -1
  186. package/dist/devices-hap/humidifier.js +0 -716
  187. package/dist/devices-hap/humidifier.js.map +0 -1
  188. package/dist/devices-hap/iosensor.d.ts +0 -42
  189. package/dist/devices-hap/iosensor.d.ts.map +0 -1
  190. package/dist/devices-hap/iosensor.js +0 -397
  191. package/dist/devices-hap/iosensor.js.map +0 -1
  192. package/dist/devices-hap/lightstrip.d.ts +0 -79
  193. package/dist/devices-hap/lightstrip.d.ts.map +0 -1
  194. package/dist/devices-hap/lightstrip.js +0 -827
  195. package/dist/devices-hap/lightstrip.js.map +0 -1
  196. package/dist/devices-hap/lock.d.ts +0 -53
  197. package/dist/devices-hap/lock.d.ts.map +0 -1
  198. package/dist/devices-hap/lock.js +0 -569
  199. package/dist/devices-hap/lock.js.map +0 -1
  200. package/dist/devices-hap/meter.d.ts +0 -37
  201. package/dist/devices-hap/meter.d.ts.map +0 -1
  202. package/dist/devices-hap/meter.js +0 -380
  203. package/dist/devices-hap/meter.js.map +0 -1
  204. package/dist/devices-hap/meterplus.d.ts +0 -42
  205. package/dist/devices-hap/meterplus.d.ts.map +0 -1
  206. package/dist/devices-hap/meterplus.js +0 -385
  207. package/dist/devices-hap/meterplus.js.map +0 -1
  208. package/dist/devices-hap/meterpro.d.ts +0 -43
  209. package/dist/devices-hap/meterpro.d.ts.map +0 -1
  210. package/dist/devices-hap/meterpro.js +0 -469
  211. package/dist/devices-hap/meterpro.js.map +0 -1
  212. package/dist/devices-hap/motion.d.ts +0 -42
  213. package/dist/devices-hap/motion.d.ts.map +0 -1
  214. package/dist/devices-hap/motion.js +0 -345
  215. package/dist/devices-hap/motion.js.map +0 -1
  216. package/dist/devices-hap/plug.d.ts +0 -49
  217. package/dist/devices-hap/plug.d.ts.map +0 -1
  218. package/dist/devices-hap/plug.js +0 -400
  219. package/dist/devices-hap/plug.js.map +0 -1
  220. package/dist/devices-hap/relayswitch.d.ts +0 -96
  221. package/dist/devices-hap/relayswitch.d.ts.map +0 -1
  222. package/dist/devices-hap/relayswitch.js +0 -642
  223. package/dist/devices-hap/relayswitch.js.map +0 -1
  224. package/dist/devices-hap/robotvacuumcleaner.d.ts +0 -54
  225. package/dist/devices-hap/robotvacuumcleaner.d.ts.map +0 -1
  226. package/dist/devices-hap/robotvacuumcleaner.js +0 -530
  227. package/dist/devices-hap/robotvacuumcleaner.js.map +0 -1
  228. package/dist/devices-hap/waterdetector.d.ts +0 -41
  229. package/dist/devices-hap/waterdetector.d.ts.map +0 -1
  230. package/dist/devices-hap/waterdetector.js +0 -356
  231. package/dist/devices-hap/waterdetector.js.map +0 -1
  232. package/dist/devices-matter/BaseMatterAccessory.d.ts +0 -90
  233. package/dist/devices-matter/BaseMatterAccessory.d.ts.map +0 -1
  234. package/dist/devices-matter/BaseMatterAccessory.js +0 -264
  235. package/dist/devices-matter/BaseMatterAccessory.js.map +0 -1
  236. package/dist/devices-matter/ColorLightAccessory.d.ts +0 -20
  237. package/dist/devices-matter/ColorLightAccessory.d.ts.map +0 -1
  238. package/dist/devices-matter/ColorLightAccessory.js +0 -95
  239. package/dist/devices-matter/ColorLightAccessory.js.map +0 -1
  240. package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts +0 -18
  241. package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts.map +0 -1
  242. package/dist/devices-matter/ColorTemperatureLightAccessory.js +0 -76
  243. package/dist/devices-matter/ColorTemperatureLightAccessory.js.map +0 -1
  244. package/dist/devices-matter/ContactSensorAccessory.d.ts +0 -12
  245. package/dist/devices-matter/ContactSensorAccessory.d.ts.map +0 -1
  246. package/dist/devices-matter/ContactSensorAccessory.js +0 -34
  247. package/dist/devices-matter/ContactSensorAccessory.js.map +0 -1
  248. package/dist/devices-matter/DimmableLightAccessory.d.ts +0 -58
  249. package/dist/devices-matter/DimmableLightAccessory.d.ts.map +0 -1
  250. package/dist/devices-matter/DimmableLightAccessory.js +0 -167
  251. package/dist/devices-matter/DimmableLightAccessory.js.map +0 -1
  252. package/dist/devices-matter/DoorLockAccessory.d.ts +0 -14
  253. package/dist/devices-matter/DoorLockAccessory.d.ts.map +0 -1
  254. package/dist/devices-matter/DoorLockAccessory.js +0 -50
  255. package/dist/devices-matter/DoorLockAccessory.js.map +0 -1
  256. package/dist/devices-matter/ExtendedColorLightAccessory.d.ts +0 -21
  257. package/dist/devices-matter/ExtendedColorLightAccessory.d.ts.map +0 -1
  258. package/dist/devices-matter/ExtendedColorLightAccessory.js +0 -106
  259. package/dist/devices-matter/ExtendedColorLightAccessory.js.map +0 -1
  260. package/dist/devices-matter/FanAccessory.d.ts +0 -16
  261. package/dist/devices-matter/FanAccessory.d.ts.map +0 -1
  262. package/dist/devices-matter/FanAccessory.js +0 -81
  263. package/dist/devices-matter/FanAccessory.js.map +0 -1
  264. package/dist/devices-matter/HumiditySensorAccessory.d.ts +0 -12
  265. package/dist/devices-matter/HumiditySensorAccessory.d.ts.map +0 -1
  266. package/dist/devices-matter/HumiditySensorAccessory.js +0 -34
  267. package/dist/devices-matter/HumiditySensorAccessory.js.map +0 -1
  268. package/dist/devices-matter/LeakSensorAccessory.d.ts +0 -12
  269. package/dist/devices-matter/LeakSensorAccessory.d.ts.map +0 -1
  270. package/dist/devices-matter/LeakSensorAccessory.js +0 -33
  271. package/dist/devices-matter/LeakSensorAccessory.js.map +0 -1
  272. package/dist/devices-matter/LightSensorAccessory.d.ts +0 -12
  273. package/dist/devices-matter/LightSensorAccessory.d.ts.map +0 -1
  274. package/dist/devices-matter/LightSensorAccessory.js +0 -34
  275. package/dist/devices-matter/LightSensorAccessory.js.map +0 -1
  276. package/dist/devices-matter/OccupancySensorAccessory.d.ts +0 -12
  277. package/dist/devices-matter/OccupancySensorAccessory.d.ts.map +0 -1
  278. package/dist/devices-matter/OccupancySensorAccessory.js +0 -39
  279. package/dist/devices-matter/OccupancySensorAccessory.js.map +0 -1
  280. package/dist/devices-matter/OnOffLightAccessory.d.ts +0 -38
  281. package/dist/devices-matter/OnOffLightAccessory.d.ts.map +0 -1
  282. package/dist/devices-matter/OnOffLightAccessory.js +0 -110
  283. package/dist/devices-matter/OnOffLightAccessory.js.map +0 -1
  284. package/dist/devices-matter/OnOffOutletAccessory.d.ts +0 -14
  285. package/dist/devices-matter/OnOffOutletAccessory.d.ts.map +0 -1
  286. package/dist/devices-matter/OnOffOutletAccessory.js +0 -43
  287. package/dist/devices-matter/OnOffOutletAccessory.js.map +0 -1
  288. package/dist/devices-matter/OnOffSwitchAccessory.d.ts +0 -14
  289. package/dist/devices-matter/OnOffSwitchAccessory.d.ts.map +0 -1
  290. package/dist/devices-matter/OnOffSwitchAccessory.js +0 -42
  291. package/dist/devices-matter/OnOffSwitchAccessory.js.map +0 -1
  292. package/dist/devices-matter/RoboticVacuumAccessory.d.ts +0 -61
  293. package/dist/devices-matter/RoboticVacuumAccessory.d.ts.map +0 -1
  294. package/dist/devices-matter/RoboticVacuumAccessory.js +0 -544
  295. package/dist/devices-matter/RoboticVacuumAccessory.js.map +0 -1
  296. package/dist/devices-matter/SmokeCOAlarmAccessory.d.ts +0 -11
  297. package/dist/devices-matter/SmokeCOAlarmAccessory.d.ts.map +0 -1
  298. package/dist/devices-matter/SmokeCOAlarmAccessory.js +0 -49
  299. package/dist/devices-matter/SmokeCOAlarmAccessory.js.map +0 -1
  300. package/dist/devices-matter/TemperatureSensorAccessory.d.ts +0 -12
  301. package/dist/devices-matter/TemperatureSensorAccessory.d.ts.map +0 -1
  302. package/dist/devices-matter/TemperatureSensorAccessory.js +0 -36
  303. package/dist/devices-matter/TemperatureSensorAccessory.js.map +0 -1
  304. package/dist/devices-matter/ThermostatAccessory.d.ts +0 -19
  305. package/dist/devices-matter/ThermostatAccessory.d.ts.map +0 -1
  306. package/dist/devices-matter/ThermostatAccessory.js +0 -95
  307. package/dist/devices-matter/ThermostatAccessory.js.map +0 -1
  308. package/dist/devices-matter/VenetianBlindAccessory.d.ts +0 -19
  309. package/dist/devices-matter/VenetianBlindAccessory.d.ts.map +0 -1
  310. package/dist/devices-matter/VenetianBlindAccessory.js +0 -99
  311. package/dist/devices-matter/VenetianBlindAccessory.js.map +0 -1
  312. package/dist/devices-matter/WindowBlindAccessory.d.ts +0 -17
  313. package/dist/devices-matter/WindowBlindAccessory.d.ts.map +0 -1
  314. package/dist/devices-matter/WindowBlindAccessory.js +0 -131
  315. package/dist/devices-matter/WindowBlindAccessory.js.map +0 -1
  316. package/dist/devices-matter/custom/PowerStripAccessory.d.ts +0 -97
  317. package/dist/devices-matter/custom/PowerStripAccessory.d.ts.map +0 -1
  318. package/dist/devices-matter/custom/PowerStripAccessory.js +0 -265
  319. package/dist/devices-matter/custom/PowerStripAccessory.js.map +0 -1
  320. package/dist/devices-matter/custom/index.d.ts +0 -8
  321. package/dist/devices-matter/custom/index.d.ts.map +0 -1
  322. package/dist/devices-matter/custom/index.js +0 -8
  323. package/dist/devices-matter/custom/index.js.map +0 -1
  324. package/dist/devices-matter/index.d.ts +0 -29
  325. package/dist/devices-matter/index.d.ts.map +0 -1
  326. package/dist/devices-matter/index.js +0 -28
  327. package/dist/devices-matter/index.js.map +0 -1
  328. package/dist/irdevice/airconditioner.d.ts +0 -61
  329. package/dist/irdevice/airconditioner.d.ts.map +0 -1
  330. package/dist/irdevice/airconditioner.js +0 -472
  331. package/dist/irdevice/airconditioner.js.map +0 -1
  332. package/dist/irdevice/airpurifier.d.ts +0 -50
  333. package/dist/irdevice/airpurifier.d.ts.map +0 -1
  334. package/dist/irdevice/airpurifier.js +0 -213
  335. package/dist/irdevice/airpurifier.js.map +0 -1
  336. package/dist/irdevice/camera.d.ts +0 -32
  337. package/dist/irdevice/camera.d.ts.map +0 -1
  338. package/dist/irdevice/camera.js +0 -107
  339. package/dist/irdevice/camera.js.map +0 -1
  340. package/dist/irdevice/fan.d.ts +0 -36
  341. package/dist/irdevice/fan.d.ts.map +0 -1
  342. package/dist/irdevice/fan.js +0 -200
  343. package/dist/irdevice/fan.js.map +0 -1
  344. package/dist/irdevice/irdevice.d.ts +0 -69
  345. package/dist/irdevice/irdevice.d.ts.map +0 -1
  346. package/dist/irdevice/irdevice.js +0 -339
  347. package/dist/irdevice/irdevice.js.map +0 -1
  348. package/dist/irdevice/light.d.ts +0 -36
  349. package/dist/irdevice/light.d.ts.map +0 -1
  350. package/dist/irdevice/light.js +0 -206
  351. package/dist/irdevice/light.js.map +0 -1
  352. package/dist/irdevice/other.d.ts +0 -57
  353. package/dist/irdevice/other.d.ts.map +0 -1
  354. package/dist/irdevice/other.js +0 -778
  355. package/dist/irdevice/other.js.map +0 -1
  356. package/dist/irdevice/tv.d.ts +0 -45
  357. package/dist/irdevice/tv.d.ts.map +0 -1
  358. package/dist/irdevice/tv.js +0 -327
  359. package/dist/irdevice/tv.js.map +0 -1
  360. package/dist/irdevice/vacuumcleaner.d.ts +0 -28
  361. package/dist/irdevice/vacuumcleaner.d.ts.map +0 -1
  362. package/dist/irdevice/vacuumcleaner.js +0 -104
  363. package/dist/irdevice/vacuumcleaner.js.map +0 -1
  364. package/dist/irdevice/waterheater.d.ts +0 -30
  365. package/dist/irdevice/waterheater.d.ts.map +0 -1
  366. package/dist/irdevice/waterheater.js +0 -105
  367. package/dist/irdevice/waterheater.js.map +0 -1
  368. package/dist/platform-hap.d.ts +0 -160
  369. package/dist/platform-hap.d.ts.map +0 -1
  370. package/dist/platform-hap.js +0 -3041
  371. package/dist/platform-hap.js.map +0 -1
  372. package/dist/platform-matter.d.ts +0 -188
  373. package/dist/platform-matter.d.ts.map +0 -1
  374. package/dist/platform-matter.js +0 -2545
  375. package/dist/platform-matter.js.map +0 -1
  376. package/dist/test/apiRequestTracker.test.d.ts +0 -2
  377. package/dist/test/apiRequestTracker.test.d.ts.map +0 -1
  378. package/dist/test/apiRequestTracker.test.js +0 -392
  379. package/dist/test/apiRequestTracker.test.js.map +0 -1
  380. package/dist/test/hap/device-webhook-context.test.d.ts +0 -2
  381. package/dist/test/hap/device-webhook-context.test.d.ts.map +0 -1
  382. package/dist/test/hap/device-webhook-context.test.js +0 -128
  383. package/dist/test/hap/device-webhook-context.test.js.map +0 -1
  384. package/dist/test/hap/platform-hap.logging.test.d.ts +0 -2
  385. package/dist/test/hap/platform-hap.logging.test.d.ts.map +0 -1
  386. package/dist/test/hap/platform-hap.logging.test.js +0 -33
  387. package/dist/test/hap/platform-hap.logging.test.js.map +0 -1
  388. package/dist/test/hap/platform-hap.test.d.ts +0 -2
  389. package/dist/test/hap/platform-hap.test.d.ts.map +0 -1
  390. package/dist/test/hap/platform-hap.test.js +0 -62
  391. package/dist/test/hap/platform-hap.test.js.map +0 -1
  392. package/dist/test/helpers/platform-fixtures.d.ts +0 -9
  393. package/dist/test/helpers/platform-fixtures.d.ts.map +0 -1
  394. package/dist/test/helpers/platform-fixtures.js +0 -30
  395. package/dist/test/helpers/platform-fixtures.js.map +0 -1
  396. package/dist/test/homebridge-ui/server.test.d.ts +0 -2
  397. package/dist/test/homebridge-ui/server.test.d.ts.map +0 -1
  398. package/dist/test/homebridge-ui/server.test.js +0 -445
  399. package/dist/test/homebridge-ui/server.test.js.map +0 -1
  400. package/dist/test/index.test.d.ts +0 -2
  401. package/dist/test/index.test.d.ts.map +0 -1
  402. package/dist/test/index.test.js +0 -19
  403. package/dist/test/index.test.js.map +0 -1
  404. package/dist/test/matter/devices-matter/baseMatterAccessory.test.d.ts +0 -2
  405. package/dist/test/matter/devices-matter/baseMatterAccessory.test.d.ts.map +0 -1
  406. package/dist/test/matter/devices-matter/baseMatterAccessory.test.js +0 -71
  407. package/dist/test/matter/devices-matter/baseMatterAccessory.test.js.map +0 -1
  408. package/dist/test/matter/platform-matter.additional.test.d.ts +0 -2
  409. package/dist/test/matter/platform-matter.additional.test.d.ts.map +0 -1
  410. package/dist/test/matter/platform-matter.additional.test.js +0 -35
  411. package/dist/test/matter/platform-matter.additional.test.js.map +0 -1
  412. package/dist/test/matter/platform-matter.bleparse.test.d.ts +0 -2
  413. package/dist/test/matter/platform-matter.bleparse.test.d.ts.map +0 -1
  414. package/dist/test/matter/platform-matter.bleparse.test.js +0 -43
  415. package/dist/test/matter/platform-matter.bleparse.test.js.map +0 -1
  416. package/dist/test/matter/platform-matter.cleanup.test.d.ts +0 -2
  417. package/dist/test/matter/platform-matter.cleanup.test.d.ts.map +0 -1
  418. package/dist/test/matter/platform-matter.cleanup.test.js +0 -70
  419. package/dist/test/matter/platform-matter.cleanup.test.js.map +0 -1
  420. package/dist/test/matter/platform-matter.keepstale.test.d.ts +0 -2
  421. package/dist/test/matter/platform-matter.keepstale.test.d.ts.map +0 -1
  422. package/dist/test/matter/platform-matter.keepstale.test.js +0 -27
  423. package/dist/test/matter/platform-matter.keepstale.test.js.map +0 -1
  424. package/dist/test/matter/platform-matter.logging.test.d.ts +0 -2
  425. package/dist/test/matter/platform-matter.logging.test.d.ts.map +0 -1
  426. package/dist/test/matter/platform-matter.logging.test.js +0 -29
  427. package/dist/test/matter/platform-matter.logging.test.js.map +0 -1
  428. package/dist/test/matter/platform-matter.mapping.test.d.ts +0 -2
  429. package/dist/test/matter/platform-matter.mapping.test.d.ts.map +0 -1
  430. package/dist/test/matter/platform-matter.mapping.test.js +0 -43
  431. package/dist/test/matter/platform-matter.mapping.test.js.map +0 -1
  432. package/dist/test/matter/platform-matter.openapi-mapping.test.d.ts +0 -2
  433. package/dist/test/matter/platform-matter.openapi-mapping.test.d.ts.map +0 -1
  434. package/dist/test/matter/platform-matter.openapi-mapping.test.js +0 -84
  435. package/dist/test/matter/platform-matter.openapi-mapping.test.js.map +0 -1
  436. package/dist/test/matter/platform-matter.test.d.ts +0 -2
  437. package/dist/test/matter/platform-matter.test.d.ts.map +0 -1
  438. package/dist/test/matter/platform-matter.test.js +0 -117
  439. package/dist/test/matter/platform-matter.test.js.map +0 -1
  440. package/dist/test/matter/platform-matter.unregister.test.d.ts +0 -2
  441. package/dist/test/matter/platform-matter.unregister.test.d.ts.map +0 -1
  442. package/dist/test/matter/platform-matter.unregister.test.js +0 -30
  443. package/dist/test/matter/platform-matter.unregister.test.js.map +0 -1
  444. package/dist/test/matter/platform-matter.webhook.test.d.ts +0 -2
  445. package/dist/test/matter/platform-matter.webhook.test.d.ts.map +0 -1
  446. package/dist/test/matter/platform-matter.webhook.test.js +0 -46
  447. package/dist/test/matter/platform-matter.webhook.test.js.map +0 -1
  448. package/dist/test/utils.test.d.ts +0 -2
  449. package/dist/test/utils.test.d.ts.map +0 -1
  450. package/dist/test/utils.test.js +0 -95
  451. package/dist/test/utils.test.js.map +0 -1
  452. package/dist/test/verifyconfig.test.d.ts +0 -2
  453. package/dist/test/verifyconfig.test.d.ts.map +0 -1
  454. package/dist/test/verifyconfig.test.js +0 -167
  455. package/dist/test/verifyconfig.test.js.map +0 -1
  456. package/src/custom.d.ts +0 -7
  457. package/src/devices-hap/airpurifier.ts +0 -568
  458. package/src/devices-hap/blindtilt.ts +0 -1049
  459. package/src/devices-hap/bot.ts +0 -910
  460. package/src/devices-hap/ceilinglight.ts +0 -747
  461. package/src/devices-hap/colorbulb.ts +0 -940
  462. package/src/devices-hap/contact.ts +0 -457
  463. package/src/devices-hap/curtain.ts +0 -944
  464. package/src/devices-hap/device.ts +0 -890
  465. package/src/devices-hap/fan.ts +0 -716
  466. package/src/devices-hap/hub.ts +0 -440
  467. package/src/devices-hap/humidifier.ts +0 -762
  468. package/src/devices-hap/iosensor.ts +0 -442
  469. package/src/devices-hap/lightstrip.ts +0 -863
  470. package/src/devices-hap/lock.ts +0 -627
  471. package/src/devices-hap/meter.ts +0 -427
  472. package/src/devices-hap/meterplus.ts +0 -431
  473. package/src/devices-hap/meterpro.ts +0 -523
  474. package/src/devices-hap/motion.ts +0 -390
  475. package/src/devices-hap/plug.ts +0 -427
  476. package/src/devices-hap/relayswitch.ts +0 -727
  477. package/src/devices-hap/robotvacuumcleaner.ts +0 -574
  478. package/src/devices-hap/waterdetector.ts +0 -400
  479. package/src/devices-matter/BaseMatterAccessory.ts +0 -302
  480. package/src/devices-matter/ColorLightAccessory.ts +0 -110
  481. package/src/devices-matter/ColorTemperatureLightAccessory.ts +0 -90
  482. package/src/devices-matter/ContactSensorAccessory.ts +0 -41
  483. package/src/devices-matter/DimmableLightAccessory.ts +0 -192
  484. package/src/devices-matter/DoorLockAccessory.ts +0 -60
  485. package/src/devices-matter/ExtendedColorLightAccessory.ts +0 -122
  486. package/src/devices-matter/FanAccessory.ts +0 -95
  487. package/src/devices-matter/HumiditySensorAccessory.ts +0 -41
  488. package/src/devices-matter/LeakSensorAccessory.ts +0 -40
  489. package/src/devices-matter/LightSensorAccessory.ts +0 -41
  490. package/src/devices-matter/OccupancySensorAccessory.ts +0 -48
  491. package/src/devices-matter/OnOffLightAccessory.ts +0 -125
  492. package/src/devices-matter/OnOffOutletAccessory.ts +0 -51
  493. package/src/devices-matter/OnOffSwitchAccessory.ts +0 -51
  494. package/src/devices-matter/RoboticVacuumAccessory.ts +0 -621
  495. package/src/devices-matter/SmokeCOAlarmAccessory.ts +0 -59
  496. package/src/devices-matter/TemperatureSensorAccessory.ts +0 -43
  497. package/src/devices-matter/ThermostatAccessory.ts +0 -110
  498. package/src/devices-matter/VenetianBlindAccessory.ts +0 -115
  499. package/src/devices-matter/WindowBlindAccessory.ts +0 -135
  500. package/src/devices-matter/custom/PowerStripAccessory.ts +0 -309
  501. package/src/devices-matter/custom/index.ts +0 -8
  502. package/src/devices-matter/index.ts +0 -29
  503. package/src/irdevice/airconditioner.ts +0 -533
  504. package/src/irdevice/airpurifier.ts +0 -252
  505. package/src/irdevice/camera.ts +0 -129
  506. package/src/irdevice/fan.ts +0 -226
  507. package/src/irdevice/irdevice.ts +0 -383
  508. package/src/irdevice/light.ts +0 -246
  509. package/src/irdevice/other.ts +0 -790
  510. package/src/irdevice/tv.ts +0 -378
  511. package/src/irdevice/vacuumcleaner.ts +0 -126
  512. package/src/irdevice/waterheater.ts +0 -127
  513. package/src/platform-hap.ts +0 -3193
  514. package/src/platform-matter.ts +0 -2703
  515. package/src/test/apiRequestTracker.test.ts +0 -417
  516. package/src/test/hap/device-webhook-context.test.ts +0 -136
  517. package/src/test/hap/platform-hap.logging.test.ts +0 -36
  518. package/src/test/hap/platform-hap.test.ts +0 -70
  519. package/src/test/helpers/platform-fixtures.ts +0 -33
  520. package/src/test/homebridge-ui/server.test.ts +0 -486
  521. package/src/test/index.test.ts +0 -24
  522. package/src/test/matter/devices-matter/baseMatterAccessory.test.ts +0 -88
  523. package/src/test/matter/platform-matter.additional.test.ts +0 -44
  524. package/src/test/matter/platform-matter.bleparse.test.ts +0 -47
  525. package/src/test/matter/platform-matter.cleanup.test.ts +0 -86
  526. package/src/test/matter/platform-matter.keepstale.test.ts +0 -37
  527. package/src/test/matter/platform-matter.logging.test.ts +0 -33
  528. package/src/test/matter/platform-matter.mapping.test.ts +0 -57
  529. package/src/test/matter/platform-matter.openapi-mapping.test.ts +0 -109
  530. package/src/test/matter/platform-matter.test.ts +0 -144
  531. package/src/test/matter/platform-matter.unregister.test.ts +0 -39
  532. package/src/test/matter/platform-matter.webhook.test.ts +0 -54
  533. package/src/test/utils.test.ts +0 -96
  534. package/src/test/verifyconfig.test.ts +0 -198
package/dist/utils.js CHANGED
@@ -1,1373 +1,44 @@
1
- /* Copyright(C) 2017-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
2
- *
3
- * util.ts: @switchbot/homebridge-switchbot platform class.
4
- */
5
- import { existsSync, readFileSync, writeFileSync } from 'node:fs';
6
- import { join } from 'node:path';
7
- export var BlindTiltMappingMode;
8
- (function (BlindTiltMappingMode) {
9
- BlindTiltMappingMode["OnlyUp"] = "only_up";
10
- BlindTiltMappingMode["OnlyDown"] = "only_down";
11
- BlindTiltMappingMode["DownAndUp"] = "down_and_up";
12
- BlindTiltMappingMode["UpAndDown"] = "up_and_down";
13
- BlindTiltMappingMode["UseTiltForDirection"] = "use_tilt_for_direction";
14
- })(BlindTiltMappingMode || (BlindTiltMappingMode = {}));
15
- export function isCurtainDevice(device) {
16
- return device.deviceType === 'Curtain' || device.deviceType === 'Curtain3';
17
- }
18
- export function isBlindTiltDevice(device) {
19
- return device.deviceType === 'Blind Tilt';
20
- }
21
- export function sleep(ms) {
22
- return new Promise(resolve => setTimeout(resolve, ms));
23
- }
24
- /**
25
- * Check if the humidity is within the min and max range
26
- * @param humidity - The humidity value
27
- * @param min - The minimum humidity value
28
- * @param max - The maximum humidity value
29
- * @returns The humidity value
30
- */
31
- export function validHumidity(humidity, min, max) {
32
- if (humidity < (min || 0)) {
33
- return min ?? 0;
34
- }
35
- else if (humidity > (max || 100)) {
36
- return max ?? 100;
37
- }
38
- return humidity;
39
- }
40
- /**
41
- * Converts the value to celsius if the temperature units are in Fahrenheit
42
- */
43
- export function convertUnits(value, unit, convert) {
44
- // Convert only when source unit differs from target unit.
45
- // Supported values for unit/convert: 'CELSIUS' | 'FAHRENHEIT'
46
- if (!convert || unit === convert) {
47
- return value;
48
- }
49
- if (unit === 'CELSIUS' && convert === 'FAHRENHEIT') {
50
- return Math.round((value * 9) / 5 + 32);
51
- }
52
- if (unit === 'FAHRENHEIT' && convert === 'CELSIUS') {
53
- // Celsius should be to the nearest 0.5 degree
54
- return Math.round(((value - 32) * 5) / 9 * 2) / 2;
55
- }
56
- // Unknown unit combination: return as-is
57
- return value;
58
- }
59
- /**
60
- * Safely serializes an object to a JSON string, handling circular references.
61
- *
62
- * @param obj - The object to be serialized.
63
- * @returns The JSON string representation of the object.
64
- */
65
- export function safeStringify(obj) {
66
- const seen = new WeakSet();
67
- return JSON.stringify(obj, (_key, value) => {
68
- if (typeof value === 'object' && value !== null) {
69
- if (seen.has(value)) {
70
- return;
71
- }
72
- seen.add(value);
73
- }
74
- return value;
75
- }, ' ');
76
- }
77
- /**
78
- * Formats a device ID as a MAC address.
79
- * Ensures the device ID does not already contain colons.
80
- *
81
- * @param deviceId - The device ID to format.
82
- * @param cassSensative - If the MAC address should be case sensitive. Default is false, which will return the MAC address in lowercase.
83
- * @returns The formatted MAC address.
84
- * @throws Will throw an error if the device ID is not a valid MAC address or a 12-character hexadecimal string.
85
- */
86
- export function formatDeviceIdAsMac(deviceId, cassSensative) {
87
- if (typeof deviceId !== 'string') {
88
- throw new TypeError('Invalid device ID format. Device ID must be a string.');
89
- }
90
- deviceId = deviceId.trim();
91
- const macAddressRegex = /^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$/i;
92
- const hexRegex = /^[0-9a-f]{12}$/i;
93
- const vacuumFormatRegex = /^[a-z0-9]{12,18}$/i;
94
- // Check if the deviceId is already in a valid MAC address format
95
- if (macAddressRegex.test(deviceId)) {
96
- return cassSensative ? deviceId : deviceId.toLowerCase();
97
- }
98
- // Check if the deviceId is a valid 12-character hexadecimal string
99
- if (hexRegex.test(deviceId)) {
100
- const formattedDeviceId = deviceId.match(/.{1,2}/g).join(':');
101
- return cassSensative ? formattedDeviceId : formattedDeviceId.toLowerCase();
102
- }
103
- // Check if the deviceId matches the custom format
104
- if (vacuumFormatRegex.test(deviceId)) {
105
- const formattedDeviceId = deviceId.slice(-12).match(/.{1,2}/g).join(':');
106
- return cassSensative ? formattedDeviceId : formattedDeviceId.toLowerCase();
107
- }
108
- throw new Error(`Invalid device ID format. Must be a valid MAC address, a 12-character hexadecimal string, or a 12 to 18-character alphanumeric string. Device ID: ${deviceId}`);
109
- }
110
- export function rgb2hs(r, g, b) {
111
- /**
112
- * Credit:
113
- * https://github.com/WickyNilliams/pure-color
114
- */
115
- r = Number.parseInt(r);
116
- g = Number.parseInt(g);
117
- b = Number.parseInt(b);
118
- const min = Math.min(r, g, b);
119
- const max = Math.max(r, g, b);
120
- const delta = max - min;
121
- let h;
122
- let s;
123
- if (max === 0) {
124
- s = 0;
125
- }
126
- else {
127
- s = (delta / max) * 100;
128
- }
129
- if (max === min) {
130
- h = 0;
131
- }
132
- else if (r === max) {
133
- h = (g - b) / delta;
134
- }
135
- else if (g === max) {
136
- h = 2 + (b - r) / delta;
137
- }
138
- else if (b === max) {
139
- h = 4 + (r - g) / delta;
140
- }
141
- h = Math.min(h * 60, 360);
142
- if (h < 0) {
143
- h += 360;
144
- }
145
- return [Math.round(h), Math.round(s)];
146
- }
147
- export function hs2rgb(h, s) {
148
- /*
149
- Credit:
150
- https://github.com/WickyNilliams/pure-color
151
- */
152
- h = Number.parseInt(h) / 60;
153
- s = Number.parseInt(s) / 100;
154
- const f = h - Math.floor(h);
155
- const p = 255 * (1 - s);
156
- const q = 255 * (1 - s * f);
157
- const t = 255 * (1 - s * (1 - f));
158
- let rgb;
159
- switch (Math.floor(h) % 6) {
160
- case 0:
161
- rgb = [255, t, p];
162
- break;
163
- case 1:
164
- rgb = [q, 255, p];
165
- break;
166
- case 2:
167
- rgb = [p, 255, t];
168
- break;
169
- case 3:
170
- rgb = [p, q, 255];
171
- break;
172
- case 4:
173
- rgb = [t, p, 255];
174
- break;
175
- case 5:
176
- rgb = [255, p, q];
177
- break;
178
- }
179
- if (rgb[0] === 255) {
180
- rgb[1] *= 0.8;
181
- rgb[2] *= 0.8;
182
- if (rgb[1] <= 25 && rgb[2] <= 25) {
183
- rgb[1] = 0;
184
- rgb[2] = 0;
185
- }
186
- }
187
- return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2])];
188
- }
189
- export function k2rgb(k) {
190
- // Set kelvin to nearest 100, between 2000 and 7100
191
- k = Math.round(k / 100) * 100;
192
- k = Math.max(Math.min(k, 7100), 2000);
193
- // k should now appear in our table of kelvin to rgb
194
- const values = {
195
- 2000: [255, 141, 11],
196
- 2100: [255, 146, 29],
197
- 2200: [255, 147, 44],
198
- 2300: [255, 152, 54],
199
- 2400: [255, 157, 63],
200
- 2500: [255, 166, 69],
201
- 2600: [255, 170, 77],
202
- 2700: [255, 174, 84],
203
- 2800: [255, 173, 94],
204
- 2900: [255, 177, 101],
205
- 3000: [255, 180, 107],
206
- 3100: [255, 189, 111],
207
- 3200: [255, 187, 120],
208
- 3300: [255, 195, 124],
209
- 3400: [255, 198, 130],
210
- 3500: [255, 201, 135],
211
- 3600: [255, 203, 141],
212
- 3700: [255, 206, 146],
213
- 3800: [255, 204, 153],
214
- 3900: [255, 206, 159],
215
- 4000: [255, 213, 161],
216
- 4100: [255, 215, 166],
217
- 4200: [255, 217, 171],
218
- 4300: [255, 219, 175],
219
- 4400: [255, 221, 180],
220
- 4500: [255, 223, 184],
221
- 4600: [255, 225, 188],
222
- 4700: [255, 226, 192],
223
- 4800: [255, 228, 196],
224
- 4900: [255, 229, 200],
225
- 5000: [255, 231, 204],
226
- 5100: [255, 230, 210],
227
- 5200: [255, 234, 211],
228
- 5300: [255, 235, 215],
229
- 5400: [255, 237, 218],
230
- 5500: [255, 236, 224],
231
- 5700: [255, 240, 228],
232
- 5800: [255, 241, 231],
233
- 5900: [255, 243, 234],
234
- 6000: [255, 244, 237],
235
- 6100: [255, 245, 240],
236
- 6200: [255, 246, 243],
237
- 6300: [255, 247, 247],
238
- 6400: [255, 248, 251],
239
- 6500: [255, 249, 253],
240
- 6600: [254, 249, 255],
241
- 6700: [252, 247, 255],
242
- 6800: [249, 246, 255],
243
- 6900: [247, 245, 255],
244
- 7000: [245, 243, 255],
245
- 7100: [243, 242, 255],
246
- };
247
- // Return the value
248
- return values[k];
249
- }
250
- export function m2hs(m) {
251
- /*
252
- Credit:
253
- https://github.com/homebridge/HAP-NodeJS
254
- */
255
- const table = {
256
- 100: [19, 222.1],
257
- 101: [18.7, 222.2],
258
- 102: [18.4, 222.3],
259
- 103: [18.2, 222.3],
260
- 104: [17.9, 222.4],
261
- 105: [17.6, 222.5],
262
- 106: [17.3, 222.7],
263
- 107: [17, 222.8],
264
- 108: [16.7, 222.9],
265
- 109: [16.4, 223],
266
- 110: [16.1, 223.2],
267
- 111: [15.8, 223.3],
268
- 112: [15.4, 223.4],
269
- 113: [15.2, 223.6],
270
- 114: [14.9, 223.8],
271
- 115: [14.7, 223.9],
272
- 116: [14.3, 224.1],
273
- 117: [14.1, 224.2],
274
- 118: [13.8, 224.4],
275
- 119: [13.5, 224.6],
276
- 120: [13.2, 224.8],
277
- 121: [12.9, 225],
278
- 122: [12.5, 225.3],
279
- 123: [12.2, 225.6],
280
- 124: [11.8, 225.9],
281
- 125: [11.4, 226.3],
282
- 126: [11.1, 226.7],
283
- 127: [10.7, 227.1],
284
- 128: [10.3, 227.6],
285
- 129: [9.9, 228],
286
- 130: [9.6, 228.5],
287
- 131: [9.3, 229.1],
288
- 132: [8.9, 229.6],
289
- 133: [8.5, 230.2],
290
- 134: [8.2, 230.9],
291
- 135: [7.8, 231.6],
292
- 136: [7.5, 232.5],
293
- 137: [7.1, 233.5],
294
- 138: [6.7, 234.6],
295
- 139: [6.3, 235.8],
296
- 140: [6, 237.1],
297
- 141: [5.6, 238.9],
298
- 142: [5.2, 240.9],
299
- 143: [5, 242.9],
300
- 144: [4.8, 244.9],
301
- 145: [4.6, 246.9],
302
- 146: [4.4, 249.3],
303
- 147: [4.3, 251.9],
304
- 148: [4.1, 254.9],
305
- 149: [3.9, 258],
306
- 150: [3.7, 261.8],
307
- 151: [3.4, 265.9],
308
- 152: [3.2, 271],
309
- 153: [3, 276.4],
310
- 154: [2.8, 283.6],
311
- 155: [2.6, 290.4],
312
- 156: [2.3, 295.3],
313
- 157: [2.1, 300],
314
- 158: [1.9, 300],
315
- 159: [1.6, 300],
316
- 160: [1.4, 195.8],
317
- 161: [1.2, 84.3],
318
- 162: [1.3, 58.2],
319
- 163: [1.5, 55.9],
320
- 164: [1.7, 53.2],
321
- 165: [1.9, 50.2],
322
- 166: [2.1, 47.1],
323
- 167: [2.4, 44.5],
324
- 168: [2.6, 42.6],
325
- 169: [2.9, 40.9],
326
- 170: [3.1, 39.5],
327
- 171: [3.4, 38.3],
328
- 172: [3.7, 37.3],
329
- 173: [3.9, 36.5],
330
- 174: [4.2, 35.7],
331
- 175: [4.4, 35.1],
332
- 176: [4.6, 34.5],
333
- 177: [4.9, 34],
334
- 178: [5.1, 33.5],
335
- 179: [5.3, 33],
336
- 180: [5.6, 32.7],
337
- 181: [5.8, 32.3],
338
- 182: [6, 32],
339
- 183: [6.3, 31.7],
340
- 184: [6.5, 31.4],
341
- 185: [6.7, 31.2],
342
- 186: [7, 30.9],
343
- 187: [7.2, 30.7],
344
- 188: [7.4, 30.5],
345
- 189: [7.6, 30.3],
346
- 190: [7.9, 30.1],
347
- 191: [8.1, 29.9],
348
- 192: [8.4, 29.7],
349
- 193: [8.6, 29.6],
350
- 194: [8.9, 29.5],
351
- 195: [9.1, 29.3],
352
- 196: [9.4, 29.2],
353
- 197: [9.6, 29.1],
354
- 198: [9.8, 29],
355
- 199: [10, 28.9],
356
- 200: [10.2, 28.7],
357
- 201: [10.5, 28.7],
358
- 202: [10.7, 28.6],
359
- 203: [11, 28.5],
360
- 204: [11.2, 28.4],
361
- 205: [11.4, 28.3],
362
- 206: [11.6, 28.3],
363
- 207: [11.8, 28.2],
364
- 208: [12.1, 28.1],
365
- 209: [12.3, 28.1],
366
- 210: [12.5, 28],
367
- 211: [12.7, 28],
368
- 212: [12.9, 27.9],
369
- 213: [13.2, 27.8],
370
- 214: [13.4, 27.8],
371
- 215: [13.6, 27.7],
372
- 216: [13.8, 27.7],
373
- 217: [14, 27.7],
374
- 218: [14.3, 27.6],
375
- 219: [14.5, 27.6],
376
- 220: [14.7, 27.5],
377
- 221: [14.9, 27.5],
378
- 222: [15.1, 27.5],
379
- 223: [15.3, 27.4],
380
- 224: [15.5, 27.4],
381
- 225: [15.8, 27.4],
382
- 226: [16, 27.3],
383
- 227: [16.2, 27.3],
384
- 228: [16.4, 27.3],
385
- 229: [16.6, 27.3],
386
- 230: [16.8, 27.2],
387
- 231: [17, 27.2],
388
- 232: [17.2, 27.2],
389
- 233: [17.4, 27.2],
390
- 234: [17.6, 27.2],
391
- 235: [17.8, 27.1],
392
- 236: [18, 27.1],
393
- 237: [18.2, 27.1],
394
- 238: [18.4, 27.1],
395
- 239: [18.7, 27.1],
396
- 240: [18.8, 27],
397
- 241: [19, 27],
398
- 242: [19.2, 27],
399
- 243: [19.4, 27],
400
- 244: [19.6, 27],
401
- 245: [19.8, 27],
402
- 246: [20, 27],
403
- 247: [20.3, 26.9],
404
- 248: [20.5, 26.9],
405
- 249: [20.6, 26.9],
406
- 250: [20.8, 26.9],
407
- 251: [21, 26.9],
408
- 252: [21.3, 26.9],
409
- 253: [21.5, 26.9],
410
- 254: [21.6, 26.9],
411
- 255: [21.8, 26.8],
412
- 256: [22, 26.8],
413
- 257: [22.2, 26.8],
414
- 258: [22.4, 26.8],
415
- 259: [22.6, 26.8],
416
- 260: [22.8, 26.8],
417
- 261: [23, 26.8],
418
- 262: [23.2, 26.8],
419
- 263: [23.4, 26.8],
420
- 264: [23.6, 26.8],
421
- 265: [23.8, 26.8],
422
- 266: [24, 26.8],
423
- 267: [24.1, 26.8],
424
- 268: [24.3, 26.8],
425
- 269: [24.5, 26.8],
426
- 270: [24.7, 26.8],
427
- 271: [24.8, 26.8],
428
- 272: [25.1, 26.7],
429
- 273: [25.3, 26.7],
430
- 274: [25.4, 26.7],
431
- 275: [25.6, 26.7],
432
- 276: [25.8, 26.7],
433
- 277: [26, 26.7],
434
- 278: [26.1, 26.7],
435
- 279: [26.3, 26.7],
436
- 280: [26.5, 26.7],
437
- 281: [26.7, 26.7],
438
- 282: [26.9, 26.7],
439
- 283: [27.1, 26.7],
440
- 284: [27.3, 26.7],
441
- 285: [27.5, 26.7],
442
- 286: [27.7, 26.7],
443
- 287: [27.8, 26.7],
444
- 288: [28, 26.7],
445
- 289: [28.2, 26.7],
446
- 290: [28.4, 26.7],
447
- 291: [28.6, 26.7],
448
- 292: [28.8, 26.7],
449
- 293: [28.9, 26.7],
450
- 294: [29.1, 26.7],
451
- 295: [29.3, 26.7],
452
- 296: [29.5, 26.7],
453
- 297: [29.6, 26.7],
454
- 298: [29.8, 26.7],
455
- 299: [30, 26.7],
456
- 300: [30.2, 26.7],
457
- 301: [30.4, 26.7],
458
- 302: [30.5, 26.7],
459
- 303: [30.7, 26.7],
460
- 304: [30.9, 26.7],
461
- 305: [31.1, 26.7],
462
- 306: [31.2, 26.7],
463
- 307: [31.4, 26.7],
464
- 308: [31.6, 26.7],
465
- 309: [31.8, 26.8],
466
- 310: [31.9, 26.8],
467
- 311: [32.1, 26.8],
468
- 312: [32.3, 26.8],
469
- 313: [32.5, 26.8],
470
- 314: [32.6, 26.8],
471
- 315: [32.8, 26.8],
472
- 316: [33, 26.8],
473
- 317: [33.2, 26.8],
474
- 318: [33.3, 26.8],
475
- 319: [33.5, 26.8],
476
- 320: [33.7, 26.8],
477
- 321: [33.8, 26.8],
478
- 322: [34, 26.8],
479
- 323: [34.2, 26.8],
480
- 324: [34.4, 26.8],
481
- 325: [34.5, 26.8],
482
- 326: [34.7, 26.8],
483
- 327: [34.9, 26.8],
484
- 328: [35.1, 26.8],
485
- 329: [35.2, 26.8],
486
- 330: [35.4, 26.8],
487
- 331: [35.5, 26.8],
488
- 332: [35.7, 26.8],
489
- 333: [35.9, 26.8],
490
- 334: [36.1, 26.8],
491
- 335: [36.3, 26.9],
492
- 336: [36.5, 26.9],
493
- 337: [36.7, 26.9],
494
- 338: [36.9, 26.9],
495
- 339: [37.1, 26.9],
496
- 340: [37.2, 26.9],
497
- 341: [37.4, 26.9],
498
- 342: [37.5, 26.9],
499
- 343: [37.7, 26.9],
500
- 344: [37.9, 26.9],
501
- 345: [38.1, 26.9],
502
- 346: [38.3, 26.9],
503
- 347: [38.5, 26.9],
504
- 348: [38.7, 26.9],
505
- 349: [38.9, 26.9],
506
- 350: [39, 26.9],
507
- 351: [39.2, 26.9],
508
- 352: [39.3, 27],
509
- 353: [39.5, 27],
510
- 354: [39.7, 27],
511
- 355: [39.9, 27],
512
- 356: [40.1, 27],
513
- 357: [40.2, 27],
514
- 358: [40.4, 27],
515
- 359: [40.6, 27],
516
- 360: [40.8, 27],
517
- 361: [40.9, 27],
518
- 362: [41.1, 27],
519
- 363: [41.2, 27],
520
- 364: [41.4, 27],
521
- 365: [41.6, 27],
522
- 366: [41.8, 27],
523
- 367: [42, 27],
524
- 368: [42.1, 27.1],
525
- 369: [42.3, 27.1],
526
- 370: [42.4, 27.1],
527
- 371: [42.6, 27.1],
528
- 372: [42.8, 27.1],
529
- 373: [43, 27.1],
530
- 374: [43.1, 27.1],
531
- 375: [43.2, 27.1],
532
- 376: [43.4, 27.1],
533
- 377: [43.6, 27.1],
534
- 378: [43.8, 27.1],
535
- 379: [43.9, 27.1],
536
- 380: [44.1, 27.1],
537
- 381: [44.3, 27.2],
538
- 382: [44.4, 27.2],
539
- 383: [44.6, 27.2],
540
- 384: [44.7, 27.2],
541
- 385: [44.9, 27.2],
542
- 386: [45.1, 27.2],
543
- 387: [45.3, 27.2],
544
- 388: [45.5, 27.2],
545
- 389: [45.6, 27.2],
546
- 390: [45.8, 27.2],
547
- 391: [46, 27.2],
548
- 392: [46.2, 27.3],
549
- 393: [46.4, 27.3],
550
- 394: [46.5, 27.3],
551
- 395: [46.7, 27.3],
552
- 396: [46.9, 27.3],
553
- 397: [47.1, 27.3],
554
- 398: [47.2, 27.3],
555
- 399: [47.4, 27.3],
556
- 400: [47.6, 27.3],
557
- 401: [47.7, 27.3],
558
- 402: [47.9, 27.3],
559
- 403: [48.1, 27.3],
560
- 404: [48.3, 27.3],
561
- 405: [48.5, 27.4],
562
- 406: [48.7, 27.4],
563
- 407: [48.8, 27.4],
564
- 408: [49, 27.4],
565
- 409: [49.2, 27.4],
566
- 410: [49.4, 27.4],
567
- 411: [49.6, 27.4],
568
- 412: [49.7, 27.4],
569
- 413: [49.9, 27.4],
570
- 414: [50.1, 27.4],
571
- 415: [50.2, 27.4],
572
- 416: [50.4, 27.4],
573
- 417: [50.6, 27.5],
574
- 418: [50.7, 27.5],
575
- 419: [50.9, 27.5],
576
- 420: [51.1, 27.5],
577
- 421: [51.2, 27.5],
578
- 422: [51.4, 27.5],
579
- 423: [51.6, 27.5],
580
- 424: [51.7, 27.5],
581
- 425: [51.9, 27.5],
582
- 426: [52.1, 27.5],
583
- 427: [51.2, 27.6],
584
- 428: [52.4, 27.6],
585
- 429: [52.5, 27.6],
586
- 430: [52.7, 27.6],
587
- 431: [52.9, 27.6],
588
- 432: [53.1, 27.6],
589
- 433: [53.2, 27.6],
590
- 434: [53.4, 27.6],
591
- 435: [53.6, 27.6],
592
- 436: [53.7, 27.6],
593
- 437: [53.9, 27.6],
594
- 438: [54.1, 27.7],
595
- 439: [54.2, 27.7],
596
- 440: [54.3, 27.7],
597
- 441: [54.5, 27.7],
598
- 442: [54.7, 27.7],
599
- 443: [54.8, 27.7],
600
- 444: [55, 27.7],
601
- 445: [55.2, 27.7],
602
- 446: [55.3, 27.7],
603
- 447: [55.5, 27.7],
604
- 448: [55.7, 27.7],
605
- 449: [55.8, 27.8],
606
- 450: [56, 27.8],
607
- 451: [56.2, 27.8],
608
- 452: [56.3, 27.8],
609
- 453: [56.5, 27.8],
610
- 454: [56.7, 27.8],
611
- 455: [56.8, 27.8],
612
- 456: [57, 27.8],
613
- 457: [57.2, 27.8],
614
- 458: [57.3, 27.9],
615
- 459: [57.4, 27.9],
616
- 460: [57.6, 27.9],
617
- 461: [57.8, 27.9],
618
- 462: [57.9, 27.9],
619
- 463: [58.1, 27.9],
620
- 464: [58.3, 27.9],
621
- 465: [58.4, 27.9],
622
- 466: [58.6, 27.9],
623
- 467: [58.8, 27.9],
624
- 468: [59, 28],
625
- 469: [59.1, 28],
626
- 470: [59.2, 28],
627
- 471: [59.4, 28],
628
- 472: [59.6, 28],
629
- 473: [59.7, 28],
630
- 474: [60, 28],
631
- 475: [60.1, 28],
632
- 476: [60.2, 28],
633
- 477: [60.4, 28],
634
- 478: [60.6, 28.1],
635
- 479: [60.7, 28.1],
636
- 480: [60.9, 28.1],
637
- 481: [60.1, 28.1],
638
- 482: [60.3, 28.1],
639
- 483: [61.4, 28.1],
640
- 484: [61.5, 28.1],
641
- 485: [61.7, 28.1],
642
- 486: [61.9, 28.1],
643
- 487: [62, 28.2],
644
- 488: [62.2, 28.2],
645
- 489: [62.3, 28.2],
646
- 490: [62.5, 28.2],
647
- 491: [62.7, 28.2],
648
- 492: [62.8, 28.2],
649
- 493: [63, 28.2],
650
- 494: [63.2, 28.2],
651
- 495: [63.3, 28.2],
652
- 496: [63.4, 28.2],
653
- 497: [63.6, 28.2],
654
- 498: [63.8, 28.3],
655
- 499: [63.9, 28.3],
656
- 500: [64.1, 28.3],
657
- };
658
- const input = Math.min(Math.max(Math.round(m), 140), 500);
659
- const toReturn = table[input];
660
- return [Math.round(toReturn[1]), Math.round(toReturn[0])];
661
- }
662
- /**
663
- * Factory that returns a function to send OpenAPI commands using a retry wrapper.
664
- *
665
- * @param retryCommandFunc - bound function that calls platform.retryCommand(device, bodyChange, maxRetries, delay)
666
- * @param deviceObj - the device object to operate on
667
- * @param opts - optional overrides for maxRetries and delayBetweenRetries
668
- * @param opts.maxRetries - override for maxRetries
669
- * @param opts.delayBetweenRetries - override for delayBetweenRetries
670
- */
671
- export function makeOpenAPISender(retryCommandFunc, deviceObj, opts) {
672
- return async (command, parameter = 'default') => {
673
- const bodyChange = { command, parameter, commandType: 'command' };
674
- return retryCommandFunc(deviceObj, bodyChange, opts?.maxRetries, opts?.delayBetweenRetries);
675
- };
676
- }
677
- /**
678
- * Factory that returns a function to perform BLE actions using a SwitchBotBLE client.
679
- * Handles discovery retries and method invocation on the discovered device instance.
680
- *
681
- * @param switchBotBLE - instance of SwitchBotBLE (may be undefined)
682
- * @param deviceObj - the device object (used to obtain bleModel/deviceId)
683
- * @param opts - optional retry settings
684
- * @param opts.bleRetries - number of BLE discovery retries
685
- * @param opts.bleRetryDelay - delay between BLE retries in ms
686
- */
687
- export function makeBLESender(switchBotBLE, deviceObj, opts) {
688
- return async (methodName, ...args) => {
689
- if (!switchBotBLE) {
690
- throw new Error('Platform BLE not available');
691
- }
692
- const id = formatDeviceIdAsMac(deviceObj.deviceId);
693
- const maxRetries = opts?.bleRetries ?? 2;
694
- const retryDelay = opts?.bleRetryDelay ?? 500;
695
- let attempt = 0;
696
- while (attempt < maxRetries) {
697
- try {
698
- const list = await switchBotBLE.discover({ model: deviceObj.bleModel, id });
699
- if (!Array.isArray(list) || list.length === 0) {
700
- throw new Error('BLE device not found');
701
- }
702
- const deviceInst = list[0];
703
- if (typeof deviceInst[methodName] !== 'function') {
704
- throw new TypeError(`BLE method ${methodName} not available on device`);
705
- }
706
- return await deviceInst[methodName](...args);
707
- }
708
- catch (e) {
709
- attempt++;
710
- if (attempt >= maxRetries) {
711
- throw e;
712
- }
713
- await sleep(retryDelay);
714
- }
715
- }
716
- throw new Error('BLE operation failed');
717
- };
718
- }
719
- /**
720
- * Decide effective connection type for a device given platform options.
721
- * Mirrors the logic previously in platform-matter.
722
- */
723
- export function chooseConnectionType(platformOptions, deviceObj) {
724
- if (deviceObj?.connectionType) {
725
- return deviceObj.connectionType === 'BLE' ? 'BLE' : 'OpenAPI';
726
- }
727
- if (platformOptions?.BLE && (deviceObj?.bleModel || (typeof deviceObj?.deviceId === 'string' && deviceObj.deviceId.length > 0))) {
728
- return 'BLE';
729
- }
730
- return 'OpenAPI';
731
- }
732
- /**
733
- * Detect whether Matter is enabled/available on the provided Homebridge API object.
734
- * This encapsulates the multi-fallback detection used across the project.
735
- */
736
- /**
737
- * Detect whether Matter is enabled on the provided Homebridge API object.
738
- * Returns an object with an `enabled` boolean and an optional `reason` string
739
- * describing which check matched (useful for diagnostics).
740
- */
741
- export function detectMatter(apiObj) {
742
- try {
743
- const maybe = apiObj.isMatterEnabled;
744
- if (typeof maybe === 'function') {
745
- return { enabled: Boolean(maybe.call(apiObj)), reason: 'api.isMatterEnabled() returned truthy' };
746
- }
747
- if (typeof maybe !== 'undefined') {
748
- return { enabled: Boolean(maybe), reason: 'api.isMatterEnabled property present' };
749
- }
750
- const server = apiObj.server ?? apiObj.homebridgeServer ?? apiObj.homebridge_server;
751
- const serverMaybe = server?.isMatterEnabled;
752
- if (typeof serverMaybe === 'function') {
753
- return { enabled: Boolean(serverMaybe.call(server)), reason: 'server.isMatterEnabled() returned truthy' };
754
- }
755
- if (typeof server?.isMatterEnabled !== 'undefined') {
756
- return { enabled: Boolean(server.isMatterEnabled), reason: 'server.isMatterEnabled property present' };
757
- }
758
- }
759
- catch (e) {
760
- return { enabled: false, reason: `error during detection: ${String(e?.message ?? e)}` };
761
- }
762
- return { enabled: false, reason: 'no isMatterEnabled API or server fallback detected' };
763
- }
764
- /**
765
- * Backwards-compatible boolean wrapper for detectMatter.
766
- */
767
- export function detectMatterEnabled(apiObj) {
768
- return detectMatter(apiObj).enabled;
769
- }
770
- /**
771
- * Create platform logging helpers used by both HAP and Matter platforms.
772
- *
773
- * getPlatformLogging may be either a synchronous string-returning function or an
774
- * async function that resolves to the current platform logging setting. The
775
- * returned helpers mirror the instance methods previously implemented on the
776
- * HAP platform (infoLog, warnLog, errorLog, debugLog, etc.).
777
- */
778
- export function createPlatformLogger(getPlatformLogging, log) {
779
- const getPL = async () => {
780
- try {
781
- return await getPlatformLogging();
782
- }
783
- catch {
784
- return undefined;
785
- }
786
- };
787
- const loggingIsDebug = async () => {
788
- const pl = await getPL();
789
- return pl === 'debugMode' || pl === 'debug';
790
- };
791
- const enablingPlatformLogging = async () => {
792
- const pl = await getPL();
793
- return pl === 'debugMode' || pl === 'debug' || pl === 'standard';
794
- };
795
- const formatArgs = (args) => {
796
- return args
797
- .map((a) => {
798
- if (typeof a === 'string') {
799
- return a;
800
- }
801
- try {
802
- return JSON.stringify(a);
803
- }
804
- catch {
805
- return String(a);
806
- }
807
- })
808
- .join(' ');
809
- };
810
- return {
811
- // Format arbitrary arguments into a single string to ensure values are not dropped
812
- // when loggers only accept a single message parameter.
813
- // Prefer readable JSON for objects, fall back to String() on errors.
814
- // Example: infoLog('Loaded', accessory.displayName) => "Loaded My Light"
815
- infoLog: async (...args) => {
816
- if (await enablingPlatformLogging()) {
817
- const msg = formatArgs(args);
818
- log.info(msg);
819
- }
820
- },
821
- successLog: async (...args) => {
822
- if (await enablingPlatformLogging()) {
823
- const msg = formatArgs(args);
824
- log.success?.(msg) ?? log.info(msg);
825
- }
826
- },
827
- debugSuccessLog: async (...args) => {
828
- if (await enablingPlatformLogging()) {
829
- if (await loggingIsDebug()) {
830
- const msg = formatArgs(args);
831
- log.success?.(`[DEBUG] ${msg}`) ?? log.info(`[DEBUG] ${msg}`);
832
- }
833
- }
834
- },
835
- warnLog: async (...args) => {
836
- if (await enablingPlatformLogging()) {
837
- const msg = formatArgs(args);
838
- log.warn(msg);
839
- }
840
- },
841
- debugWarnLog: async (...args) => {
842
- if (await enablingPlatformLogging()) {
843
- if (await loggingIsDebug()) {
844
- const msg = formatArgs(args);
845
- log.warn(`[DEBUG] ${msg}`);
846
- }
847
- }
848
- },
849
- errorLog: async (...args) => {
850
- if (await enablingPlatformLogging()) {
851
- const msg = formatArgs(args);
852
- log.error(msg);
853
- }
854
- },
855
- debugErrorLog: async (...args) => {
856
- if (await enablingPlatformLogging()) {
857
- if (await loggingIsDebug()) {
858
- const msg = formatArgs(args);
859
- log.error(`[DEBUG] ${msg}`);
860
- }
861
- }
862
- },
863
- debugLog: async (...args) => {
864
- if (await enablingPlatformLogging()) {
865
- const pl = await getPL();
866
- if (pl === 'debug') {
867
- const msg = formatArgs(args);
868
- log.info(`[DEBUG] ${msg}`);
869
- }
870
- else if (pl === 'debugMode') {
871
- const msg = formatArgs(args);
872
- log.debug(msg);
873
- }
874
- }
875
- },
876
- loggingIsDebug,
877
- enablingPlatformLogging,
878
- };
879
- }
880
- /**
881
- * Create a Platform proxy class that selects between two platform constructors
882
- * (HAP vs Matter) at runtime using `detectMatter`. Returns a class suitable
883
- * for passing to `api.registerPlatform`.
884
- */
885
- export function createPlatformProxy(HAPCtor, MatterCtor) {
886
- return class PlatformProxy {
887
- log;
888
- config;
889
- api;
890
- delegate;
1
+ // Canonical Matter cluster ID mapping (from matter.js clusters)
2
+ export const MATTER_CLUSTER_IDS = {
3
+ OnOff: 0x0006,
4
+ LevelControl: 0x0008,
5
+ ColorControl: 0x0300,
6
+ WindowCovering: 0x0102,
7
+ DoorLock: 0x0101,
8
+ FanControl: 0x0202,
9
+ RelativeHumidityMeasurement: 0x0405,
10
+ };
11
+ // Common Matter attribute IDs grouped by cluster
12
+ export const MATTER_ATTRIBUTE_IDS = {
13
+ OnOff: { OnOff: 0x0000 },
14
+ LevelControl: { CurrentLevel: 0x0000 },
15
+ ColorControl: { CurrentHue: 0x0000, CurrentSaturation: 0x0001, ColorTemperatureMireds: 0x0002 },
16
+ WindowCovering: { CurrentPosition: 0x0000, TargetPosition: 0x0001 },
17
+ FanControl: { SpeedCurrent: 0x0000 },
18
+ DoorLock: { LockState: 0x0000 },
19
+ RelativeHumidityMeasurement: { MeasuredValue: 0x0000 },
20
+ };
21
+ export function normalizeConfig(raw) {
22
+ if (!raw)
23
+ return {};
24
+ return { ...raw };
25
+ }
26
+ // Create a Proxy constructor that instantiates the right platform implementation at runtime.
27
+ export function createPlatformProxy(HAPPlatform, MatterPlatform) {
28
+ return class SwitchBotPlatformProxy {
29
+ impl;
891
30
  constructor(log, config, api) {
892
- this.log = log;
893
- this.config = config;
894
- this.api = api;
895
- const matterInfo = detectMatter(this.api);
896
- const isMatter = matterInfo.enabled;
897
- const reason = matterInfo.reason ? ` Reason: ${matterInfo.reason}` : '';
898
- this.log.info?.(`Homebridge SwitchBot Plugin initializing in ${isMatter ? 'Matter' : 'HAP'} mode.`);
899
- this.log.debug?.(`Homebridge SwitchBot Plugin initializing in ${isMatter ? 'Matter' : 'HAP'} mode.${reason}`);
900
- const PlatformCtor = isMatter ? MatterCtor : HAPCtor;
901
- this.delegate = new PlatformCtor(this.log, this.config, this.api);
902
- }
903
- configureAccessory(accessory) {
904
- try {
905
- if (this.delegate && typeof this.delegate.configureAccessory === 'function') {
906
- return this.delegate.configureAccessory(accessory);
907
- }
908
- }
909
- catch (e) {
910
- // swallow — preserve previous behaviour where delegate errors don't bubble here
31
+ const cfg = normalizeConfig(config);
32
+ const preferMatter = cfg.preferMatter ?? true;
33
+ const enableMatter = cfg.enableMatter ?? true;
34
+ if (enableMatter && preferMatter && MatterPlatform) {
35
+ this.impl = new MatterPlatform(log, cfg, api);
36
+ return this.impl;
911
37
  }
38
+ // Fallback to HAP
39
+ this.impl = new HAPPlatform(log, cfg, api);
40
+ return this.impl;
912
41
  }
913
- configureMatterAccessory(accessory) {
914
- try {
915
- if (this.delegate && typeof this.delegate.configureMatterAccessory === 'function') {
916
- return this.delegate.configureMatterAccessory(accessory);
917
- }
918
- }
919
- catch (e) {
920
- // swallow — delegate may not implement this or may throw
921
- }
922
- }
923
- get accessories() {
924
- try {
925
- return this.delegate?.accessories;
926
- }
927
- catch (e) {
928
- return undefined;
929
- }
930
- }
931
- get matterAccessories() {
932
- try {
933
- return this.delegate?.matterAccessories;
934
- }
935
- catch (e) {
936
- return undefined;
937
- }
938
- }
939
- };
940
- }
941
- /**
942
- * API Request Tracker - Persistent tracking of SwitchBot API calls
943
- * Tracks requests per day with automatic midnight rollover
944
- */
945
- export class ApiRequestTracker {
946
- count = 0;
947
- date = '';
948
- statsFile = '';
949
- hourlyTimer;
950
- midnightTimer;
951
- log;
952
- // Daily limits
953
- dailyLimit;
954
- reserveForCommands;
955
- lastWarn = {};
956
- pausePollingAtReserve = false;
957
- resetAtLocalMidnight = false;
958
- constructor(api, log, pluginName = 'SwitchBot', limits) {
959
- this.log = log;
960
- this.statsFile = join(api.user.storagePath(), `${pluginName.toLowerCase()}-api-stats.json`);
961
- this.dailyLimit = Math.max(0, Number(limits?.dailyLimit ?? 10000));
962
- this.reserveForCommands = Math.max(0, Number(limits?.reserveForCommands ?? 1000));
963
- this.pausePollingAtReserve = Boolean(limits?.pausePollingAtReserve ?? false);
964
- this.resetAtLocalMidnight = Boolean(limits?.resetAtLocalMidnight ?? false);
965
- this.load();
966
- }
967
- /**
968
- * Return date key string (YYYY-MM-DD) based on reset mode
969
- * - UTC (default): uses UTC date
970
- * - Local: uses local timezone date
971
- */
972
- dateKey(now = new Date()) {
973
- if (!this.resetAtLocalMidnight) {
974
- return now.toISOString().split('T')[0];
975
- }
976
- const y = now.getFullYear();
977
- const m = (now.getMonth() + 1).toString().padStart(2, '0');
978
- const d = now.getDate().toString().padStart(2, '0');
979
- return `${y}-${m}-${d}`;
980
- }
981
- /**
982
- * Load API request statistics from persistent storage
983
- */
984
- load() {
985
- try {
986
- const today = this.dateKey();
987
- if (existsSync(this.statsFile)) {
988
- const data = JSON.parse(readFileSync(this.statsFile, 'utf8'));
989
- // If it's a new day, reset the counter
990
- if (data.date === today) {
991
- this.count = data.count || 0;
992
- this.date = data.date;
993
- this.log.warn?.(`[API Stats] Loaded: ${this.count} requests today (${today})`);
994
- }
995
- else {
996
- this.log.error?.(`[API Stats] New day detected (${this.resetAtLocalMidnight ? 'local' : 'UTC'}). Previous: ${data.count || 0} requests on ${data.date}`);
997
- this.count = 0;
998
- this.date = today;
999
- this.save();
1000
- }
1001
- }
1002
- else {
1003
- this.log.debug?.('[API Stats] No existing stats file, starting fresh');
1004
- this.count = 0;
1005
- this.date = today;
1006
- this.save();
1007
- }
1008
- }
1009
- catch (e) {
1010
- this.log.error?.(`[API Stats] Failed to load stats: ${e?.message ?? e}`);
1011
- this.count = 0;
1012
- this.date = this.dateKey();
1013
- }
1014
- }
1015
- /**
1016
- * Save API request statistics to persistent storage
1017
- */
1018
- save() {
1019
- try {
1020
- const data = {
1021
- date: this.date,
1022
- count: this.count,
1023
- lastUpdated: new Date().toISOString(),
1024
- };
1025
- writeFileSync(this.statsFile, JSON.stringify(data, null, 2), 'utf8');
1026
- }
1027
- catch (e) {
1028
- this.log.debug?.(`[API Stats] Failed to save stats: ${e?.message ?? e}`);
1029
- }
1030
- }
1031
- /**
1032
- * Increment API request counter and save
1033
- */
1034
- track() {
1035
- const today = this.dateKey();
1036
- // Reset counter if it's a new day
1037
- if (this.date !== today) {
1038
- this.log.debug?.(`[API Stats] Day rollover: ${this.count} requests on ${this.date}`);
1039
- this.count = 0;
1040
- this.date = today;
1041
- }
1042
- this.count++;
1043
- this.save();
1044
- }
1045
- /**
1046
- * Attempt to spend from the daily budget for a request of a given kind.
1047
- * Kinds: 'command' (user actions), 'poll' (status refresh), 'discovery'.
1048
- * Returns true if allowed (and increments the counter), false if blocked.
1049
- */
1050
- trySpend(kind, n = 1) {
1051
- const today = this.dateKey();
1052
- if (this.date !== today) {
1053
- // Day rollover
1054
- this.log.debug?.(`[API Stats] Day rollover: ${this.count} requests on ${this.date}`);
1055
- this.count = 0;
1056
- this.date = today;
1057
- this.save();
1058
- }
1059
- const softCap = Math.max(0, this.dailyLimit - this.reserveForCommands);
1060
- const projected = this.count + n;
1061
- const now = Date.now();
1062
- const overHardCap = projected > this.dailyLimit;
1063
- const overSoftCap = projected > softCap;
1064
- const shouldRateLimit = (kind === 'command')
1065
- ? overHardCap
1066
- : (this.pausePollingAtReserve ? overSoftCap : overHardCap);
1067
- if (shouldRateLimit) {
1068
- const warnKey = kind === 'command' ? 'hardcap' : 'softcap';
1069
- const last = this.lastWarn[warnKey] ?? 0;
1070
- if (now - last > 10 * 60 * 1000) { // warn at most every 10 minutes
1071
- if (kind === 'command') {
1072
- this.log.error?.(`[API Stats] Daily limit (${this.dailyLimit}) reached. Blocking command requests until reset.`);
1073
- }
1074
- else {
1075
- const remainingForCommands = Math.max(0, this.dailyLimit - this.count);
1076
- this.log.warn?.(`[API Stats] Near daily limit. Pausing ${kind} requests to reserve ~${this.reserveForCommands} calls for commands. Remaining today: ${remainingForCommands}`);
1077
- }
1078
- this.lastWarn[warnKey] = now;
1079
- }
1080
- return false;
1081
- }
1082
- this.count += n;
1083
- this.save();
1084
- return true;
1085
- }
1086
- /**
1087
- * Start hourly logging of API request count
1088
- */
1089
- startHourlyLogging() {
1090
- // Log immediately on startup
1091
- const softCap = Math.max(0, this.dailyLimit - this.reserveForCommands);
1092
- const remaining = Math.max(0, this.dailyLimit - this.count);
1093
- const percentUsed = Math.round((this.count / this.dailyLimit) * 100);
1094
- this.log.info?.(`[API Stats] Today (${this.date}): ${this.count}/${this.dailyLimit} API requests (${percentUsed}%), ${remaining} remaining`);
1095
- this.log.info?.(`[API Stats] Reset schedule: ${this.resetAtLocalMidnight ? 'local midnight' : 'UTC midnight'}`);
1096
- if (this.count >= this.dailyLimit) {
1097
- this.log.warn?.('[API Stats] ⚠️ DAILY LIMIT REACHED - All API requests blocked until reset');
1098
- }
1099
- else if (this.count >= softCap) {
1100
- this.log.warn?.(`[API Stats] ⚠️ NEAR LIMIT - Background polling paused, ${remaining} requests reserved for commands`);
1101
- }
1102
- // Then log every hour
1103
- this.hourlyTimer = setInterval(() => {
1104
- const today = this.dateKey();
1105
- if (this.date !== today) {
1106
- // Day rollover
1107
- this.log.info?.(`[API Stats] Day rollover - Previous day (${this.date}): ${this.count} API requests`);
1108
- this.count = 0;
1109
- this.date = today;
1110
- this.save();
1111
- this.log.info?.('[API Stats] ✅ Polling resumed after daily reset');
1112
- }
1113
- const remaining = Math.max(0, this.dailyLimit - this.count);
1114
- const percentUsed = Math.round((this.count / this.dailyLimit) * 100);
1115
- const softCap = Math.max(0, this.dailyLimit - this.reserveForCommands);
1116
- // Provide context-aware status message
1117
- if (this.count >= this.dailyLimit) {
1118
- this.log.warn?.(`[API Stats] Today (${this.date}): ${this.count}/${this.dailyLimit} requests (${percentUsed}%) - ⚠️ LIMIT REACHED, all requests blocked`);
1119
- }
1120
- else if (this.count >= softCap) {
1121
- this.log.warn?.(`[API Stats] Today (${this.date}): ${this.count}/${this.dailyLimit} requests (${percentUsed}%), ${remaining} remaining - polling paused`);
1122
- }
1123
- else {
1124
- this.log.info?.(`[API Stats] Today (${this.date}): ${this.count}/${this.dailyLimit} requests (${percentUsed}%), ${remaining} remaining`);
1125
- }
1126
- }, 60 * 60 * 1000); // Every hour
1127
- // Schedule an exact midnight rollover log/reset
1128
- this.scheduleMidnightRollover();
1129
- }
1130
- /**
1131
- * Stop hourly logging
1132
- */
1133
- stopHourlyLogging() {
1134
- if (this.hourlyTimer) {
1135
- clearInterval(this.hourlyTimer);
1136
- this.hourlyTimer = undefined;
1137
- }
1138
- if (this.midnightTimer) {
1139
- clearTimeout(this.midnightTimer);
1140
- this.midnightTimer = undefined;
1141
- }
1142
- }
1143
- /**
1144
- * Get current count
1145
- */
1146
- getCount() {
1147
- return this.count;
1148
- }
1149
- /**
1150
- * Get current date
1151
- */
1152
- getDate() {
1153
- return this.date;
1154
- }
1155
- /** Schedule a precise log/reset at the next UTC midnight */
1156
- scheduleMidnightRollover() {
1157
- try {
1158
- // Clear any previous timer
1159
- if (this.midnightTimer) {
1160
- clearTimeout(this.midnightTimer);
1161
- this.midnightTimer = undefined;
1162
- }
1163
- const now = new Date();
1164
- let delay = 0;
1165
- if (this.resetAtLocalMidnight) {
1166
- // Next local midnight
1167
- const next = new Date(now);
1168
- next.setHours(24, 0, 0, 0); // rolls to next day at 00:00 local
1169
- delay = Math.max(1000, next.getTime() - now.getTime());
1170
- }
1171
- else {
1172
- // Next UTC midnight
1173
- const nextUtcMidnightMs = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1, 0, 0, 0, 0);
1174
- delay = Math.max(1000, nextUtcMidnightMs - now.getTime());
1175
- }
1176
- this.midnightTimer = setTimeout(() => {
1177
- try {
1178
- const today = this.dateKey();
1179
- if (this.date !== today) {
1180
- this.log.info?.(`[API Stats] 🌙 Day rollover - Previous day (${this.date}): ${this.count} API requests`);
1181
- this.count = 0;
1182
- this.date = today;
1183
- this.save();
1184
- }
1185
- // Emit the precise resume line and a fresh today counter line
1186
- this.log.info?.('[API Stats] ✅ Daily API counter reset - Polling resumed');
1187
- this.log.info?.(`[API Stats] Today (${this.date}): ${this.count}/${this.dailyLimit} API requests`);
1188
- }
1189
- catch { }
1190
- // Reschedule for the next midnight
1191
- this.scheduleMidnightRollover();
1192
- }, delay);
1193
- }
1194
- catch { }
1195
- }
1196
- }
1197
- /**
1198
- * Normalize a deviceId for matching (uppercase alphanumerics only)
1199
- */
1200
- export function normalizeDeviceId(deviceId) {
1201
- return (deviceId ?? '').toUpperCase().replace(/[^A-Z0-9]+/g, '');
1202
- }
1203
- /**
1204
- * Merge two arrays by deviceId. For each item in a1 (user-provided devices list),
1205
- * find matching item in a2 (discovered devices) and merge them with user overrides last.
1206
- */
1207
- export function mergeByDeviceId(a1, a2, allowConfigOnly = false) {
1208
- const result = [];
1209
- for (const itm of (a1 || [])) {
1210
- const matchingItem = (a2 || []).find(item => normalizeDeviceId(item.deviceId) === normalizeDeviceId(itm.deviceId));
1211
- if (matchingItem) {
1212
- result.push({ ...matchingItem, ...itm });
1213
- }
1214
- else if (allowConfigOnly) {
1215
- result.push(itm);
1216
- }
1217
- }
1218
- return result;
1219
- }
1220
- /**
1221
- * Apply device-type or remote-type templates to an array of devices.
1222
- * Templates are config entries with applyToAllDevicesOfType=true.
1223
- *
1224
- * @param devices - Array of devices to apply templates to
1225
- * @param configDevices - User config array that may contain template entries
1226
- * @param typeKey - Property name to match device types ('deviceType' for devices, 'remoteType' for IR devices)
1227
- * @param debugLog - Optional debug logging function
1228
- * @returns Array of devices with templates applied
1229
- */
1230
- export function applyDeviceTypeTemplates(devices, configDevices, typeKey, debugLog) {
1231
- // Build a map of device-type templates from config devices with applyToAllDevicesOfType=true
1232
- const typeTemplates = new Map();
1233
- for (const configDevice of configDevices || []) {
1234
- if (configDevice.applyToAllDevicesOfType) {
1235
- // Get the type value from multiple possible sources
1236
- const deviceType = configDevice[typeKey] || configDevice.configDeviceType || configDevice.configRemoteType;
1237
- if (!deviceType) {
1238
- continue;
1239
- }
1240
- // Store all config properties except deviceId and applyToAllDevicesOfType flag
1241
- const template = { ...configDevice };
1242
- delete template.deviceId;
1243
- delete template.applyToAllDevicesOfType;
1244
- typeTemplates.set(deviceType, template);
1245
- if (debugLog) {
1246
- debugLog(`Device type template found for '${deviceType}': ${JSON.stringify(template)}`);
1247
- }
1248
- }
1249
- }
1250
- // If no templates found, return original array
1251
- if (typeTemplates.size === 0) {
1252
- return devices;
1253
- }
1254
- // Apply templates to devices
1255
- return devices.map((device) => {
1256
- const deviceType = device[typeKey] || device.configDeviceType || device.configRemoteType;
1257
- const template = typeTemplates.get(deviceType);
1258
- if (template) {
1259
- if (debugLog) {
1260
- debugLog(`Applying device type template to ${device.deviceId} (${deviceType})`);
1261
- }
1262
- // Template settings go first, then device data (device data takes precedence)
1263
- return Object.assign({}, template, device);
1264
- }
1265
- return device;
1266
- });
1267
- }
1268
- /**
1269
- * Check if an API status code indicates success
1270
- */
1271
- export function isSuccessfulStatusCode(statusCode) {
1272
- return statusCode === 200 || statusCode === 100;
1273
- }
1274
- /**
1275
- * Log status code messages with appropriate log level
1276
- */
1277
- export async function logStatusCode(statusCode, log) {
1278
- const messages = {
1279
- 151: `Command not supported by this device type, statusCode: ${statusCode}, Submit Feature Request Here:
1280
- https://tinyurl.com/SwitchBotFeatureRequest`,
1281
- 152: `Device not found, statusCode: ${statusCode}`,
1282
- 160: `Command is not supported, statusCode: ${statusCode}, Submit Bugs Here: https://tinyurl.com/SwitchBotBug`,
1283
- 161: `Device is offline, statusCode: ${statusCode}`,
1284
- 171: `is offline, statusCode: ${statusCode}`,
1285
- 190: `Requests reached the daily limit, statusCode: ${statusCode}`,
1286
- 100: `Command successfully sent, statusCode: ${statusCode}`,
1287
- 200: `Request successful, statusCode: ${statusCode}`,
1288
- 400: `Bad Request, The client has issued an invalid request. This is commonly used to specify validation errors in a request payload,
1289
- statusCode: ${statusCode}`,
1290
- 401: `Unauthorized, Authorization for the API is required, but the request has not been authenticated, statusCode: ${statusCode}`,
1291
- 403: `Forbidden, The request has been authenticated but does not have appropriate permissions, or a requested resource is not found,
1292
- statusCode: ${statusCode}`,
1293
- 404: `Not Found, Specifies the requested path does not exist, statusCode: ${statusCode}`,
1294
- 406: `Not Acceptable, The client has requested a MIME type via the Accept header for a value not supported by the server,
1295
- statusCode: ${statusCode}`,
1296
- 415: `Unsupported Media Type, The client has defined a contentType header that is not supported by the server, statusCode: ${statusCode}`,
1297
- 422: `Unprocessable Entity, The client has made a valid request, but the server cannot process it. This is often used for APIs for which
1298
- certain limits have been exceeded, statusCode: ${statusCode}`,
1299
- 429: `Too Many Requests, The client has exceeded the number of requests allowed for a given time window, statusCode: ${statusCode}`,
1300
- 500: `Internal Server Error, An unexpected error on the SmartThings servers has occurred. These errors should be rare,
1301
- statusCode: ${statusCode}`,
1302
- };
1303
- const message = messages[statusCode] ?? `Unknown statusCode, statusCode: ${statusCode}, Submit Bugs Here: https://tinyurl.com/SwitchBotBug`;
1304
- if ([100, 200].includes(statusCode)) {
1305
- await log.debugLog(message);
1306
- }
1307
- else {
1308
- await log.errorLog(message);
1309
- }
1310
- }
1311
- /**
1312
- * Shared device logging helpers
1313
- */
1314
- /**
1315
- * Check if device logging is in debug mode
1316
- */
1317
- export function deviceLoggingIsDebug(deviceLogging) {
1318
- return deviceLogging === 'debugMode' || deviceLogging === 'debug';
1319
- }
1320
- /**
1321
- * Check if device logging is enabled
1322
- */
1323
- export function deviceLoggingEnabled(deviceLogging, platformLogging) {
1324
- // If deviceLogging isn't provided, fall back to platform-wide flag
1325
- if (deviceLogging === undefined || deviceLogging === '') {
1326
- return platformLogging === 'debugMode' || platformLogging === 'debug' || platformLogging === 'standard';
1327
- }
1328
- return deviceLogging === 'debugMode' || deviceLogging === 'debug' || deviceLogging === 'standard';
1329
- }
1330
- export async function logDeviceStatusCode(statusCode, log, deviceId, hubDeviceId) {
1331
- let adjustedStatusCode = statusCode;
1332
- // Handle special case where device is its own hub
1333
- if (statusCode === 171 && hubDeviceId && deviceId && (hubDeviceId === deviceId || hubDeviceId === '000000000000')) {
1334
- if (log.debugErrorLog) {
1335
- log.debugErrorLog(`statusCode 171 changed to 161: hubDeviceId ${hubDeviceId} matches deviceId ${deviceId}, device is its own hub.`);
1336
- }
1337
- adjustedStatusCode = 161;
1338
- }
1339
- const statusMessages = {
1340
- 151: 'Command not supported by this device type',
1341
- 152: 'Device not found',
1342
- 160: 'Command is not supported',
1343
- 161: 'Device is offline',
1344
- 171: hubDeviceId ? `Hub Device is offline. Hub: ${hubDeviceId}` : 'Hub Device is offline',
1345
- 190: 'Device internal error due to device states not synchronized with server, or command format is invalid',
1346
- 100: 'Command successfully sent',
1347
- 200: 'Request successful',
1348
- 400: 'Bad Request, an invalid payload request',
1349
- 401: 'Unauthorized, Authorization for the API is required, but the request has not been authenticated',
1350
- 403: 'Forbidden, The request has been authenticated but does not have appropriate permissions, or a requested resource is not found',
1351
- 404: 'Not Found, Specifies the requested path does not exist',
1352
- 406: 'Not Acceptable, a MIME type has been requested via the Accept header for a value not supported by the server',
1353
- 415: 'Unsupported Media Type, a contentType header has been defined that is not supported by the server',
1354
- 422: 'Unprocessable Entity: The server cannot process the request, often due to exceeded API limits.',
1355
- 429: 'Too Many Requests, exceeded the number of requests allowed for a given time window',
1356
- 500: 'Internal Server Error, An unexpected error occurred. These errors should be rare',
1357
42
  };
1358
- const logMessage = statusMessages[adjustedStatusCode] || `Unknown statusCode: ${adjustedStatusCode}, Submit Bugs Here: https://tinyurl.com/SwitchBotBug`;
1359
- const fullMessage = `${logMessage}, statusCode: ${adjustedStatusCode}`;
1360
- if ([100, 200].includes(adjustedStatusCode)) {
1361
- await log.debugLog(fullMessage);
1362
- }
1363
- else if (statusMessages[adjustedStatusCode]) {
1364
- await log.errorLog(fullMessage);
1365
- }
1366
- else if (log.infoLog) {
1367
- await log.infoLog(fullMessage);
1368
- }
1369
- else {
1370
- await log.errorLog(fullMessage);
1371
- }
1372
43
  }
1373
44
  //# sourceMappingURL=utils.js.map