@switchbot/homebridge-switchbot 3.8.3 → 4.0.0-beta.1

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 (205) hide show
  1. package/.github/npm-version-script.cjs +81 -0
  2. package/.github/workflows/beta-release.yml +12 -4
  3. package/CHANGELOG.md +15 -1
  4. package/config.schema.json +583 -937
  5. package/dist/device/blindtilt.d.ts +1 -4
  6. package/dist/device/blindtilt.d.ts.map +1 -1
  7. package/dist/device/blindtilt.js +68 -66
  8. package/dist/device/blindtilt.js.map +1 -1
  9. package/dist/device/bot.d.ts +1 -4
  10. package/dist/device/bot.d.ts.map +1 -1
  11. package/dist/device/bot.js +63 -59
  12. package/dist/device/bot.js.map +1 -1
  13. package/dist/device/ceilinglight.d.ts +1 -4
  14. package/dist/device/ceilinglight.d.ts.map +1 -1
  15. package/dist/device/ceilinglight.js +56 -65
  16. package/dist/device/ceilinglight.js.map +1 -1
  17. package/dist/device/colorbulb.d.ts +1 -4
  18. package/dist/device/colorbulb.d.ts.map +1 -1
  19. package/dist/device/colorbulb.js +73 -80
  20. package/dist/device/colorbulb.js.map +1 -1
  21. package/dist/device/contact.d.ts +1 -4
  22. package/dist/device/contact.d.ts.map +1 -1
  23. package/dist/device/contact.js +38 -38
  24. package/dist/device/contact.js.map +1 -1
  25. package/dist/device/curtain.d.ts +1 -4
  26. package/dist/device/curtain.d.ts.map +1 -1
  27. package/dist/device/curtain.js +60 -60
  28. package/dist/device/curtain.js.map +1 -1
  29. package/dist/device/device.d.ts +6 -6
  30. package/dist/device/device.d.ts.map +1 -1
  31. package/dist/device/device.js +86 -20
  32. package/dist/device/device.js.map +1 -1
  33. package/dist/device/fan.d.ts +1 -4
  34. package/dist/device/fan.d.ts.map +1 -1
  35. package/dist/device/fan.js +50 -51
  36. package/dist/device/fan.js.map +1 -1
  37. package/dist/device/hub.d.ts +1 -4
  38. package/dist/device/hub.d.ts.map +1 -1
  39. package/dist/device/hub.js +48 -48
  40. package/dist/device/hub.js.map +1 -1
  41. package/dist/device/humidifier.d.ts +1 -4
  42. package/dist/device/humidifier.d.ts.map +1 -1
  43. package/dist/device/humidifier.js +53 -55
  44. package/dist/device/humidifier.js.map +1 -1
  45. package/dist/device/iosensor.d.ts +1 -4
  46. package/dist/device/iosensor.d.ts.map +1 -1
  47. package/dist/device/iosensor.js +35 -35
  48. package/dist/device/iosensor.js.map +1 -1
  49. package/dist/device/lightstrip.d.ts +1 -4
  50. package/dist/device/lightstrip.d.ts.map +1 -1
  51. package/dist/device/lightstrip.js +57 -65
  52. package/dist/device/lightstrip.js.map +1 -1
  53. package/dist/device/lock.d.ts +1 -4
  54. package/dist/device/lock.d.ts.map +1 -1
  55. package/dist/device/lock.js +38 -39
  56. package/dist/device/lock.js.map +1 -1
  57. package/dist/device/meter.d.ts +1 -4
  58. package/dist/device/meter.d.ts.map +1 -1
  59. package/dist/device/meter.js +35 -35
  60. package/dist/device/meter.js.map +1 -1
  61. package/dist/device/meterplus.d.ts +1 -4
  62. package/dist/device/meterplus.d.ts.map +1 -1
  63. package/dist/device/meterplus.js +35 -35
  64. package/dist/device/meterplus.js.map +1 -1
  65. package/dist/device/motion.d.ts +1 -4
  66. package/dist/device/motion.d.ts.map +1 -1
  67. package/dist/device/motion.js +27 -27
  68. package/dist/device/motion.js.map +1 -1
  69. package/dist/device/plug.d.ts +1 -4
  70. package/dist/device/plug.d.ts.map +1 -1
  71. package/dist/device/plug.js +32 -32
  72. package/dist/device/plug.js.map +1 -1
  73. package/dist/device/robotvacuumcleaner.d.ts +1 -4
  74. package/dist/device/robotvacuumcleaner.d.ts.map +1 -1
  75. package/dist/device/robotvacuumcleaner.js +39 -40
  76. package/dist/device/robotvacuumcleaner.js.map +1 -1
  77. package/dist/device/waterdetector.d.ts +1 -4
  78. package/dist/device/waterdetector.d.ts.map +1 -1
  79. package/dist/device/waterdetector.js +28 -28
  80. package/dist/device/waterdetector.js.map +1 -1
  81. package/dist/irdevice/airconditioner.d.ts +1 -1
  82. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  83. package/dist/irdevice/airconditioner.js +31 -32
  84. package/dist/irdevice/airconditioner.js.map +1 -1
  85. package/dist/irdevice/airpurifier.d.ts +1 -1
  86. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  87. package/dist/irdevice/airpurifier.js +11 -12
  88. package/dist/irdevice/airpurifier.js.map +1 -1
  89. package/dist/irdevice/camera.d.ts +1 -1
  90. package/dist/irdevice/camera.d.ts.map +1 -1
  91. package/dist/irdevice/camera.js +9 -10
  92. package/dist/irdevice/camera.js.map +1 -1
  93. package/dist/irdevice/fan.d.ts +1 -1
  94. package/dist/irdevice/fan.d.ts.map +1 -1
  95. package/dist/irdevice/fan.js +22 -23
  96. package/dist/irdevice/fan.js.map +1 -1
  97. package/dist/irdevice/irdevice.d.ts +6 -6
  98. package/dist/irdevice/irdevice.d.ts.map +1 -1
  99. package/dist/irdevice/irdevice.js +28 -14
  100. package/dist/irdevice/irdevice.js.map +1 -1
  101. package/dist/irdevice/light.d.ts +1 -1
  102. package/dist/irdevice/light.d.ts.map +1 -1
  103. package/dist/irdevice/light.js +12 -13
  104. package/dist/irdevice/light.js.map +1 -1
  105. package/dist/irdevice/other.d.ts +1 -1
  106. package/dist/irdevice/other.d.ts.map +1 -1
  107. package/dist/irdevice/other.js +12 -15
  108. package/dist/irdevice/other.js.map +1 -1
  109. package/dist/irdevice/tv.d.ts +1 -1
  110. package/dist/irdevice/tv.d.ts.map +1 -1
  111. package/dist/irdevice/tv.js +27 -28
  112. package/dist/irdevice/tv.js.map +1 -1
  113. package/dist/irdevice/vacuumcleaner.d.ts +1 -1
  114. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  115. package/dist/irdevice/vacuumcleaner.js +9 -10
  116. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  117. package/dist/irdevice/waterheater.d.ts +1 -1
  118. package/dist/irdevice/waterheater.d.ts.map +1 -1
  119. package/dist/irdevice/waterheater.js +9 -10
  120. package/dist/irdevice/waterheater.js.map +1 -1
  121. package/dist/platform.d.ts +9 -22
  122. package/dist/platform.d.ts.map +1 -1
  123. package/dist/platform.js +154 -260
  124. package/dist/platform.js.map +1 -1
  125. package/dist/settings.d.ts +64 -82
  126. package/dist/settings.d.ts.map +1 -1
  127. package/dist/settings.js +0 -20
  128. package/dist/settings.js.map +1 -1
  129. package/dist/utils.d.ts +1 -102
  130. package/dist/utils.d.ts.map +1 -1
  131. package/dist/utils.js +0 -105
  132. package/dist/utils.js.map +1 -1
  133. package/docs/assets/highlight.css +42 -0
  134. package/docs/assets/main.js +1 -1
  135. package/docs/functions/default.html +1 -1
  136. package/docs/index.html +61 -50
  137. package/docs/media/security-privacy-bluetooth.png +0 -0
  138. package/package.json +5 -7
  139. package/src/device/blindtilt.ts +74 -74
  140. package/src/device/bot.ts +68 -67
  141. package/src/device/ceilinglight.ts +59 -70
  142. package/src/device/colorbulb.ts +79 -88
  143. package/src/device/contact.ts +40 -43
  144. package/src/device/curtain.ts +66 -68
  145. package/src/device/device.ts +86 -40
  146. package/src/device/fan.ts +52 -56
  147. package/src/device/hub.ts +50 -53
  148. package/src/device/humidifier.ts +56 -61
  149. package/src/device/iosensor.ts +37 -40
  150. package/src/device/lightstrip.ts +62 -72
  151. package/src/device/lock.ts +41 -45
  152. package/src/device/meter.ts +37 -40
  153. package/src/device/meterplus.ts +37 -40
  154. package/src/device/motion.ts +29 -32
  155. package/src/device/plug.ts +34 -37
  156. package/src/device/robotvacuumcleaner.ts +41 -49
  157. package/src/device/waterdetector.ts +30 -33
  158. package/src/irdevice/airconditioner.ts +33 -35
  159. package/src/irdevice/airpurifier.ts +12 -13
  160. package/src/irdevice/camera.ts +10 -11
  161. package/src/irdevice/fan.ts +24 -25
  162. package/src/irdevice/irdevice.ts +31 -27
  163. package/src/irdevice/light.ts +14 -15
  164. package/src/irdevice/other.ts +14 -17
  165. package/src/irdevice/tv.ts +28 -29
  166. package/src/irdevice/vacuumcleaner.ts +10 -11
  167. package/src/irdevice/waterheater.ts +10 -11
  168. package/src/platform.ts +178 -287
  169. package/src/settings.ts +67 -88
  170. package/src/utils.ts +2 -106
  171. package/dist/types/bledevicestatus.d.ts +0 -281
  172. package/dist/types/bledevicestatus.d.ts.map +0 -1
  173. package/dist/types/bledevicestatus.js +0 -2
  174. package/dist/types/bledevicestatus.js.map +0 -1
  175. package/dist/types/devicelist.d.ts +0 -89
  176. package/dist/types/devicelist.d.ts.map +0 -1
  177. package/dist/types/devicelist.js +0 -6
  178. package/dist/types/devicelist.js.map +0 -1
  179. package/dist/types/deviceresponse.d.ts +0 -13
  180. package/dist/types/deviceresponse.d.ts.map +0 -1
  181. package/dist/types/deviceresponse.js +0 -2
  182. package/dist/types/deviceresponse.js.map +0 -1
  183. package/dist/types/devicestatus.d.ts +0 -149
  184. package/dist/types/devicestatus.d.ts.map +0 -1
  185. package/dist/types/devicestatus.js +0 -3
  186. package/dist/types/devicestatus.js.map +0 -1
  187. package/dist/types/devicewebhookstatus.d.ts +0 -156
  188. package/dist/types/devicewebhookstatus.d.ts.map +0 -1
  189. package/dist/types/devicewebhookstatus.js +0 -2
  190. package/dist/types/devicewebhookstatus.js.map +0 -1
  191. package/dist/types/irdevicelist.d.ts +0 -10
  192. package/dist/types/irdevicelist.d.ts.map +0 -1
  193. package/dist/types/irdevicelist.js +0 -2
  194. package/dist/types/irdevicelist.js.map +0 -1
  195. package/dist/types/pushbody.d.ts +0 -6
  196. package/dist/types/pushbody.d.ts.map +0 -1
  197. package/dist/types/pushbody.js +0 -2
  198. package/dist/types/pushbody.js.map +0 -1
  199. package/src/types/bledevicestatus.ts +0 -303
  200. package/src/types/devicelist.ts +0 -124
  201. package/src/types/deviceresponse.ts +0 -14
  202. package/src/types/devicestatus.ts +0 -178
  203. package/src/types/devicewebhookstatus.ts +0 -191
  204. package/src/types/irdevicelist.ts +0 -15
  205. package/src/types/pushbody.ts +0 -9
