@switchbot/homebridge-switchbot 5.0.0-beta.5 → 5.0.0-beta.51

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 (262) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +45 -3
  3. package/config.schema.json +866 -13754
  4. package/dist/devices-hap/airpurifier.d.ts.map +1 -1
  5. package/dist/devices-hap/airpurifier.js +12 -6
  6. package/dist/devices-hap/airpurifier.js.map +1 -1
  7. package/dist/devices-hap/blindtilt.js +3 -3
  8. package/dist/devices-hap/bot.d.ts.map +1 -1
  9. package/dist/devices-hap/bot.js +16 -5
  10. package/dist/devices-hap/bot.js.map +1 -1
  11. package/dist/devices-hap/ceilinglight.d.ts.map +1 -1
  12. package/dist/devices-hap/ceilinglight.js +13 -7
  13. package/dist/devices-hap/ceilinglight.js.map +1 -1
  14. package/dist/devices-hap/colorbulb.d.ts.map +1 -1
  15. package/dist/devices-hap/colorbulb.js +49 -9
  16. package/dist/devices-hap/colorbulb.js.map +1 -1
  17. package/dist/devices-hap/contact.js +3 -3
  18. package/dist/devices-hap/curtain.js +2 -2
  19. package/dist/devices-hap/curtain.js.map +1 -1
  20. package/dist/devices-hap/device.d.ts +18 -8
  21. package/dist/devices-hap/device.d.ts.map +1 -1
  22. package/dist/devices-hap/device.js +141 -69
  23. package/dist/devices-hap/device.js.map +1 -1
  24. package/dist/devices-hap/fan.d.ts.map +1 -1
  25. package/dist/devices-hap/fan.js +12 -6
  26. package/dist/devices-hap/fan.js.map +1 -1
  27. package/dist/devices-hap/hub.d.ts.map +1 -1
  28. package/dist/devices-hap/hub.js +6 -5
  29. package/dist/devices-hap/hub.js.map +1 -1
  30. package/dist/devices-hap/humidifier.d.ts +5 -0
  31. package/dist/devices-hap/humidifier.d.ts.map +1 -1
  32. package/dist/devices-hap/humidifier.js +92 -4
  33. package/dist/devices-hap/humidifier.js.map +1 -1
  34. package/dist/devices-hap/iosensor.d.ts.map +1 -1
  35. package/dist/devices-hap/iosensor.js +36 -21
  36. package/dist/devices-hap/iosensor.js.map +1 -1
  37. package/dist/devices-hap/lightstrip.d.ts.map +1 -1
  38. package/dist/devices-hap/lightstrip.js +38 -8
  39. package/dist/devices-hap/lightstrip.js.map +1 -1
  40. package/dist/devices-hap/lock.d.ts.map +1 -1
  41. package/dist/devices-hap/lock.js +14 -6
  42. package/dist/devices-hap/lock.js.map +1 -1
  43. package/dist/devices-hap/meter.d.ts.map +1 -1
  44. package/dist/devices-hap/meter.js +6 -5
  45. package/dist/devices-hap/meter.js.map +1 -1
  46. package/dist/devices-hap/meterplus.d.ts.map +1 -1
  47. package/dist/devices-hap/meterplus.js +6 -5
  48. package/dist/devices-hap/meterplus.js.map +1 -1
  49. package/dist/devices-hap/meterpro.d.ts.map +1 -1
  50. package/dist/devices-hap/meterpro.js +7 -6
  51. package/dist/devices-hap/meterpro.js.map +1 -1
  52. package/dist/devices-hap/motion.js +3 -3
  53. package/dist/devices-hap/plug.d.ts.map +1 -1
  54. package/dist/devices-hap/plug.js +11 -6
  55. package/dist/devices-hap/plug.js.map +1 -1
  56. package/dist/devices-hap/relayswitch.js +3 -3
  57. package/dist/devices-hap/robotvacuumcleaner.d.ts.map +1 -1
  58. package/dist/devices-hap/robotvacuumcleaner.js +13 -6
  59. package/dist/devices-hap/robotvacuumcleaner.js.map +1 -1
  60. package/dist/devices-hap/waterdetector.js +3 -3
  61. package/dist/devices-matter/BaseMatterAccessory.d.ts +27 -0
  62. package/dist/devices-matter/BaseMatterAccessory.d.ts.map +1 -1
  63. package/dist/devices-matter/BaseMatterAccessory.js +169 -5
  64. package/dist/devices-matter/BaseMatterAccessory.js.map +1 -1
  65. package/dist/devices-matter/ColorLightAccessory.d.ts.map +1 -1
  66. package/dist/devices-matter/ColorLightAccessory.js +12 -12
  67. package/dist/devices-matter/ColorLightAccessory.js.map +1 -1
  68. package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts.map +1 -1
  69. package/dist/devices-matter/ColorTemperatureLightAccessory.js +5 -7
  70. package/dist/devices-matter/ColorTemperatureLightAccessory.js.map +1 -1
  71. package/dist/devices-matter/DimmableLightAccessory.js +9 -9
  72. package/dist/devices-matter/DimmableLightAccessory.js.map +1 -1
  73. package/dist/devices-matter/ExtendedColorLightAccessory.d.ts.map +1 -1
  74. package/dist/devices-matter/ExtendedColorLightAccessory.js +14 -15
  75. package/dist/devices-matter/ExtendedColorLightAccessory.js.map +1 -1
  76. package/dist/devices-matter/OnOffLightAccessory.d.ts.map +1 -1
  77. package/dist/devices-matter/OnOffLightAccessory.js +8 -16
  78. package/dist/devices-matter/OnOffLightAccessory.js.map +1 -1
  79. package/dist/devices-matter/OnOffOutletAccessory.d.ts +2 -0
  80. package/dist/devices-matter/OnOffOutletAccessory.d.ts.map +1 -1
  81. package/dist/devices-matter/OnOffOutletAccessory.js +10 -7
  82. package/dist/devices-matter/OnOffOutletAccessory.js.map +1 -1
  83. package/dist/devices-matter/OnOffSwitchAccessory.js +2 -2
  84. package/dist/devices-matter/OnOffSwitchAccessory.js.map +1 -1
  85. package/dist/devices-matter/RoboticVacuumAccessory.d.ts +11 -2
  86. package/dist/devices-matter/RoboticVacuumAccessory.d.ts.map +1 -1
  87. package/dist/devices-matter/RoboticVacuumAccessory.js +26 -17
  88. package/dist/devices-matter/RoboticVacuumAccessory.js.map +1 -1
  89. package/dist/homebridge-ui/public/index.html +64 -2
  90. package/dist/homebridge-ui/server.js +77 -9
  91. package/dist/homebridge-ui/server.js.map +1 -1
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +4 -7
  94. package/dist/index.js.map +1 -1
  95. package/dist/irdevice/irdevice.d.ts +11 -10
  96. package/dist/irdevice/irdevice.d.ts.map +1 -1
  97. package/dist/irdevice/irdevice.js +76 -35
  98. package/dist/irdevice/irdevice.js.map +1 -1
  99. package/dist/platform-hap.d.ts +26 -15
  100. package/dist/platform-hap.d.ts.map +1 -1
  101. package/dist/platform-hap.js +333 -153
  102. package/dist/platform-hap.js.map +1 -1
  103. package/dist/platform-matter.d.ts +98 -6
  104. package/dist/platform-matter.d.ts.map +1 -1
  105. package/dist/platform-matter.js +1885 -253
  106. package/dist/platform-matter.js.map +1 -1
  107. package/dist/settings.d.ts +58 -7
  108. package/dist/settings.d.ts.map +1 -1
  109. package/dist/settings.js.map +1 -1
  110. package/dist/test/apiRequestTracker.test.d.ts +2 -0
  111. package/dist/test/apiRequestTracker.test.d.ts.map +1 -0
  112. package/dist/test/apiRequestTracker.test.js +392 -0
  113. package/dist/test/apiRequestTracker.test.js.map +1 -0
  114. package/dist/test/hap/device-webhook-context.test.d.ts +2 -0
  115. package/dist/test/hap/device-webhook-context.test.d.ts.map +1 -0
  116. package/dist/test/hap/device-webhook-context.test.js +128 -0
  117. package/dist/test/hap/device-webhook-context.test.js.map +1 -0
  118. package/dist/test/hap/platform-hap.logging.test.d.ts +2 -0
  119. package/dist/test/hap/platform-hap.logging.test.d.ts.map +1 -0
  120. package/dist/test/hap/platform-hap.logging.test.js +33 -0
  121. package/dist/test/hap/platform-hap.logging.test.js.map +1 -0
  122. package/dist/test/hap/platform-hap.test.d.ts +2 -0
  123. package/dist/test/hap/platform-hap.test.d.ts.map +1 -0
  124. package/dist/test/hap/platform-hap.test.js +62 -0
  125. package/dist/test/hap/platform-hap.test.js.map +1 -0
  126. package/dist/test/helpers/platform-fixtures.d.ts +9 -0
  127. package/dist/test/helpers/platform-fixtures.d.ts.map +1 -0
  128. package/dist/test/helpers/platform-fixtures.js +30 -0
  129. package/dist/test/helpers/platform-fixtures.js.map +1 -0
  130. package/dist/test/homebridge-ui/server.test.d.ts +2 -0
  131. package/dist/test/homebridge-ui/server.test.d.ts.map +1 -0
  132. package/dist/test/homebridge-ui/server.test.js +445 -0
  133. package/dist/test/homebridge-ui/server.test.js.map +1 -0
  134. package/dist/{index.test.d.ts.map → test/index.test.d.ts.map} +1 -1
  135. package/dist/test/index.test.js +19 -0
  136. package/dist/test/index.test.js.map +1 -0
  137. package/dist/test/matter/devices-matter/baseMatterAccessory.test.d.ts +2 -0
  138. package/dist/test/matter/devices-matter/baseMatterAccessory.test.d.ts.map +1 -0
  139. package/dist/test/matter/devices-matter/baseMatterAccessory.test.js +71 -0
  140. package/dist/test/matter/devices-matter/baseMatterAccessory.test.js.map +1 -0
  141. package/dist/test/matter/platform-matter.additional.test.d.ts +2 -0
  142. package/dist/test/matter/platform-matter.additional.test.d.ts.map +1 -0
  143. package/dist/test/matter/platform-matter.additional.test.js +35 -0
  144. package/dist/test/matter/platform-matter.additional.test.js.map +1 -0
  145. package/dist/test/matter/platform-matter.bleparse.test.d.ts +2 -0
  146. package/dist/test/matter/platform-matter.bleparse.test.d.ts.map +1 -0
  147. package/dist/test/matter/platform-matter.bleparse.test.js +43 -0
  148. package/dist/test/matter/platform-matter.bleparse.test.js.map +1 -0
  149. package/dist/test/matter/platform-matter.cleanup.test.d.ts +2 -0
  150. package/dist/test/matter/platform-matter.cleanup.test.d.ts.map +1 -0
  151. package/dist/test/matter/platform-matter.cleanup.test.js +70 -0
  152. package/dist/test/matter/platform-matter.cleanup.test.js.map +1 -0
  153. package/dist/test/matter/platform-matter.keepstale.test.d.ts +2 -0
  154. package/dist/test/matter/platform-matter.keepstale.test.d.ts.map +1 -0
  155. package/dist/test/matter/platform-matter.keepstale.test.js +27 -0
  156. package/dist/test/matter/platform-matter.keepstale.test.js.map +1 -0
  157. package/dist/test/matter/platform-matter.logging.test.d.ts +2 -0
  158. package/dist/test/matter/platform-matter.logging.test.d.ts.map +1 -0
  159. package/dist/test/matter/platform-matter.logging.test.js +29 -0
  160. package/dist/test/matter/platform-matter.logging.test.js.map +1 -0
  161. package/dist/test/matter/platform-matter.mapping.test.d.ts +2 -0
  162. package/dist/test/matter/platform-matter.mapping.test.d.ts.map +1 -0
  163. package/dist/test/matter/platform-matter.mapping.test.js +43 -0
  164. package/dist/test/matter/platform-matter.mapping.test.js.map +1 -0
  165. package/dist/test/matter/platform-matter.openapi-mapping.test.d.ts +2 -0
  166. package/dist/test/matter/platform-matter.openapi-mapping.test.d.ts.map +1 -0
  167. package/dist/test/matter/platform-matter.openapi-mapping.test.js +84 -0
  168. package/dist/test/matter/platform-matter.openapi-mapping.test.js.map +1 -0
  169. package/dist/test/matter/platform-matter.test.d.ts +2 -0
  170. package/dist/test/matter/platform-matter.test.d.ts.map +1 -0
  171. package/dist/test/matter/platform-matter.test.js +117 -0
  172. package/dist/test/matter/platform-matter.test.js.map +1 -0
  173. package/dist/test/matter/platform-matter.unregister.test.d.ts +2 -0
  174. package/dist/test/matter/platform-matter.unregister.test.d.ts.map +1 -0
  175. package/dist/test/matter/platform-matter.unregister.test.js +30 -0
  176. package/dist/test/matter/platform-matter.unregister.test.js.map +1 -0
  177. package/dist/test/matter/platform-matter.webhook.test.d.ts +2 -0
  178. package/dist/test/matter/platform-matter.webhook.test.d.ts.map +1 -0
  179. package/dist/test/matter/platform-matter.webhook.test.js +46 -0
  180. package/dist/test/matter/platform-matter.webhook.test.js.map +1 -0
  181. package/dist/test/utils.test.d.ts +2 -0
  182. package/dist/test/utils.test.d.ts.map +1 -0
  183. package/dist/test/utils.test.js +95 -0
  184. package/dist/test/utils.test.js.map +1 -0
  185. package/dist/test/verifyconfig.test.d.ts.map +1 -0
  186. package/dist/{verifyconfig.test.js → test/verifyconfig.test.js} +2 -2
  187. package/dist/test/verifyconfig.test.js.map +1 -0
  188. package/dist/utils.d.ts +204 -3
  189. package/dist/utils.d.ts.map +1 -1
  190. package/dist/utils.js +713 -33
  191. package/dist/utils.js.map +1 -1
  192. package/docs/assets/highlight.css +14 -0
  193. package/docs/assets/main.js +2 -2
  194. package/docs/index.html +31 -2
  195. package/docs/variables/default.html +1 -1
  196. package/package.json +15 -15
  197. package/src/devices-hap/airpurifier.ts +11 -6
  198. package/src/devices-hap/blindtilt.ts +3 -3
  199. package/src/devices-hap/bot.ts +15 -5
  200. package/src/devices-hap/ceilinglight.ts +12 -7
  201. package/src/devices-hap/colorbulb.ts +46 -10
  202. package/src/devices-hap/contact.ts +3 -3
  203. package/src/devices-hap/curtain.ts +2 -2
  204. package/src/devices-hap/device.ts +149 -70
  205. package/src/devices-hap/fan.ts +11 -6
  206. package/src/devices-hap/hub.ts +6 -5
  207. package/src/devices-hap/humidifier.ts +97 -4
  208. package/src/devices-hap/iosensor.ts +36 -21
  209. package/src/devices-hap/lightstrip.ts +35 -8
  210. package/src/devices-hap/lock.ts +13 -6
  211. package/src/devices-hap/meter.ts +6 -5
  212. package/src/devices-hap/meterplus.ts +6 -5
  213. package/src/devices-hap/meterpro.ts +7 -6
  214. package/src/devices-hap/motion.ts +3 -3
  215. package/src/devices-hap/plug.ts +10 -6
  216. package/src/devices-hap/relayswitch.ts +3 -3
  217. package/src/devices-hap/robotvacuumcleaner.ts +12 -6
  218. package/src/devices-hap/waterdetector.ts +3 -3
  219. package/src/devices-matter/BaseMatterAccessory.ts +176 -5
  220. package/src/devices-matter/ColorLightAccessory.ts +12 -12
  221. package/src/devices-matter/ColorTemperatureLightAccessory.ts +5 -7
  222. package/src/devices-matter/DimmableLightAccessory.ts +9 -9
  223. package/src/devices-matter/ExtendedColorLightAccessory.ts +14 -15
  224. package/src/devices-matter/OnOffLightAccessory.ts +8 -16
  225. package/src/devices-matter/OnOffOutletAccessory.ts +12 -7
  226. package/src/devices-matter/OnOffSwitchAccessory.ts +2 -2
  227. package/src/devices-matter/RoboticVacuumAccessory.ts +27 -17
  228. package/src/homebridge-ui/public/index.html +64 -2
  229. package/src/homebridge-ui/server.ts +80 -9
  230. package/src/index.ts +4 -7
  231. package/src/irdevice/irdevice.ts +74 -35
  232. package/src/platform-hap.ts +365 -169
  233. package/src/platform-matter.ts +1938 -256
  234. package/src/settings.ts +62 -3
  235. package/src/test/apiRequestTracker.test.ts +417 -0
  236. package/src/test/hap/device-webhook-context.test.ts +136 -0
  237. package/src/test/hap/platform-hap.logging.test.ts +36 -0
  238. package/src/test/hap/platform-hap.test.ts +70 -0
  239. package/src/test/helpers/platform-fixtures.ts +33 -0
  240. package/src/test/homebridge-ui/server.test.ts +486 -0
  241. package/src/test/index.test.ts +24 -0
  242. package/src/test/matter/devices-matter/baseMatterAccessory.test.ts +88 -0
  243. package/src/test/matter/platform-matter.additional.test.ts +44 -0
  244. package/src/test/matter/platform-matter.bleparse.test.ts +47 -0
  245. package/src/test/matter/platform-matter.cleanup.test.ts +86 -0
  246. package/src/test/matter/platform-matter.keepstale.test.ts +37 -0
  247. package/src/test/matter/platform-matter.logging.test.ts +33 -0
  248. package/src/test/matter/platform-matter.mapping.test.ts +57 -0
  249. package/src/test/matter/platform-matter.openapi-mapping.test.ts +109 -0
  250. package/src/test/matter/platform-matter.test.ts +144 -0
  251. package/src/test/matter/platform-matter.unregister.test.ts +39 -0
  252. package/src/test/matter/platform-matter.webhook.test.ts +54 -0
  253. package/src/test/utils.test.ts +96 -0
  254. package/src/{verifyconfig.test.ts → test/verifyconfig.test.ts} +12 -11
  255. package/src/utils.ts +777 -36
  256. package/dist/index.test.js +0 -14
  257. package/dist/index.test.js.map +0 -1
  258. package/dist/verifyconfig.test.d.ts.map +0 -1
  259. package/dist/verifyconfig.test.js.map +0 -1
  260. package/src/index.test.ts +0 -19
  261. /package/dist/{index.test.d.ts → test/index.test.d.ts} +0 -0
  262. /package/dist/{verifyconfig.test.d.ts → test/verifyconfig.test.d.ts} +0 -0