package/src/platform.ts CHANGED
@@ -2,21 +2,14 @@
2
2
  *
3
3
  * platform.ts: @switchbot/homebridge-switchbot platform class.
4
4
  */
5
- import type { IncomingMessage, Server, ServerResponse } from 'node:http'
6
- import type { UrlObject } from 'node:url'
5
+ import type { Server } from 'node:http'
7
6
 
8
7
  import type { API, DynamicPlatformPlugin, Logging, PlatformAccessory } from 'homebridge'
9
8
  import type { MqttClient } from 'mqtt'
10
- import type { Dispatcher } from 'undici'
11
9
 
12
- import type { devicesConfig, irDevicesConfig, options, SwitchBotPlatformConfig } from './settings.js'
13
- import type { blindTilt, curtain, curtain3, device } from './types/devicelist.js'
14
- import type { irdevice } from './types/irdevicelist.js'
10
+ import type { blindTiltConfig, curtainConfig, devicesConfig, irDevicesConfig, options, SwitchBotPlatformConfig } from './settings.js'
15
11
 
16
- import { Buffer } from 'node:buffer'
17
- import crypto, { randomUUID } from 'node:crypto'
18
- import { readFileSync, writeFileSync } from 'node:fs'
19
- import { createServer } from 'node:http'
12
+ import { readFileSync } from 'node:fs'
20
13
  import process from 'node:process'
21
14
 
22
15
  import asyncmqtt from 'async-mqtt'
@@ -24,11 +17,13 @@ import fakegato from 'fakegato-history'
24
17
  import { EveHomeKitTypes } from 'homebridge-lib/EveHomeKitTypes'
25
18
  /*
26
19
  * For Testing Locally:
27
- * import { SwitchBotModel } from '/Users/Shared/GitHub/OpenWonderLabs/node-switchbot/dist/index.js';
20
+ * import type { blindTilt, curtain, curtain3, device, irdevice } from '/Users/Shared/GitHub/OpenWonderLabs/node-switchbot/dist/index.js';
21
+ * import { LogLevel, SwitchBotBLE, SwitchBotModel, SwitchBotOpenAPI } from '/Users/Shared/GitHub/OpenWonderLabs/node-switchbot/dist/index.js';
28
22
  */
29
- import { SwitchBot, SwitchBotModel } from 'node-switchbot'
23
+ import type { blindTilt, curtain, curtain3, device, irdevice } from 'node-switchbot'
24
+
25
+ import { LogLevel, SwitchBotBLE, SwitchBotModel, SwitchBotOpenAPI } from 'node-switchbot'
30
26
  import { queueScheduler } from 'rxjs'
31
- import { request } from 'undici'
32
27
 
33
28
  import { BlindTilt } from './device/blindtilt.js'
34
29
  import { Bot } from './device/bot.js'
@@ -57,7 +52,7 @@ import { Others } from './irdevice/other.js'
57
52
  import { TV } from './irdevice/tv.js'
58
53
  import { VacuumCleaner } from './irdevice/vacuumcleaner.js'
59
54
  import { WaterHeater } from './irdevice/waterheater.js'
60
- import { deleteWebhook, Devices, PLATFORM_NAME, PLUGIN_NAME, queryWebhook, setupWebhook, updateWebhook } from './settings.js'
55
+ import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'
61
56
  import { formatDeviceIdAsMac, isBlindTiltDevice, isCurtainDevice, sleep } from './utils.js'
62
57
 
63
58
  /**
@@ -85,6 +80,10 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
85
80
  mqttClient: MqttClient | null = null
86
81
  webhookEventListener: Server | null = null
87
82
 
83
+ // SwitchBot APIs
84
+ switchBotAPI!: SwitchBotOpenAPI
85
+ switchBotBLE!: SwitchBotBLE
86
+
88
87
  // External APIs
89
88
  public readonly eve: any
90
89
  public readonly fakegatoAPI: any
@@ -103,6 +102,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
103
102
 
104
103
  // only load if configured
105
104
  if (!config) {
105
+ this.log.error('No configuration found for the plugin, please check your config.')
106
106
  return
107
107
  }
108
108
 
@@ -112,6 +112,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
112
112
  name: config.name,
113
113
  credentials: config.credentials as object,
114
114
  options: config.options as object,
115
+ devices: config.devices as { deviceId: string }[],
116
+ deviceConfig: config.deviceConfig as { [deviceType: string]: devicesConfig },
115
117
  }
116
118
 
117
119
  // Plugin Configuration
@@ -127,11 +129,51 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
127
129
  this.verifyConfig()
128
130
  this.debugLog('Config OK')
129
131
  } catch (e: any) {
130
- this.errorLog(`Verify Config, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug')
131
- this.debugErrorLog(`Verify Config, Error: ${e}`)
132
+ this.errorLog(`Verify Config, Error Message: ${e.message ?? e}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug')
133
+ this.debugErrorLog(`Verify Config, Error: ${e.message ?? e}`)
132
134
  return
133
135
  }
134
136
 
137
+ // SwitchBot OpenAPI
138
+ if (this.config.credentials?.token && this.config.credentials?.secret) {
139
+ this.switchBotAPI = new SwitchBotOpenAPI(this.config.credentials.token, this.config.credentials.secret)
140
+ } else {
141
+ this.debugErrorLog('Missing SwitchBot API credentials (token or secret).')
142
+ }
143
+ // Listen for log events
144
+ if (!this.config.options?.disableLogsforOpenAPI && this.switchBotAPI) {
145
+ this.switchBotAPI.on('log', (log) => {
146
+ switch (log.level) {
147
+ case LogLevel.SUCCESS:
148
+ this.successLog(log.message)
149
+ break
150
+ case LogLevel.DEBUGSUCCESS:
151
+ this.debugSuccessLog(log.message)
152
+ break
153
+ case LogLevel.WARN:
154
+ this.warnLog(log.message)
155
+ break
156
+ case LogLevel.DEBUGWARN:
157
+ this.debugWarnLog(log.message)
158
+ break
159
+ case LogLevel.ERROR:
160
+ this.errorLog(log.message)
161
+ break
162
+ case LogLevel.DEBUGERROR:
163
+ this.debugErrorLog(log.message)
164
+ break
165
+ case LogLevel.DEBUG:
166
+ this.debugLog(log.message)
167
+ break
168
+ case LogLevel.INFO:
169
+ default:
170
+ this.infoLog(log.message)
171
+ }
172
+ })
173
+ } else {
174
+ this.debugErrorLog(`SwitchBot OpenAPI logs are disabled, enable it by setting disableLogsforOpenAPI to false.`)
175
+ this.debugLog(`SwitchBot OpenAPI: ${JSON.stringify(this.switchBotAPI)}, disableLogsforOpenAPI: ${this.config.options?.disableLogsforOpenAPI}`)
176
+ }
135
177
  // import fakegato-history module and EVE characteristics
136
178
  this.fakegatoAPI = fakegato(api)
137
179
  this.eve = new EveHomeKitTypes(api)
@@ -144,33 +186,27 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
144
186
  await this.debugLog('Executed didFinishLaunching callback')
145
187
  // run the method to discover / register your devices as accessories
146
188
  try {
147
- if (this.config.credentials?.openToken && !this.config.credentials.token) {
148
- await this.updateToken()
149
- } else if (this.config.credentials?.token && !this.config.credentials?.secret) {
150
- await this.errorLog('"secret" config is not populated, you must populate then please restart Homebridge.')
151
- } else {
152
- await this.discoverDevices()
153
- }
189
+ await this.discoverDevices()
154
190
  } catch (e: any) {
155
- await this.errorLog(`Failed to Discover, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug')
156
- await this.debugErrorLog(`Failed to Discover, Error: ${e}`)
191
+ await this.errorLog(`Failed to Discover, Error Message: ${e.message ?? e}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug')
192
+ await this.debugErrorLog(`Failed to Discover, Error: ${e.message ?? e}`)
157
193
  }
158
194
  })
159
195
 
160
196
  try {
161
197
  this.setupMqtt()
162
198
  } catch (e: any) {
163
- this.errorLog(`Setup MQTT, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug')
199
+ this.errorLog(`Setup MQTT, Error Message: ${e.message ?? e}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug')
164
200
  }
165
201
  try {
166
202
  this.setupwebhook()
167
203
  } catch (e: any) {
168
- this.errorLog(`Setup Webhook, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug')
204
+ this.errorLog(`Setup Webhook, Error Message: ${e.message ?? e}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug')
169
205
  }
170
206
  try {
171
207
  this.setupBlE()
172
208
  } catch (e: any) {
173
- this.errorLog(`Setup Platform BLE, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug')
209
+ this.errorLog(`Setup Platform BLE, Error Message: ${e.message ?? e}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug')
174
210
  }
175
211
  }
176
212
 
@@ -181,7 +217,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
181
217
  this.mqttClient = await connectAsync(this.config.options?.mqttURL, this.config.options.mqttOptions || {})
182
218
  await this.debugLog('MQTT connection has been established successfully.')
183
219
  this.mqttClient.on('error', async (e: Error) => {
184
- await this.errorLog(`Failed to publish MQTT messages. ${e}`)
220
+ await this.errorLog(`Failed to publish MQTT messages. ${e.message ?? e}`)
185
221
  })
186
222
  if (!this.config.options?.webhookURL) {
187
223
  // receive webhook events via MQTT
@@ -193,13 +229,13 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
193
229
  const context = JSON.parse(message.toString())
194
230
  this.webhookEventHandler[context.deviceMac]?.(context)
195
231
  } catch (e: any) {
196
- await this.errorLog(`Failed to handle webhook event. Error:${e}`)
232
+ await this.errorLog(`Failed to handle webhook event. Error:${e.message ?? e}`)
197
233
  }
198
234
  })
199
235
  }
200
- } catch (e) {
236
+ } catch (e: any) {
201
237
  this.mqttClient = null
202
- await this.errorLog(`Failed to establish MQTT connection. ${e}`)
238
+ await this.errorLog(`Failed to establish MQTT connection. ${e.message ?? e}`)
203
239
  }
204
240
  }
205
241
  }
@@ -208,156 +244,94 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
208
244
  // webhook configuration
209
245
  if (this.config.options?.webhookURL) {
210
246
  const url = this.config.options?.webhookURL
211
-
212
247
  try {
213
- const xurl = new URL(url)
214
- const port = Number(xurl.port)
215
- const path = xurl.pathname
216
- this.webhookEventListener = createServer((request: IncomingMessage, response: ServerResponse) => {
217
- try {
218
- if (request.url === path && request.method === 'POST') {
219
- request.on('data', async (data) => {
220
- try {
221
- const body = JSON.parse(data)
222
- await this.debugLog(`Received Webhook: ${JSON.stringify(body)}`)
223
- if (this.config.options?.mqttURL) {
224
- const mac = body.context.deviceMac?.toLowerCase().match(/[\s\S]{1,2}/g)?.join(':')
225
- const options = this.config.options?.mqttPubOptions || {}
226
- this.mqttClient?.publish(`homebridge-switchbot/webhook/${mac}`, `${JSON.stringify(body.context)}`, options)
227
- }
228
- this.webhookEventHandler[body.context.deviceMac]?.(body.context)
229
- } catch (e: any) {
230
- await this.errorLog(`Failed to handle webhook event. Error:${e}`)
231
- }
232
- })
233
- response.writeHead(200, { 'Content-Type': 'text/plain' })
234
- response.end('OK')
235
- }
236
- // else {
237
- // response.writeHead(403, {'Content-Type': 'text/plain'});
238
- // response.end(`NG`);
239
- // }
240
- } catch (e: any) {
241
- this.errorLog(`Failed to handle webhook event. Error:${e}`)
248
+ this.switchBotAPI.setupWebhook(url)
249
+ // Listen for webhook events
250
+ this.switchBotAPI.on('webhookEvent', (body) => {
251
+ if (this.config.options?.mqttURL) {
252
+ const mac = body.context.deviceMac?.toLowerCase().match(/[\s\S]{1,2}/g)?.join(':')
253
+ const options = this.config.options?.mqttPubOptions || {}
254
+ this.mqttClient?.publish(`homebridge-switchbot/webhook/${mac}`, `${JSON.stringify(body.context)}`, options)
242
255
  }
243
- }).listen(port || 80)
244
- } catch (e: any) {
245
- await this.errorLog(`Failed to create webhook listener. Error:${e.message}`)
246
- return
247
- }
248
-
249
- try {
250
- const { body, statusCode } = await request(setupWebhook, {
251
- method: 'POST',
252
- headers: this.generateHeaders(),
253
- body: JSON.stringify({
254
- action: 'setupWebhook',
255
- url,
256
- deviceList: 'ALL',
257
- }),
256
+ this.webhookEventHandler[body.context.deviceMac]?.(body.context)
258
257
  })
259
- const response: any = await body.json()
260
- await this.debugLog(`setupWebhook: url:${url}, body:${JSON.stringify(response)}, statusCode:${statusCode}`)
261
- if (statusCode !== 200 || response?.statusCode !== 100) {
262
- await this.errorLog(`Failed to configure webhook. Existing webhook well be overridden. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`)
263
- }
264
258
  } catch (e: any) {
265
- await this.errorLog(`Failed to configure webhook. Error: ${e.message}`)
266
- }
267
-
268
- try {
269
- const { body, statusCode } = await request(updateWebhook, {
270
- method: 'POST',
271
- headers: this.generateHeaders(),
272
- body: JSON.stringify({
273
- action: 'updateWebhook',
274
- config: {
275
- url,
276
- enable: true,
277
- },
278
- }),
279
- })
280
- const response: any = await body.json()
281
- await this.debugLog(`updateWebhook: url:${url}, body:${JSON.stringify(response)}, statusCode:${statusCode}`)
282
- if (statusCode !== 200 || response?.statusCode !== 100) {
283
- await this.errorLog(`Failed to update webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`)
284
- }
285
- } catch (e: any) {
286
- await this.errorLog(`Failed to update webhook. Error:${e.message}`)
287
- }
288
-
289
- try {
290
- const { body, statusCode } = await request(queryWebhook, {
291
- method: 'POST',
292
- headers: this.generateHeaders(),
293
- body: JSON.stringify({
294
- action: 'queryUrl',
295
- }),
296
- })
297
- const response: any = await body.json()
298
- await this.debugLog(`queryWebhook: body:${JSON.stringify(response)}`)
299
- await this.debugLog(`queryWebhook: statusCode:${statusCode}`)
300
- if (statusCode !== 200 || response?.statusCode !== 100) {
301
- await this.errorLog(`Failed to query webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`)
302
- } else {
303
- await this.infoLog(`Listening webhook on ${response?.body?.urls[0]}`)
304
- }
305
- } catch (e: any) {
306
- await this.errorLog(`Failed to query webhook. Error:${e}`)
259
+ await this.errorLog(`Failed to setup webhook. Error:${e.message ?? e}`)
307
260
  }
308
261
 
309
262
  this.api.on('shutdown', async () => {
310
263
  try {
311
- const { body, statusCode } = await request(deleteWebhook, {
312
- method: 'POST',
313
- headers: this.generateHeaders(),
314
- body: JSON.stringify({
315
- action: 'deleteWebhook',
316
- url,
317
- }),
318
- })
319
- const response: any = await body.json()
320
- await this.debugLog(`deleteWebhook: url:${url}, body:${JSON.stringify(response)}, statusCode:${statusCode}`)
321
- if (statusCode !== 200 || response?.statusCode !== 100) {
322
- await this.errorLog(`Failed to delete webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`)
323
- } else {
324
- await this.infoLog('Unregistered webhook to close listening.')
325
- }
264
+ this.switchBotAPI.deleteWebhook(url)
326
265
  } catch (e: any) {
327
- await this.errorLog(`Failed to delete webhook. Error:${e.message}`)
266
+ await this.errorLog(`Failed to delete webhook. Error:${e.message ?? e}`)
328
267
  }
329
268
  })
330
269
  }
331
270
  }
332
271
 
333
272
  async setupBlE() {
273
+ this.switchBotBLE = new SwitchBotBLE()
274
+ // Listen for log events
275
+ if (!this.config.options?.disableLogsforBLE) {
276
+ this.switchBotBLE.on('log', (log) => {
277
+ switch (log.level) {
278
+ case LogLevel.SUCCESS:
279
+ this.successLog(log.message)
280
+ break
281
+ case LogLevel.DEBUGSUCCESS:
282
+ this.debugSuccessLog(log.message)
283
+ break
284
+ case LogLevel.WARN:
285
+ this.warnLog(log.message)
286
+ break
287
+ case LogLevel.DEBUGWARN:
288
+ this.debugWarnLog(log.message)
289
+ break
290
+ case LogLevel.ERROR:
291
+ this.errorLog(log.message)
292
+ break
293
+ case LogLevel.DEBUGERROR:
294
+ this.debugErrorLog(log.message)
295
+ break
296
+ case LogLevel.DEBUG:
297
+ this.debugLog(log.message)
298
+ break
299
+ case LogLevel.INFO:
300
+ default:
301
+ this.infoLog(log.message)
302
+ }
303
+ })
304
+ }
334
305
  if (this.config.options?.BLE) {
335
306
  await this.debugLog('setupBLE')
336
- const switchbot = new SwitchBot()
337
- if (switchbot === undefined) {
338
- await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`)
307
+ if (this.switchBotBLE === undefined) {
308
+ await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(this.switchBotBLE)}`)
339
309
  } else {
340
310
  // Start to monitor advertisement packets
341
311
  (async () => {
342
312
  // Start to monitor advertisement packets
343
313
  await this.debugLog('Scanning for BLE SwitchBot devices...')
344
- await switchbot.startScan()
314
+ try {
315
+ await this.switchBotBLE.startScan()
316
+ } catch (e: any) {
317
+ await this.errorLog(`Failed to start BLE scanning. Error:${e.message ?? e}`)
318
+ }
345
319
  // Set an event handler to monitor advertisement packets
346
- switchbot.onadvertisement = async (ad: any) => {
320
+ this.switchBotBLE.onadvertisement = async (ad: any) => {
347
321
  try {
348
322
  this.bleEventHandler[ad.address]?.(ad.serviceData)
349
323
  } catch (e: any) {
350
- await this.errorLog(`Failed to handle BLE event. Error:${e}`)
324
+ await this.errorLog(`Failed to handle BLE event. Error:${e.message ?? e}`)
351
325
  }
352
326
  }
353
327
  })()
354
328
 
355
329
  this.api.on('shutdown', async () => {
356
330
  try {
357
- switchbot.stopScan()
331
+ // this.switchBotBLE.stopScan()
358
332
  await this.infoLog('Stopped BLE scanning to close listening.')
359
333
  } catch (e: any) {
360
- await this.errorLog(`Failed to stop Platform BLE scanning. Error:${e.message}`)
334
+ await this.errorLog(`Failed to stop Platform BLE scanning. Error:${e.message ?? e}`)
361
335
  }
362
336
  })
363
337
  }
@@ -408,7 +382,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
408
382
  if (!deviceConfig.deviceId) {
409
383
  throw new Error('The devices config section is missing the *Device ID* in the config. Please check your config.')
410
384
  }
411
- if (!deviceConfig.configDeviceType && deviceConfig.connectionType) {
385
+ if (!deviceConfig.configDeviceType && (deviceConfig as devicesConfig).connectionType) {
412
386
  throw new Error('The devices config section is missing the *Device Type* in the config. Please check your config.')
413
387
  }
414
388
  }
@@ -477,79 +451,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
477
451
  }
478
452
  }
479
453
 
480
- /**
481
- * The openToken was old config.
482
- * This method saves the openToken as the token in the config.json file
483
- */
484
- async updateToken() {
485
- try {
486
- // check the new token was provided
487
- if (!this.config.credentials?.openToken) {
488
- throw new Error('New token not provided')
489
- }
490
-
491
- // load in the current config
492
- const currentConfig = JSON.parse(readFileSync(this.api.user.configPath(), 'utf8'))
493
-
494
- // check the platforms section is an array before we do array things on it
495
- if (!Array.isArray(currentConfig.platforms)) {
496
- throw new TypeError('Cannot find platforms array in config')
497
- }
498
-
499
- // find this plugins current config
500
- const pluginConfig = currentConfig.platforms.find((x: { platform: string }) => x.platform === PLATFORM_NAME)
501
-
502
- if (!pluginConfig) {
503
- throw new Error(`Cannot find config for ${PLATFORM_NAME} in platforms array`)
504
- }
505
-
506
- // check the .credentials is an object before doing object things with it
507
- if (typeof pluginConfig.credentials !== 'object') {
508
- throw new TypeError('pluginConfig.credentials is not an object')
509
- }
510
- // Move openToken to token
511
- if (!this.config.credentials.secret) {
512
- await this.warnLog('This plugin has been updated to use OpenAPI v1.1, config is set with openToken, "openToken" cconfig has been moved to the "token" config')
513
- this.errorLog('"secret" config is not populated, you must populate then please restart Homebridge.')
514
- } else {
515
- await this.warnLog('This plugin has been updated to use OpenAPI v1.1, config is set with openToken, "openToken" config has been moved to the "token" config, please restart Homebridge.')
516
- }
517
-
518
- // set the refresh token
519
- pluginConfig.credentials.token = this.config.credentials?.openToken
520
- if (pluginConfig.credentials.token) {
521
- pluginConfig.credentials.openToken = undefined
522
- }
523
-
524
- await this.debugWarnLog(`token: ${pluginConfig.credentials.token}`)
525
-
526
- // save the config, ensuring we maintain pretty json
527
- writeFileSync(this.api.user.configPath(), JSON.stringify(currentConfig, null, 4))
528
- await this.verifyConfig()
529
- } catch (e: any) {
530
- await this.errorLog(`Update Token: ${e}`)
531
- }
532
- }
533
-
534
- generateHeaders = () => {
535
- const t = `${Date.now()}`
536
- const nonce = randomUUID()
537
- const data = this.config.credentials?.token + t + nonce
538
- const signTerm = crypto
539
- .createHmac('sha256', this.config.credentials?.secret)
540
- .update(Buffer.from(data, 'utf-8'))
541
- .digest()
542
- const sign = signTerm.toString('base64')
543
-
544
- return {
545
- 'Authorization': this.config.credentials?.token,
546
- 'sign': sign,
547
- 'nonce': nonce,
548
- 't': t,
549
- 'Content-Type': 'application/json',
550
- }
551
- }
552
-
553
454
  async discoverDevices() {
554
455
  if (!this.config.credentials?.token) {
555
456
  return this.handleManualConfig()
@@ -565,23 +466,20 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
565
466
 
566
467
  while (retryCount < maxRetries) {
567
468
  try {
568
- const { body, statusCode } = await request(Devices, { headers: this.generateHeaders() })
569
- await this.debugWarnLog(`statusCode: ${statusCode}`)
570
- const devicesAPI: any = await body.json()
571
- await this.debugWarnLog(`devicesAPI: ${JSON.stringify(devicesAPI)}`)
572
-
573
- if (this.isSuccessfulResponse(statusCode, devicesAPI.statusCode)) {
574
- await this.handleDevices(devicesAPI.body.deviceList)
575
- await this.handleIRDevices(devicesAPI.body.infraredRemoteList)
469
+ const { response, statusCode } = await this.switchBotAPI.getDevices()
470
+ await this.debugLog(`response: ${JSON.stringify(response)}`)
471
+ if (this.isSuccessfulResponse(statusCode)) {
472
+ await this.handleDevices(Array.isArray(response.body.deviceList) ? response.body.deviceList : [])
473
+ await this.handleIRDevices(Array.isArray(response.body.infraredRemoteList) ? response.body.infraredRemoteList : [])
576
474
  break
577
475
  } else {
578
- await this.handleErrorResponse(statusCode, devicesAPI.statusCode, retryCount, maxRetries, delayBetweenRetries)
476
+ await this.handleErrorResponse(statusCode, retryCount, maxRetries, delayBetweenRetries)
579
477
  retryCount++
580
478
  }
581
479
  } catch (e: any) {
582
480
  retryCount++
583
481
  await this.debugErrorLog(`Failed to Discover Devices, Error Message: ${JSON.stringify(e.message)}, Submit Bugs Here: https://tinyurl.com/SwitchBotBug`)
584
- await this.debugErrorLog(`Failed to Discover Devices, Error: ${e}`)
482
+ await this.debugErrorLog(`Failed to Discover Devices, Error: ${e.message ?? e}`)
585
483
  }
586
484
  }
587
485
  }
@@ -608,8 +506,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
608
506
  }
609
507
  }