@@ -5,6 +5,7 @@
5
5
 
6
6
  import type { API, Logger, MatterRequests } from 'homebridge'
7
7
 
8
+ import { hs2rgb } from '../utils.js'
8
9
  import { BaseMatterAccessory } from './BaseMatterAccessory.js'
9
10
 
10
11
  export class ColorLightAccessory extends BaseMatterAccessory {
@@ -56,38 +57,37 @@ export class ColorLightAccessory extends BaseMatterAccessory {
56
57
 
57
58
  private async handleOn(): Promise<void> {
58
59
  this.logInfo('turning on.')
59
- // TODO: await myLightAPI.turnOn()
60
+ await this.sendOnCommand()
60
61
  }
61
62
 
62
63
  private async handleOff(): Promise<void> {
63
64
  this.logInfo('turning off.')
64
- // TODO: await myLightAPI.turnOff()
65
+ await this.sendOffCommand()
65
66
  }
66
67
 
67
68
  private async handleSetLevel(request: MatterRequests.MoveToLevel): Promise<void> {
68
69
  this.logInfo(`MoveToLevel request: ${JSON.stringify(request)}`)
69
70
  const { level } = request
70
71
  const brightnessPercent = Math.round((level / 254) * 100)
71
- this.logInfo(`setting brightness to ${brightnessPercent}%.`)
72
- // TODO: await myLightAPI.setBrightness(brightnessPercent)
72
+ await this.sendSetBrightness(brightnessPercent)
73
73
  }
74
74
 
75
75
  private async handleSetColor(request: MatterRequests.MoveToColor): Promise<void> {
76
76
  this.logInfo(`MoveToColor request: ${JSON.stringify(request)}`)
77
- const { colorX, colorY, transitionTime } = request
78
- const xFloat = (colorX / 65535).toFixed(4)
79
- const yFloat = (colorY / 65535).toFixed(4)
80
- this.logInfo(`setting xy color to (${xFloat}, ${yFloat}) with transition ${transitionTime}.`)
81
- // TODO: await myLightAPI.setXY(xFloat, yFloat, transitionTime)
77
+ const { colorX, colorY } = request
78
+ const hueApprox = Math.round((colorX / 65535) * 360)
79
+ const satApprox = Math.round((colorY / 65535) * 100)
80
+ const [r, g, b] = hs2rgb(hueApprox, satApprox)
81
+ await this.sendSetColor(r, g, b)
82
82
  }
83
83
 
84
84
  private async handleSetHueSaturation(request: MatterRequests.MoveToHueAndSaturation): Promise<void> {
85
85
  this.logInfo(`MoveToHueAndSaturation request: ${JSON.stringify(request)}`)
86
- const { hue, saturation, transitionTime } = request
86
+ const { hue, saturation } = request
87
87
  const hueDegrees = Math.round((hue / 254) * 360)
88
88
  const saturationPercent = Math.round((saturation / 254) * 100)
89
- this.logInfo(`setting color to ${hueDegrees}°, ${saturationPercent}% with transition ${transitionTime}.`)
90
- // TODO: await myLightAPI.setColor(hueDegrees, saturationPercent, transitionTime)
89
+ const [r, g, b] = hs2rgb(hueDegrees, saturationPercent)
90
+ await this.sendSetColor(r, g, b)
91
91
  }
92
92
 
93
93
  public updateOnOffState(isOn: boolean): void {
@@ -52,28 +52,26 @@ export class ColorTemperatureLightAccessory extends BaseMatterAccessory {
52
52
 
53
53
  private async handleOn(): Promise<void> {
54
54
  this.logInfo('turning on.')
55
- // TODO: await myLightAPI.turnOn()
55
+ await this.sendOnCommand()
56
56
  }
57
57
 
58
58
  private async handleOff(): Promise<void> {
59
59
  this.logInfo('turning off.')
60
- // TODO: await myLightAPI.turnOff()
60
+ await this.sendOffCommand()
61
61
  }
62
62
 
63
63
  private async handleSetLevel(request: MatterRequests.MoveToLevel): Promise<void> {
64
64
  this.logInfo(`MoveToLevel request: ${JSON.stringify(request)}`)
65
65
  const { level } = request
66
66
  const brightnessPercent = Math.round((level / 254) * 100)
67
- this.logInfo(`setting brightness to ${brightnessPercent}% (level: ${level}).`)
68
- // TODO: await myLightAPI.setBrightness(brightnessPercent)
67
+ await this.sendSetBrightness(brightnessPercent)
69
68
  }
70
69
 
71
70
  private async handleSetColorTemperature(request: MatterRequests.MoveToColorTemperature): Promise<void> {
72
71
  this.logInfo(`MoveToColorTemperature request: ${JSON.stringify(request)}`)
73
- const { colorTemperatureMireds, transitionTime } = request
72
+ const { colorTemperatureMireds } = request
74
73
  const kelvin = Math.round(1000000 / colorTemperatureMireds)
75
- this.logInfo(`setting color temp to ${kelvin}k (${colorTemperatureMireds} mireds) with transitionTime=${transitionTime}.`)
76
- // TODO: await myLightAPI.setColorTemperature(kelvin, transitionTime)
74
+ await this.sendSetColorTemperature(kelvin)
77
75
  }
78
76
 
79
77
  public updateOnOffState(isOn: boolean): void {
@@ -69,10 +69,10 @@ export class DimmableLightAccessory extends BaseMatterAccessory {
69
69
  this.logInfo('turning on.')
70
70
 
71
71
  try {
72
- // TODO: Control your physical device
73
- // await myLightAPI.turnOn()
72
+ // Use platform helper (OpenAPI/BLE) when available
73
+ await this.sendOnCommand()
74
74
 
75
- this.logInfo('physical device turned on.')
75
+ this.logInfo('physical device turned on (via platform helper).')
76
76
  } catch (error) {
77
77
  this.logError('failed to turn on:', error)
78
78
  throw error
@@ -86,10 +86,10 @@ export class DimmableLightAccessory extends BaseMatterAccessory {
86
86
  this.logInfo('turning off.')
87
87
 
88
88
  try {
89
- // TODO: Control your physical device
90
- // await myLightAPI.turnOff()
89
+ // Use platform helper (OpenAPI/BLE) when available
90
+ await this.sendOffCommand()
91
91
 
92
- this.logInfo('physical device turned off.')
92
+ this.logInfo('physical device turned off (via platform helper).')
93
93
  } catch (error) {
94
94
  this.logError('failed to turn off:', error)
95
95
  throw error
@@ -110,10 +110,10 @@ export class DimmableLightAccessory extends BaseMatterAccessory {
110
110
  this.logInfo(`setting brightness to ${brightnessPercent}% (level: ${level}), transitionTime: ${transitionTime}.`)
111
111
 
112
112
  try {
113
- // TODO: Control your physical device
114
- // await myLightAPI.setBrightness(brightnessPercent, transitionTime)
113
+ // Use platform helper (OpenAPI/BLE) when available
114
+ await this.sendSetBrightness(brightnessPercent)
115
115
 
116
- this.logInfo(`physical device brightness set to ${brightnessPercent}%.`)
116
+ this.logInfo(`physical device brightness set to ${brightnessPercent}% (via platform helper).`)
117
117
  } catch (error) {
118
118
  this.logError('Failed to set brightness:', error)
119
119
  throw error
@@ -5,6 +5,7 @@
5
5
 
6
6
  import type { API, Logger, MatterRequests } from 'homebridge'
7
7
 
8
+ import { hs2rgb } from '../utils.js'
8
9
  import { BaseMatterAccessory } from './BaseMatterAccessory.js'
9
10
 
10
11
  export class ExtendedColorLightAccessory extends BaseMatterAccessory {
@@ -61,46 +62,44 @@ export class ExtendedColorLightAccessory extends BaseMatterAccessory {
61
62
 
62
63
  private async handleOn(): Promise<void> {
63
64
  this.logInfo('turning on.')
64
- // TODO: await myLightAPI.turnOn()
65
+ await this.sendOnCommand()
65
66
  }
66
67
 
67
68
  private async handleOff(): Promise<void> {
68
69
  this.logInfo('turning off.')
69
- // TODO: await myLightAPI.turnOff()
70
+ await this.sendOffCommand()
70
71
  }
71
72
 
72
73
  private async handleSetLevel(request: MatterRequests.MoveToLevel): Promise<void> {
73
74
  this.logInfo(`MoveToLevel request: ${JSON.stringify(request)}`)
74
75
  const { level } = request
75
76
  const brightnessPercent = Math.round((level / 254) * 100)
76
- this.logInfo(`setting brightness to ${brightnessPercent}%.`)
77
- // TODO: await myLightAPI.setBrightness(brightnessPercent)
77
+ await this.sendSetBrightness(brightnessPercent)
78
78
  }
79
79
 
80
80
  private async handleSetColor(request: MatterRequests.MoveToColor): Promise<void> {
81
81
  this.logInfo(`MoveToColor request: ${JSON.stringify(request)}`)
82
- const { colorX, colorY, transitionTime } = request
83
- const xFloat = (colorX / 65535).toFixed(4)
84
- const yFloat = (colorY / 65535).toFixed(4)
85
- this.logInfo(`setting xy color to (${xFloat}, ${yFloat}), ${transitionTime}ms.`)
86
- // TODO: await myLightAPI.setXY(xFloat, yFloat, transitionTime)
82
+ const { colorX, colorY } = request
83
+ const hueApprox = Math.round((colorX / 65535) * 360)
84
+ const satApprox = Math.round((colorY / 65535) * 100)
85
+ const [r, g, b] = hs2rgb(hueApprox, satApprox)
86
+ await this.sendSetColor(r, g, b)
87
87
  }
88
88
 
89
89
  private async handleSetHueSaturation(request: MatterRequests.MoveToHueAndSaturation): Promise<void> {
90
90
  this.logInfo(`MoveToHueAndSaturation request: ${JSON.stringify(request)}`)
91
- const { hue, saturation, transitionTime } = request
91
+ const { hue, saturation } = request
92
92
  const hueDegrees = Math.round((hue / 254) * 360)
93
93
  const saturationPercent = Math.round((saturation / 254) * 100)
94
- this.logInfo(`setting color to ${hueDegrees}°, ${saturationPercent}%, ${transitionTime}ms.`)
95
- // TODO: await myLightAPI.setColor(hueDegrees, saturationPercent, transitionTime)
94
+ const [r, g, b] = hs2rgb(hueDegrees, saturationPercent)
95
+ await this.sendSetColor(r, g, b)
96
96
  }
97
97
 
98
98
  private async handleSetColorTemperature(request: MatterRequests.MoveToColorTemperature): Promise<void> {
99
99
  this.logInfo(`MoveToColorTemperature request: ${JSON.stringify(request)}`)
100
- const { colorTemperatureMireds, transitionTime } = request
100
+ const { colorTemperatureMireds } = request
101
101
  const kelvin = Math.round(1000000 / colorTemperatureMireds)
102
- this.logInfo(`setting color temp to ${kelvin}k, ${transitionTime}ms.`)
103
- // TODO: await myLightAPI.setColorTemperature(kelvin, transitionTime)
102
+ await this.sendSetColorTemperature(kelvin)
104
103
  }
105
104
 
106
105
  public updateOnOffState(isOn: boolean): void {
@@ -27,12 +27,8 @@ export class OnOffLightAccessory extends BaseMatterAccessory {
27
27
  const clusters = opts?.clusters ?? { onOff: { onOff: true } }
28
28
  const handlers = opts?.handlers ?? {
29
29
  onOff: {
30
- on: async () => {
31
- log.debug(`${displayName} on handler invoked (default no-op).`)
32
- },
33
- off: async () => {
34
- log.debug(`${displayName} off handler invoked (default no-op).`)
35
- },
30
+ on: async () => this.handleOnCommand(),
31
+ off: async () => this.handleOffCommand(),
36
32
  },
37
33
  }
38
34
 
@@ -61,14 +57,10 @@ export class OnOffLightAccessory extends BaseMatterAccessory {
61
57
  this.logInfo('turning on.')
62
58
 
63
59
  try {
64
- // TODO: Control your physical device here
65
- // Examples:
66
- // await fetch('https://api.mydevice.com/light/on', { method: 'POST' })
67
- // await fetch('http://192.168.1.50/api/light/on')
68
- // mqttClient.publish('home/light/command', JSON.stringify({ state: 'ON' }))
69
- // await myLightAPI.turnOn(this.context.deviceId)
60
+ // Delegate to the platform-provided helper (OpenAPI/BLE) when available
61
+ await this.sendOnCommand()
70
62
 
71
- this.logInfo('physical device turned on.')
63
+ this.logInfo('physical device turned on (via platform helper).')
72
64
 
73
65
  // State automatically updated by Homebridge after handler completes
74
66
  } catch (error) {
@@ -84,10 +76,10 @@ export class OnOffLightAccessory extends BaseMatterAccessory {
84
76
  this.logInfo('turning off.')
85
77
 
86
78
  try {
87
- // TODO: Control your physical device here
88
- // await myLightAPI.turnOff(this.context.deviceId)
79
+ // Delegate to the platform-provided helper (OpenAPI/BLE) when available
80
+ await this.sendOffCommand()
89
81
 
90
- this.logInfo('physical device turned off.')
82
+ this.logInfo('physical device turned off (via platform helper).')
91
83
 
92
84
  // State automatically updated by Homebridge after handler completes
93
85
  } catch (error) {
@@ -13,13 +13,8 @@ export class OnOffOutletAccessory extends BaseMatterAccessory {
13
13
  const clusters = opts?.clusters ?? { onOff: { onOff: false } }
14
14
  const handlers = opts?.handlers ?? {
15
15
  onOff: {
16
- on: async () => {
17
- // default no-op; platform may inject handlers via opts.handlers
18
- log.debug(`${displayName} on handler invoked (default no-op).`)
19
- },
20
- off: async () => {
21
- log.debug(`${displayName} off handler invoked (default no-op).`)
22
- },
16
+ on: async () => this.handleOn(),
17
+ off: async () => this.handleOff(),
23
18
  },
24
19
  }
25
20
 
@@ -43,4 +38,14 @@ export class OnOffOutletAccessory extends BaseMatterAccessory {
43
38
  public updateOnOffState(isOn: boolean): void {
44
39
  this.updateState(this.api.matter.clusterNames.OnOff, { onOff: isOn })
45
40
  }
41
+
42
+ private async handleOn(): Promise<void> {
43
+ this.logInfo('turning on.')
44
+ await this.sendOnCommand()
45
+ }
46
+
47
+ private async handleOff(): Promise<void> {
48
+ this.logInfo('turning off.')
49
+ await this.sendOffCommand()
50
+ }
46
51
  }
@@ -37,12 +37,12 @@ export class OnOffSwitchAccessory extends BaseMatterAccessory {
37
37
 
38
38
  private async handleOn(): Promise<void> {
39
39
  this.logInfo('turning on.')
40
- // TODO: await mySwitchAPI.turnOn()
40
+ await this.sendOnCommand()
41
41
  }
42
42
 
43
43
  private async handleOff(): Promise<void> {
44
44
  this.logInfo('turning off.')
45
- // TODO: await mySwitchAPI.turnOff()
45
+ await this.sendOffCommand()
46
46
  }
47
47
 
48
48
  public updateOnOffState(isOn: boolean): void {
@@ -33,14 +33,6 @@ export class RoboticVacuumAccessory extends BaseMatterAccessory {
33
33
  constructor(api: API, log: Logger, opts?: Partial<import('./BaseMatterAccessory').BaseMatterAccessoryConfig & { deviceId?: string }>) {
34
34
  const serialNumber = opts?.serialNumber ?? 'VACUUM-001'
35
35
  const clusters = opts?.clusters ?? {
36
- powerSource: {
37
- status: 0,
38
- order: 0,
39
- description: 'Battery',
40
- batPercentRemaining: 100,
41
- batChargeLevel: 2,
42
- batReplaceability: 1,
43
- },
44
36
  rvcRunMode: {
45
37
  supportedModes: [
46
38
  { label: 'Idle', mode: 0, modeTags: [{ value: 16384 }] },
@@ -387,21 +379,39 @@ export class RoboticVacuumAccessory extends BaseMatterAccessory {
387
379
 
388
380
  /**
389
381
  * Update battery percentage
390
- * @param percentage - Battery percentage (0-100)
382
+ *
383
+ * Note: The Matter specification for RoboticVacuumCleaner does not include
384
+ * the PowerSource cluster. Battery information for robotic vacuums should be
385
+ * communicated through device-specific status reporting or via the RVC
386
+ * operational state cluster.
387
+ *
388
+ * This method is retained for API compatibility but does not update any
389
+ * Matter cluster state.
390
+ *
391
+ * @param percentageOrBatPercentRemaining - Battery percentage (0-100) or batPercentRemaining (0-200)
391
392
  */
392
- public async updateBatteryPercentage(percentage: number): Promise<void> {
393
- // Convert percentage to Matter format (0-200, where 200 = 100%)
394
- const batPercentRemaining = Math.max(0, Math.min(200, Math.round(percentage * 2)))
393
+ public async updateBatteryPercentage(percentageOrBatPercentRemaining: number): Promise<void> {
394
+ // Accept either 0–100 (percentage) or 0200 (batPercentRemaining) to be robust
395
+ const isBatPercent = Number(percentageOrBatPercentRemaining) > 100
396
+ const percentage = isBatPercent
397
+ ? Math.max(0, Math.min(100, Math.round(Number(percentageOrBatPercentRemaining) / 2)))
398
+ : Math.max(0, Math.min(100, Math.round(Number(percentageOrBatPercentRemaining))))
395
399
 
396
400
  // Determine charge level based on percentage
397
- let batChargeLevel = 0 // Ok
401
+ let chargeLevel = 0 // Ok
398
402
  if (percentage < 20) {
399
- batChargeLevel = 2 // Critical
403
+ chargeLevel = 2 // Critical
400
404
  } else if (percentage < 40) {
401
- batChargeLevel = 1 // Warning
405
+ chargeLevel = 1 // Warning
402
406
  }
403
407
 
404
- await this.updateState('powerSource', { batPercentRemaining, batChargeLevel })
405
- this.logInfo(`battery updated to: ${percentage}% (${batChargeLevel === 0 ? 'Ok' : batChargeLevel === 1 ? 'Warning' : 'Critical'})`)
408
+ // Log battery status only - PowerSource cluster is not supported for RoboticVacuumCleaner
409
+ this.logInfo(`battery status: ${percentage}% (${chargeLevel === 0 ? 'Ok' : chargeLevel === 1 ? 'Warning' : 'Critical'})`)
410
+
411
+ // Store battery info in context for reference
412
+ if (this.context) {
413
+ (this.context as any).batteryPercentage = percentage
414
+ ;(this.context as any).batteryChargeLevel = chargeLevel
415
+ }
406
416
  }
407
417
  }
@@ -43,7 +43,7 @@
43
43
  </form>
44
44
  <table class="table w-100" id="deviceTable" style="display: none">
45
45
  <thead>
46
- <tr class="table-active">
46
+ <tr>
47
47
  <th scope="col" style="width: 40%">Device Name</th>
48
48
  <th scope="col" style="width: 60%" id="displayName"></th>
49
49
  </tr>
@@ -69,6 +69,18 @@
69
69
  <th scope="row">Connection Type</th>
70
70
  <td id="connectionType"></td>
71
71
  </tr>
72
+ <tr>
73
+ <th scope="row">Refresh Rate</th>
74
+ <td id="refreshRate"></td>
75
+ </tr>
76
+ <tr>
77
+ <th scope="row">Update Rate</th>
78
+ <td id="updateRate"></td>
79
+ </tr>
80
+ <tr>
81
+ <th scope="row">Push Rate</th>
82
+ <td id="pushRate"></td>
83
+ </tr>
72
84
  </tbody>
73
85
  </table>
74
86
  <p class="text-center">External accessories will not be displayed here.</p>
@@ -122,6 +134,41 @@
122
134
  (async () => {
123
135
  try {
124
136
  const currentConfig = await homebridge.getPluginConfig();
137
+
138
+ // Defensive wrapper: ensure token/secret aren't accidentally cleared by
139
+ // the UI/schema form. Some versions of config UI can omit sensitive
140
+ // fields from the submitted payload; when that happens we copy the
141
+ // existing values from `currentConfig` into the outgoing payload so
142
+ // saved config does not inadvertently remove credentials.
143
+ try {
144
+ if (typeof homebridge.updatePluginConfig === 'function') {
145
+ const _origUpdatePluginConfig = homebridge.updatePluginConfig.bind(homebridge)
146
+ homebridge.updatePluginConfig = async (cfg) => {
147
+ try {
148
+ if (Array.isArray(cfg) && cfg.length > 0 && Array.isArray(currentConfig) && currentConfig.length > 0) {
149
+ const incoming = cfg[0] || {}
150
+ const existing = currentConfig[0] || {}
151
+ incoming.credentials = incoming.credentials || {}
152
+ // Preserve token/secret when incoming payload leaves them blank/undefined
153
+ if ((incoming.credentials.token === undefined || String(incoming.credentials.token).trim() === '') && existing.credentials && existing.credentials.token) {
154
+ incoming.credentials.token = existing.credentials.token
155
+ }
156
+ if ((incoming.credentials.secret === undefined || String(incoming.credentials.secret).trim() === '') && existing.credentials && existing.credentials.secret) {
157
+ incoming.credentials.secret = existing.credentials.secret
158
+ }
159
+ cfg[0] = incoming
160
+ }
161
+ } catch (e) {
162
+ // Swallow any wrapper errors but log to console for debugging
163
+ // (do not expose secrets).
164
+ console.error('updatePluginConfig wrapper error', e)
165
+ }
166
+ return await _origUpdatePluginConfig(cfg)
167
+ }
168
+ }
169
+ } catch (e) {
170
+ console.error('Failed to attach updatePluginConfig wrapper', e)
171
+ }
125
172
  showIntro = () => {
126
173
  const introLink = document.getElementById('introLink');
127
174
  introLink.addEventListener('click', () => {
@@ -144,10 +191,25 @@
144
191
  document.getElementById('menuSettings').classList.add('btn-primary');
145
192
  document.getElementById('pageSupport').style.display = 'none';
146
193
  document.getElementById('pageDevices').style.display = 'block';
147
- const cachedAccessories =
194
+ let cachedAccessories =
148
195
  typeof homebridge.getCachedAccessories === 'function'
149
196
  ? await homebridge.getCachedAccessories()
150
197
  : await homebridge.request('/getCachedAccessories');
198
+
199
+ // If no HAP cached accessories were returned, try the Matter cached list using the new native method
200
+ if ((!cachedAccessories || cachedAccessories.length === 0)) {
201
+ try {
202
+ // Use the new native getCachedMatterAccessories() method from @homebridge/plugin-ui-utils v2.1.1-beta.0+
203
+ const matter = typeof homebridge.getCachedMatterAccessories === 'function'
204
+ ? await homebridge.getCachedMatterAccessories()
205
+ : await homebridge.request('/getCachedMatterAccessories'); // Fallback to custom handler for older UI versions
206
+ if (Array.isArray(matter) && matter.length > 0) {
207
+ cachedAccessories = matter
208
+ }
209
+ } catch (e) {
210
+ // ignore
211
+ }
212
+ }
151
213
  if (cachedAccessories.length > 0) {
152
214
  cachedAccessories.sort((a, b) => {
153
215
  return a.displayName.toLowerCase() > b.displayName.toLowerCase() ? 1 : b.displayName.toLowerCase() > a.displayName.toLowerCase() ? -1 : 0;
@@ -9,9 +9,12 @@ class PluginUiServer extends HomebridgePluginUiServer {
9
9
  A native method getCachedAccessories() was introduced in config-ui-x v4.37.0
10
10
  The following is for users who have a lower version of config-ui-x
11
11
  */
12
- this.onRequest('getCachedAccessories', () => {
12
+ const getCachedAccessoriesHandler = () => {
13
13
  try {
14
- const plugin = 'homebridge-switchbot'
14
+ // Some Homebridge versions store cached accessories with the scoped
15
+ // plugin name ("@switchbot/homebridge-switchbot"); others may use
16
+ // the unscoped id ("homebridge-switchbot"). Check both.
17
+ const pluginNames = ['@switchbot/homebridge-switchbot', 'homebridge-switchbot']
15
18
  const devicesToReturn = []
16
19
 
17
20
  // The path and file of the cached accessories
@@ -22,21 +25,89 @@ class PluginUiServer extends HomebridgePluginUiServer {
22
25
  // read the cached accessories file
23
26
  const cachedAccessories: any[] = JSON.parse(fs.readFileSync(accFile, 'utf8'))
24
27
 
25
- cachedAccessories.forEach((accessory: any) => {
26
- // Check the accessory is from this plugin
27
- if (accessory.plugin === plugin) {
28
- // Add the cached accessory to the array
29
- devicesToReturn.push(accessory.accessory as never)
28
+ cachedAccessories.forEach((entry: any) => {
29
+ // entry shape varies by UI version
30
+ const pluginName = entry.plugin || entry?.accessory?.plugin || entry?.accessory?.pluginName
31
+ const acc = entry.accessory ?? entry
32
+ if (pluginNames.includes(pluginName)) {
33
+ devicesToReturn.push(acc as never)
30
34
  }
31
35
  })
32
36
  }
33
37
  // Return the array
38
+ console.warn(`[Homebridge UI] getCachedAccessories returning ${devicesToReturn.length} device(s)`)
34
39
  return devicesToReturn
35
- } catch {
40
+ } catch (e: any) {
36
41
  // Just return an empty accessory list in case of any errors
42
+ console.error(`[Homebridge UI] getCachedAccessories error: ${e?.message ?? e}`)
37
43
  return []
38
44
  }
39
- })
45
+ }
46
+ this.onRequest('getCachedAccessories', getCachedAccessoriesHandler)
47
+ // Also register with a leading slash for compatibility with some UIs
48
+ this.onRequest('/getCachedAccessories', getCachedAccessoriesHandler)
49
+ // Provide Matter cached accessories if Homebridge stores them separately.
50
+ const getCachedMatterAccessoriesHandler = () => {
51
+ try {
52
+ const pluginNames = ['@switchbot/homebridge-switchbot', 'homebridge-switchbot']
53
+ const devicesToReturn: any[] = []
54
+
55
+ const accFile = `${this.homebridgeStoragePath}/accessories/cachedAccessories`
56
+ const matterFile = `${this.homebridgeStoragePath}/accessories/cachedMatterAccessories`
57
+
58
+ // Log all files in the accessories directory for debugging
59
+ try {
60
+ const accessoriesDir = `${this.homebridgeStoragePath}/accessories`
61
+ if (fs.existsSync(accessoriesDir)) {
62
+ const files = fs.readdirSync(accessoriesDir)
63
+ console.warn(`[Homebridge UI] Files in accessories directory: ${files.join(', ')}`)
64
+ }
65
+ } catch (e: any) {
66
+ console.error(`[Homebridge UI] Error listing accessories directory: ${e?.message ?? e}`)
67
+ }
68
+
69
+ console.warn(`[Homebridge UI] Checking for cached files:`)
70
+ console.warn(`[Homebridge UI] - cachedAccessories: ${fs.existsSync(accFile)}`)
71
+ console.warn(`[Homebridge UI] - cachedMatterAccessories: ${fs.existsSync(matterFile)}`)
72
+
73
+ const readAndCollect = (filePath: string) => {
74
+ if (!fs.existsSync(filePath)) {
75
+ return
76
+ }
77
+ try {
78
+ const parsed: any[] = JSON.parse(fs.readFileSync(filePath, 'utf8'))
79
+ console.warn(`[Homebridge UI] - ${filePath}: found ${parsed.length} total entries`)
80
+ let matchCount = 0
81
+ parsed.forEach((entry: any) => {
82
+ // Entry shape varies between Homebridge versions; try common locations
83
+ const pluginName = entry.plugin || entry?.accessory?.plugin || entry?.accessory?.pluginName
84
+ const acc = entry.accessory ?? entry
85
+ if (pluginNames.includes(pluginName)) {
86
+ devicesToReturn.push(acc as never)
87
+ matchCount++
88
+ }
89
+ })
90
+ console.warn(`[Homebridge UI] - ${filePath}: matched ${matchCount} SwitchBot entries`)
91
+ } catch (e: any) {
92
+ // ignore parse errors for a single file
93
+ console.error(`[Homebridge UI] - ${filePath}: parse error - ${e?.message ?? e}`)
94
+ }
95
+ }
96
+
97
+ // Read both canonical files (some Homebridge versions use one or the other)
98
+ readAndCollect(accFile)
99
+ readAndCollect(matterFile)
100
+
101
+ console.warn(`[Homebridge UI] getCachedMatterAccessories returning ${devicesToReturn.length} device(s)`)
102
+ return devicesToReturn
103
+ } catch (e: any) {
104
+ console.error(`[Homebridge UI] getCachedMatterAccessories error: ${e?.message ?? e}`)
105
+ return []
106
+ }
107
+ }
108
+ this.onRequest('getCachedMatterAccessories', getCachedMatterAccessoriesHandler)
109
+ // Also register with a leading slash for compatibility with some UIs
110
+ this.onRequest('/getCachedMatterAccessories', getCachedMatterAccessoriesHandler)
40
111
  this.ready()
41
112
  }
42
113
  }
package/src/index.ts CHANGED
@@ -7,14 +7,11 @@ import type { API } from 'homebridge'
7
7
  import { SwitchBotHAPPlatform } from './platform-hap.js'
8
8
  import { SwitchBotMatterPlatform } from './platform-matter.js'
9
9
  import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'
10
+ import { createPlatformProxy } from './utils.js'
10
11
 
11
12
  // Register our platform with homebridge.
12
13
  export default (api: API): void => {
13
- // Call the instance method on the runtime `api` object (optional chaining in case
14
- // older homebridge doesn't provide it). Treat undefined as false.
15
- const isMatter = Boolean(api.isMatterEnabled?.())
16
- const log = (api as any).logger ?? console
17
- log.info?.(`Homebridge SwitchBot Plugin initializing in ${isMatter ? 'Matter' : 'HAP'} mode.`)
18
- // If Matter is enabled register the Matter platform, otherwise use HAP platform.
19
- api.registerPlatform(PLUGIN_NAME, PLATFORM_NAME, isMatter ? SwitchBotMatterPlatform : SwitchBotHAPPlatform)
14
+ // Create and register a small proxy that selects the correct platform (HAP or Matter) at runtime.
15
+ const ProxyCtor = createPlatformProxy(SwitchBotHAPPlatform, SwitchBotMatterPlatform)
16
+ api.registerPlatform(PLUGIN_NAME, PLATFORM_NAME, ProxyCtor as any)
20
17
  }