610
508
 
611
- private isSuccessfulResponse(statusCode: number, apiStatusCode: number): boolean {
612
- return (statusCode === 200 || statusCode === 100) && (apiStatusCode === 200 || apiStatusCode === 100)
509
+ private isSuccessfulResponse(apiStatusCode: number): boolean {
510
+ return (apiStatusCode === 200 || apiStatusCode === 100)
613
511
  }
614
512
 
615
513
  private async handleDevices(deviceLists: any[]) {
@@ -674,11 +572,10 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
674
572
  })
675
573
  }
676
574
 
677
- private async handleErrorResponse(statusCode: number, apiStatusCode: number, retryCount: number, maxRetries: number, delayBetweenRetries: number) {
678
- await this.statusCode(statusCode)
575
+ private async handleErrorResponse(apiStatusCode: number, retryCount: number, maxRetries: number, delayBetweenRetries: number) {
679
576
  await this.statusCode(apiStatusCode)
680
- if (statusCode === 500) {
681
- this.infoLog(`statusCode: ${statusCode} Attempt ${retryCount + 1} of ${maxRetries}`)
577
+ if (apiStatusCode === 500) {
578
+ this.infoLog(`statusCode: ${apiStatusCode} Attempt ${retryCount + 1} of ${maxRetries}`)
682
579
  await sleep(delayBetweenRetries)
683
580
  }
684
581
  }
@@ -728,9 +625,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
728
625
  }
729
626
  }
730
627
 
731
- private async createIRDevice(device: irdevice & devicesConfig) {
628
+ private async createIRDevice(device: irdevice & irDevicesConfig) {
732
629
  device.connectionType = device.connectionType ?? 'OpenAPI'
733
- const deviceTypeHandlers: { [key: string]: (device: irdevice & devicesConfig) => Promise<void> } = {
630
+ const deviceTypeHandlers: { [key: string]: (device: irdevice & irDevicesConfig) => Promise<void> } = {
734
631
  'TV': this.createTV.bind(this),
735
632
  'DIY TV': this.createTV.bind(this),
736
633
  'Projector': this.createTV.bind(this),
@@ -1355,14 +1252,14 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
1355
1252
  this.api.updatePlatformAccessories([existingAccessory])
1356
1253
  // create the accessory handler for the restored accessory
1357
1254
  // this is imported from `platformAccessory.ts`
1358
- new BlindTilt(this, existingAccessory, device)
1255
+ new BlindTilt(this, existingAccessory, device as blindTiltConfig)
1359
1256
  await this.debugLog(`${device.deviceType} uuid: ${device.deviceId}-${device.deviceType}, (${existingAccessory.UUID})`)
1360
1257
  } else {
1361
1258
  this.unregisterPlatformAccessories(existingAccessory)
1362
1259
  }
1363
1260
  } else if (await this.registerDevice(device)) {
1364
1261
  if (isBlindTiltDevice(device)) {
1365
- if (device.group && !device.curtain?.disable_group) {
1262
+ if (device.group && !(device as blindTiltConfig | curtainConfig).disable_group) {
1366
1263
  this.debugLog(
1367
1264
  'Your Curtains are grouped, '
1368
1265
  + `, Secondary curtain automatically hidden. Main Curtain: ${device.deviceName}, deviceId: ${device.deviceId}`,
@@ -1397,7 +1294,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
1397
1294
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`)
1398
1295
  // create the accessory handler for the newly create accessory
1399
1296
  // this is imported from `platformAccessory.ts`
1400
- new BlindTilt(this, accessory, device)
1297
+ new BlindTilt(this, accessory, device as blindTiltConfig)
1401
1298
  await this.debugLog(`${device.deviceType} uuid: ${device.deviceId}-${device.deviceType}, (${accessory.UUID})`)
1402
1299
 
1403
1300
  // publish device externally or link the accessory to your platform
@@ -1432,14 +1329,14 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
1432
1329
  this.api.updatePlatformAccessories([existingAccessory])
1433
1330
  // create the accessory handler for the restored accessory
1434
1331
  // this is imported from `platformAccessory.ts`
1435
- new Curtain(this, existingAccessory, device)
1332
+ new Curtain(this, existingAccessory, device as curtainConfig)
1436
1333
  await this.debugLog(`${device.deviceType} uuid: ${device.deviceId}-${device.deviceType}, (${existingAccessory.UUID})`)
1437
1334
  } else {
1438
1335
  this.unregisterPlatformAccessories(existingAccessory)
1439
1336
  }
1440
1337
  } else if (await this.registerDevice(device)) {
1441
1338
  if (isCurtainDevice(device)) {
1442
- if (device.group && !device.curtain?.disable_group) {
1339
+ if (device.group && !(device as blindTiltConfig | curtainConfig).disable_group) {
1443
1340
  this.debugLog(
1444
1341
  'Your Curtains are grouped, '
1445
1342
  + `, Secondary curtain automatically hidden. Main Curtain: ${device.deviceName}, deviceId: ${device.deviceId}`,
@@ -1474,7 +1371,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
1474
1371
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`)
1475
1372
  // create the accessory handler for the newly create accessory
1476
1373
  // this is imported from `platformAccessory.ts`
1477
- new Curtain(this, accessory, device)
1374
+ new Curtain(this, accessory, device as curtainConfig)
1478
1375
  await this.debugLog(`${device.deviceType} uuid: ${device.deviceId}-${device.deviceType}, (${accessory.UUID})`)
1479
1376
 
1480
1377
  // publish device externally or link the accessory to your platform
@@ -1947,7 +1844,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
1947
1844
  }
1948
1845
  }
1949
1846
 
1950
- private async createTV(device: irdevice & devicesConfig) {
1847
+ private async createTV(device: irdevice & irDevicesConfig) {
1951
1848
  const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`)
1952
1849
 
1953
1850
  // see if an accessory with the same uuid has already been registered and restored from
@@ -1986,7 +1883,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
1986
1883
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
1987
1884
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName)
1988
1885
  accessory.context.connectionType = await this.connectionType(device)
1989
- accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0'
1886
+ accessory.context.version = device.firmware ?? this.version ?? '0.0.0'
1990
1887
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external'
1991
1888
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`)
1992
1889
  // create the accessory handler for the newly create accessory
@@ -2001,7 +1898,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2001
1898
  }
2002
1899
  }
2003
1900
 
2004
- private async createIRFan(device: irdevice & devicesConfig) {
1901
+ private async createIRFan(device: irdevice & irDevicesConfig) {
2005
1902
  const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`)
2006
1903
 
2007
1904
  // see if an accessory with the same uuid has already been registered and restored from
@@ -2045,7 +1942,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2045
1942
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
2046
1943
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName)
2047
1944
  accessory.context.connectionType = await this.connectionType(device)
2048
- accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0'
1945
+ accessory.context.version = device.firmware ?? this.version ?? '0.0.0'
2049
1946
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external'
2050
1947
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`)
2051
1948
  // create the accessory handler for the newly create accessory
@@ -2061,7 +1958,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2061
1958
  }
2062
1959
  }
2063
1960
 
2064
- private async createLight(device: irdevice & devicesConfig) {
1961
+ private async createLight(device: irdevice & irDevicesConfig) {
2065
1962
  const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`)
2066
1963
 
2067
1964
  // see if an accessory with the same uuid has already been registered and restored from
@@ -2105,7 +2002,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2105
2002
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
2106
2003
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName)
2107
2004
  accessory.context.connectionType = await this.connectionType(device)
2108
- accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0'
2005
+ accessory.context.version = device.firmware ?? this.version ?? '0.0.0'
2109
2006
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external'
2110
2007
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`)
2111
2008
  // create the accessory handler for the newly create accessory
@@ -2121,7 +2018,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2121
2018
  }
2122
2019
  }
2123
2020
 
2124
- private async createAirConditioner(device: irdevice & devicesConfig & irDevicesConfig) {
2021
+ private async createAirConditioner(device: irdevice & irDevicesConfig) {
2125
2022
  const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`)
2126
2023
 
2127
2024
  // see if an accessory with the same uuid has already been registered and restored from
@@ -2165,7 +2062,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2165
2062
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
2166
2063
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName)
2167
2064
  accessory.context.connectionType = await this.connectionType(device)
2168
- accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0'
2065
+ accessory.context.version = device.firmware ?? this.version ?? '0.0.0'
2169
2066
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external'
2170
2067
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`)
2171
2068
  // create the accessory handler for the newly create accessory
@@ -2181,7 +2078,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2181
2078
  }
2182
2079
  }
2183
2080
 
2184
- private async createAirPurifier(device: irdevice & devicesConfig) {
2081
+ private async createAirPurifier(device: irdevice & irDevicesConfig) {
2185
2082
  const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`)
2186
2083
 
2187
2084
  // see if an accessory with the same uuid has already been registered and restored from
@@ -2225,7 +2122,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2225
2122
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
2226
2123
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName)
2227
2124
  accessory.context.connectionType = await this.connectionType(device)
2228
- accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0'
2125
+ accessory.context.version = device.firmware ?? this.version ?? '0.0.0'
2229
2126
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external'
2230
2127
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`)
2231
2128
  // create the accessory handler for the newly create accessory
@@ -2241,7 +2138,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2241
2138
  }
2242
2139
  }
2243
2140
 
2244
- private async createWaterHeater(device: irdevice & devicesConfig) {
2141
+ private async createWaterHeater(device: irdevice & irDevicesConfig) {
2245
2142
  const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`)
2246
2143
 
2247
2144
  // see if an accessory with the same uuid has already been registered and restored from
@@ -2285,7 +2182,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2285
2182
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
2286
2183
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName)
2287
2184
  accessory.context.connectionType = await this.connectionType(device)
2288
- accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0'
2185
+ accessory.context.version = device.firmware ?? this.version ?? '0.0.0'
2289
2186
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external'
2290
2187
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`)
2291
2188
  // create the accessory handler for the newly create accessory
@@ -2301,7 +2198,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2301
2198
  }
2302
2199
  }
2303
2200
 
2304
- private async createVacuumCleaner(device: irdevice & devicesConfig) {
2201
+ private async createVacuumCleaner(device: irdevice & irDevicesConfig) {
2305
2202
  const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`)
2306
2203
 
2307
2204
  // see if an accessory with the same uuid has already been registered and restored from
@@ -2345,7 +2242,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2345
2242
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
2346
2243
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName)
2347
2244
  accessory.context.connectionType = await this.connectionType(device)
2348
- accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0'
2245
+ accessory.context.version = device.firmware ?? this.version ?? '0.0.0'
2349
2246
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external'
2350
2247
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`)
2351
2248
  // create the accessory handler for the newly create accessory
@@ -2361,7 +2258,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2361
2258
  }
2362
2259
  }
2363
2260
 
2364
- private async createCamera(device: irdevice & devicesConfig) {
2261
+ private async createCamera(device: irdevice & irDevicesConfig) {
2365
2262
  const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`)
2366
2263
 
2367
2264
  // see if an accessory with the same uuid has already been registered and restored from
@@ -2405,7 +2302,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2405
2302
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
2406
2303
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName)
2407
2304
  accessory.context.connectionType = await this.connectionType(device)
2408
- accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0'
2305
+ accessory.context.version = device.firmware ?? this.version ?? '0.0.0'
2409
2306
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external'
2410
2307
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`)
2411
2308
  // create the accessory handler for the newly create accessory
@@ -2421,7 +2318,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2421
2318
  }
2422
2319
  }
2423
2320
 
2424
- private async createOthers(device: irdevice & devicesConfig) {
2321
+ private async createOthers(device: irdevice & irDevicesConfig) {
2425
2322
  const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`)
2426
2323
 
2427
2324
  // see if an accessory with the same uuid has already been registered and restored from
@@ -2465,7 +2362,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2465
2362
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
2466
2363
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName)
2467
2364
  accessory.context.connectionType = await this.connectionType(device)
2468
- accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0'
2365
+ accessory.context.version = device.firmware ?? this.version ?? '0.0.0'
2469
2366
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external'
2470
2367
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`)
2471
2368
  // create the accessory handler for the newly create accessory
@@ -2484,10 +2381,10 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2484
2381
  async registerCurtains(device: device & devicesConfig): Promise<boolean> {
2485
2382
  let registerWindowCovering: boolean
2486
2383
  if (isCurtainDevice(device)) {
2487
- await this.debugWarnLog(`deviceName: ${device.deviceName} deviceId: ${device.deviceId}, curtainDevicesIds: ${device.curtainDevicesIds},x master: ${device.master}, group: ${device.group}, disable_group: ${device.curtain?.disable_group}, connectionType: ${device.connectionType}`)
2384
+ await this.debugWarnLog(`deviceName: ${device.deviceName} deviceId: ${device.deviceId}, curtainDevicesIds: ${device.curtainDevicesIds},x master: ${device.master}, group: ${device.group}, disable_group: ${(device as blindTiltConfig | curtainConfig).disable_group}, connectionType: ${device.connectionType}`)
2488
2385
  registerWindowCovering = await this.registerWindowCovering(device)
2489
2386
  } else if (isBlindTiltDevice(device)) {
2490
- await this.debugWarnLog(`deviceName: ${device.deviceName} deviceId: ${device.deviceId}, blindTiltDevicesIds: ${device.blindTiltDevicesIds}, master: ${device.master}, group: ${device.group}, disable_group: ${device.curtain?.disable_group}, connectionType: ${device.connectionType}`)
2387
+ await this.debugWarnLog(`deviceName: ${device.deviceName} deviceId: ${device.deviceId}, blindTiltDevicesIds: ${device.blindTiltDevicesIds}, master: ${device.master}, group: ${device.group}, disable_group: ${(device as blindTiltConfig | curtainConfig).disable_group}, connectionType: ${device.connectionType}`)
2491
2388
  registerWindowCovering = await this.registerWindowCovering(device)
2492
2389
  } else {
2493
2390
  registerWindowCovering = false
@@ -2495,7 +2392,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2495
2392
  return registerWindowCovering
2496
2393
  }
2497
2394
 
2498
- async registerWindowCovering(device: ((curtain | curtain3) & devicesConfig) | (blindTilt & devicesConfig)) {
2395
+ async registerWindowCovering(device: (curtain | curtain3 | blindTilt) & devicesConfig) {
2499
2396
  await this.debugLog(`master: ${device.master}`)
2500
2397
  let registerCurtain: boolean
2501
2398
  if (device.master && device.group) {
@@ -2503,11 +2400,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2503
2400
  registerCurtain = true
2504
2401
  await this.debugLog(`deviceName: ${device.deviceName} [${device.deviceType} Config] device.master: ${device.master}, device.group: ${device.group} connectionType; ${device.connectionType}`)
2505
2402
  await this.debugWarnLog(`Device: ${device.deviceName} registerCurtains: ${registerCurtain}`)
2506
- } else if (!device.master && device.curtain?.disable_group) {
2507
- // !device.group && device.connectionType === 'BLE'
2508
- // OpenAPI: Non-Master Curtains/Blind Tilts that has Disable Grouping Checked
2403
+ } else if (!device.master && (device as blindTiltConfig | curtainConfig).disable_group) {
2509
2404
  registerCurtain = true
2510
- await this.debugLog(`deviceName: ${device.deviceName} [${device.deviceType} Config] device.master: ${device.master}, disable_group: ${device.curtain?.disable_group}, connectionType; ${device.connectionType}`)
2405
+ await this.debugLog(`deviceName: ${device.deviceName} [${device.deviceType} Config] device.master: ${device.master}, disable_group: ${(device as blindTiltConfig | curtainConfig).disable_group}, connectionType; ${device.connectionType}`)
2511
2406
  await this.debugWarnLog(`Device: ${device.deviceName} registerCurtains: ${registerCurtain}`)
2512
2407
  } else if (device.master && !device.group) {
2513
2408
  // OpenAPI: Master Curtains/Blind Tilts not in Group
@@ -2521,13 +2416,13 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2521
2416
  await this.debugWarnLog(`Device: ${device.deviceName} registerCurtains: ${registerCurtain}`)
2522
2417
  } else {
2523
2418
  registerCurtain = false
2524
- await this.debugErrorLog(`deviceName: ${device.deviceName} [${device.deviceType} Config] disable_group: ${device.curtain?.disable_group}, device.master: ${device.master}, device.group: ${device.group}`)
2419
+ await this.debugErrorLog(`deviceName: ${device.deviceName} [${device.deviceType} Config] disable_group: ${(device as blindTiltConfig | curtainConfig).disable_group}, device.master: ${device.master}, device.group: ${device.group}`)
2525
2420
  await this.debugWarnLog(`Device: ${device.deviceName} registerCurtains: ${registerCurtain}, device.connectionType: ${device.connectionType}`)
2526
2421
  }
2527
2422
  return registerCurtain
2528
2423
  }
2529
2424
 
2530
- async connectionType(device: device & devicesConfig): Promise<any> {
2425
+ async connectionType(device: (device & devicesConfig) | (irdevice & irDevicesConfig)): Promise<any> {
2531
2426
  let connectionType: string
2532
2427
  if (!device.connectionType && this.config.credentials?.token && this.config.credentials.secret) {
2533
2428
  connectionType = 'OpenAPI'
@@ -2577,7 +2472,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2577
2472
  return registerDevice
2578
2473
  }
2579
2474
 
2580
- public async externalOrPlatform(device: device & (irDevicesConfig | devicesConfig), accessory: PlatformAccessory) {
2475
+ public async externalOrPlatform(device: (device & devicesConfig) | (irdevice & irDevicesConfig), accessory: PlatformAccessory) {
2581
2476
  const { displayName } = accessory
2582
2477
  const isExternal = device.external ?? false
2583
2478
 
@@ -2639,18 +2534,15 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2639
2534
  }
2640
2535
  }
2641
2536
 
2642
- async retryRequest(deviceMaxRetries: number, deviceDelayBetweenRetries: number, url: string | URL | UrlObject, options?: { dispatcher?: Dispatcher } & Omit<Dispatcher.RequestOptions, 'origin' | 'path' | 'method'> & Partial<Pick<Dispatcher.RequestOptions, 'method'>>): Promise<{ body: any, statusCode: number }> {
2537
+ async retryRequest(deviceId: string, deviceMaxRetries: number, deviceDelayBetweenRetries: number): Promise<{ response: any, statusCode: number }> {
2643
2538
  let retryCount = 0
2644
2539
  const maxRetries = deviceMaxRetries
2645
2540
  const delayBetweenRetries = deviceDelayBetweenRetries
2646
2541
  while (retryCount < maxRetries) {
2647
2542
  try {
2648
- const { body, statusCode } = await request(url, options)
2649
- if (statusCode === 200 || statusCode === 100) {
2650
- return { body, statusCode }
2651
- } else {
2652
- await this.debugLog(`Received status code: ${statusCode}`)
2653
- }
2543
+ const { response, statusCode } = await this.switchBotAPI.getDeviceStatus(deviceId)
2544
+ await this.debugLog(`response: ${JSON.stringify(response)}`)
2545
+ return { response, statusCode }
2654
2546
  } catch (error: any) {
2655
2547
  await this.errorLog(`Error making request: ${error.message}`)
2656
2548
  }
@@ -2658,18 +2550,17 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
2658
2550
  await this.debugLog(`Retry attempt ${retryCount} of ${maxRetries}`)
2659
2551
  await sleep(delayBetweenRetries)
2660
2552
  }
2661
- return { body: null, statusCode: -1 }
2553
+ return { response: null, statusCode: 500 }
2662
2554
  }
2663
2555
 
2664
2556
  // BLE Connection
2665
2557
  async connectBLE(accessory: PlatformAccessory, device: device & devicesConfig): Promise<any> {
2666
2558
  try {
2667
- const switchbot = new SwitchBot()
2668
- queueScheduler.schedule(async () => switchbot)
2669
- await this.debugLog(`${device.deviceType}: ${accessory.displayName} 'node-switchbot' found: ${switchbot}`)
2670
- return switchbot
2559
+ queueScheduler.schedule(async () => this.switchBotBLE)
2560
+ await this.debugLog(`${device.deviceType}: ${accessory.displayName} 'node-switchbot' found: ${this.switchBotBLE}`)
2561
+ return this.switchBotBLE
2671
2562
  } catch (e: any) {
2672
- await this.errorLog(`${device.deviceType}: ${accessory.displayName} 'node-switchbot' not found, Error: ${e}`)
2563
+ await this.errorLog(`${device.deviceType}: ${accessory.displayName} 'node-switchbot' not found, Error: ${e.message ?? e}`)
2673
2564
  return false
2674
2565
  }
2675
2566
  }