@switchbot/homebridge-switchbot 5.0.0-beta.6 → 5.0.0-beta.61

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 (267) 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 +29 -43
  86. package/dist/devices-matter/RoboticVacuumAccessory.d.ts.map +1 -1
  87. package/dist/devices-matter/RoboticVacuumAccessory.js +287 -262
  88. package/dist/devices-matter/RoboticVacuumAccessory.js.map +1 -1
  89. package/dist/homebridge-ui/public/index.html +200 -18
  90. package/dist/homebridge-ui/server.js +0 -31
  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 +93 -6
  104. package/dist/platform-matter.d.ts.map +1 -1
  105. package/dist/platform-matter.js +1822 -224
  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/devices-matter/roboticVacuumAccessory.test.d.ts +2 -0
  142. package/dist/test/matter/devices-matter/roboticVacuumAccessory.test.d.ts.map +1 -0
  143. package/dist/test/matter/devices-matter/roboticVacuumAccessory.test.js +366 -0
  144. package/dist/test/matter/devices-matter/roboticVacuumAccessory.test.js.map +1 -0
  145. package/dist/test/matter/platform-matter.additional.test.d.ts +2 -0
  146. package/dist/test/matter/platform-matter.additional.test.d.ts.map +1 -0
  147. package/dist/test/matter/platform-matter.additional.test.js +35 -0
  148. package/dist/test/matter/platform-matter.additional.test.js.map +1 -0
  149. package/dist/test/matter/platform-matter.bleparse.test.d.ts +2 -0
  150. package/dist/test/matter/platform-matter.bleparse.test.d.ts.map +1 -0
  151. package/dist/test/matter/platform-matter.bleparse.test.js +43 -0
  152. package/dist/test/matter/platform-matter.bleparse.test.js.map +1 -0
  153. package/dist/test/matter/platform-matter.cleanup.test.d.ts +2 -0
  154. package/dist/test/matter/platform-matter.cleanup.test.d.ts.map +1 -0
  155. package/dist/test/matter/platform-matter.cleanup.test.js +70 -0
  156. package/dist/test/matter/platform-matter.cleanup.test.js.map +1 -0
  157. package/dist/test/matter/platform-matter.keepstale.test.d.ts +2 -0
  158. package/dist/test/matter/platform-matter.keepstale.test.d.ts.map +1 -0
  159. package/dist/test/matter/platform-matter.keepstale.test.js +27 -0
  160. package/dist/test/matter/platform-matter.keepstale.test.js.map +1 -0
  161. package/dist/test/matter/platform-matter.logging.test.d.ts +2 -0
  162. package/dist/test/matter/platform-matter.logging.test.d.ts.map +1 -0
  163. package/dist/test/matter/platform-matter.logging.test.js +29 -0
  164. package/dist/test/matter/platform-matter.logging.test.js.map +1 -0
  165. package/dist/test/matter/platform-matter.mapping.test.d.ts +2 -0
  166. package/dist/test/matter/platform-matter.mapping.test.d.ts.map +1 -0
  167. package/dist/test/matter/platform-matter.mapping.test.js +43 -0
  168. package/dist/test/matter/platform-matter.mapping.test.js.map +1 -0
  169. package/dist/test/matter/platform-matter.openapi-mapping.test.d.ts +2 -0
  170. package/dist/test/matter/platform-matter.openapi-mapping.test.d.ts.map +1 -0
  171. package/dist/test/matter/platform-matter.openapi-mapping.test.js +84 -0
  172. package/dist/test/matter/platform-matter.openapi-mapping.test.js.map +1 -0
  173. package/dist/test/matter/platform-matter.test.d.ts +2 -0
  174. package/dist/test/matter/platform-matter.test.d.ts.map +1 -0
  175. package/dist/test/matter/platform-matter.test.js +117 -0
  176. package/dist/test/matter/platform-matter.test.js.map +1 -0
  177. package/dist/test/matter/platform-matter.unregister.test.d.ts +2 -0
  178. package/dist/test/matter/platform-matter.unregister.test.d.ts.map +1 -0
  179. package/dist/test/matter/platform-matter.unregister.test.js +30 -0
  180. package/dist/test/matter/platform-matter.unregister.test.js.map +1 -0
  181. package/dist/test/matter/platform-matter.webhook.test.d.ts +2 -0
  182. package/dist/test/matter/platform-matter.webhook.test.d.ts.map +1 -0
  183. package/dist/test/matter/platform-matter.webhook.test.js +46 -0
  184. package/dist/test/matter/platform-matter.webhook.test.js.map +1 -0
  185. package/dist/test/utils.test.d.ts +2 -0
  186. package/dist/test/utils.test.d.ts.map +1 -0
  187. package/dist/test/utils.test.js +95 -0
  188. package/dist/test/utils.test.js.map +1 -0
  189. package/dist/test/verifyconfig.test.d.ts.map +1 -0
  190. package/dist/{verifyconfig.test.js → test/verifyconfig.test.js} +2 -2
  191. package/dist/test/verifyconfig.test.js.map +1 -0
  192. package/dist/utils.d.ts +204 -3
  193. package/dist/utils.d.ts.map +1 -1
  194. package/dist/utils.js +713 -33
  195. package/dist/utils.js.map +1 -1
  196. package/docs/assets/highlight.css +14 -0
  197. package/docs/assets/main.js +2 -2
  198. package/docs/index.html +31 -2
  199. package/docs/variables/default.html +1 -1
  200. package/package.json +15 -15
  201. package/src/devices-hap/airpurifier.ts +11 -6
  202. package/src/devices-hap/blindtilt.ts +3 -3
  203. package/src/devices-hap/bot.ts +15 -5
  204. package/src/devices-hap/ceilinglight.ts +12 -7
  205. package/src/devices-hap/colorbulb.ts +46 -10
  206. package/src/devices-hap/contact.ts +3 -3
  207. package/src/devices-hap/curtain.ts +2 -2
  208. package/src/devices-hap/device.ts +149 -70
  209. package/src/devices-hap/fan.ts +11 -6
  210. package/src/devices-hap/hub.ts +6 -5
  211. package/src/devices-hap/humidifier.ts +97 -4
  212. package/src/devices-hap/iosensor.ts +36 -21
  213. package/src/devices-hap/lightstrip.ts +35 -8
  214. package/src/devices-hap/lock.ts +13 -6
  215. package/src/devices-hap/meter.ts +6 -5
  216. package/src/devices-hap/meterplus.ts +6 -5
  217. package/src/devices-hap/meterpro.ts +7 -6
  218. package/src/devices-hap/motion.ts +3 -3
  219. package/src/devices-hap/plug.ts +10 -6
  220. package/src/devices-hap/relayswitch.ts +3 -3
  221. package/src/devices-hap/robotvacuumcleaner.ts +12 -6
  222. package/src/devices-hap/waterdetector.ts +3 -3
  223. package/src/devices-matter/BaseMatterAccessory.ts +176 -5
  224. package/src/devices-matter/ColorLightAccessory.ts +12 -12
  225. package/src/devices-matter/ColorTemperatureLightAccessory.ts +5 -7
  226. package/src/devices-matter/DimmableLightAccessory.ts +9 -9
  227. package/src/devices-matter/ExtendedColorLightAccessory.ts +14 -15
  228. package/src/devices-matter/OnOffLightAccessory.ts +8 -16
  229. package/src/devices-matter/OnOffOutletAccessory.ts +12 -7
  230. package/src/devices-matter/OnOffSwitchAccessory.ts +2 -2
  231. package/src/devices-matter/RoboticVacuumAccessory.ts +340 -313
  232. package/src/homebridge-ui/public/index.html +200 -18
  233. package/src/homebridge-ui/server.ts +0 -34
  234. package/src/index.ts +4 -7
  235. package/src/irdevice/irdevice.ts +74 -35
  236. package/src/platform-hap.ts +365 -169
  237. package/src/platform-matter.ts +1872 -229
  238. package/src/settings.ts +62 -3
  239. package/src/test/apiRequestTracker.test.ts +417 -0
  240. package/src/test/hap/device-webhook-context.test.ts +136 -0
  241. package/src/test/hap/platform-hap.logging.test.ts +36 -0
  242. package/src/test/hap/platform-hap.test.ts +70 -0
  243. package/src/test/helpers/platform-fixtures.ts +33 -0
  244. package/src/test/homebridge-ui/server.test.ts +486 -0
  245. package/src/test/index.test.ts +24 -0
  246. package/src/test/matter/devices-matter/baseMatterAccessory.test.ts +88 -0
  247. package/src/test/matter/devices-matter/roboticVacuumAccessory.test.ts +453 -0
  248. package/src/test/matter/platform-matter.additional.test.ts +44 -0
  249. package/src/test/matter/platform-matter.bleparse.test.ts +47 -0
  250. package/src/test/matter/platform-matter.cleanup.test.ts +86 -0
  251. package/src/test/matter/platform-matter.keepstale.test.ts +37 -0
  252. package/src/test/matter/platform-matter.logging.test.ts +33 -0
  253. package/src/test/matter/platform-matter.mapping.test.ts +57 -0
  254. package/src/test/matter/platform-matter.openapi-mapping.test.ts +109 -0
  255. package/src/test/matter/platform-matter.test.ts +144 -0
  256. package/src/test/matter/platform-matter.unregister.test.ts +39 -0
  257. package/src/test/matter/platform-matter.webhook.test.ts +54 -0
  258. package/src/test/utils.test.ts +96 -0
  259. package/src/{verifyconfig.test.ts → test/verifyconfig.test.ts} +12 -11
  260. package/src/utils.ts +777 -36
  261. package/dist/index.test.js +0 -14
  262. package/dist/index.test.js.map +0 -1
  263. package/dist/verifyconfig.test.d.ts.map +0 -1
  264. package/dist/verifyconfig.test.js.map +0 -1
  265. package/src/index.test.ts +0 -19
  266. /package/dist/{index.test.d.ts → test/index.test.d.ts} +0 -0
  267. /package/dist/{verifyconfig.test.d.ts → test/verifyconfig.test.d.ts} +0 -0
@@ -14,7 +14,7 @@ import { hostname } from 'node:os'
14
14
 
15
15
  import { SwitchBotBLEModel, SwitchBotBLEModelFriendlyName, SwitchBotBLEModelName, SwitchBotModel } from 'node-switchbot'
16
16
 
17
- import { formatDeviceIdAsMac, safeStringify, sleep } from '../utils.js'
17
+ import { deviceLoggingEnabled, deviceLoggingIsDebug, formatDeviceIdAsMac, isSuccessfulStatusCode, logDeviceStatusCode, safeStringify, sleep } from '../utils.js'
18
18
 
19
19
  export abstract class deviceBase {
20
20
  public readonly api: API
@@ -147,7 +147,9 @@ export abstract class deviceBase {
147
147
  device.scanDuration !== 0 && { scanDuration: device.scanDuration },
148
148
  device.offline === true && { offline: device.offline },
149
149
  device.maxRetry !== 0 && { maxRetry: device.maxRetry },
150
- device.webhook === true && { webhook: device.webhook },
150
+ (device.webhook === true || (device.webhook === undefined && this.config.options?.webhook === true)) && {
151
+ webhook: device.webhook !== undefined ? device.webhook : this.config.options?.webhook,
152
+ },
151
153
  device.connectionType !== '' && { connectionType: device.connectionType },
152
154
  device.disablePlatformBLE !== false && { disablePlatformBLE: device.disablePlatformBLE },
153
155
  device.external === true && { external: device.external },
@@ -288,11 +290,7 @@ export abstract class deviceBase {
288
290
  this.device.bleMac = formattedDeviceId
289
291
  this.debugLog(`bleMac: ${this.device.bleMac}`)
290
292
  this.historyService = device.history
291
- ? new this.platform.fakegatoAPI('room', accessory, {
292
- log: this.platform.log,
293
- storage: 'fs',
294
- filename: `${hostname().split('.')[0]}_${this.device.bleMac}_persist.json`,
295
- })
293
+ ? new this.platform.fakegatoAPI('room', accessory, { log: this.platform.log, storage: 'fs', filename: `${hostname().split('.')[0]}_${this.device.bleMac}_persist.json` })
296
294
  : null
297
295
  } catch (error) {
298
296
  this.errorLog(`failed to format device ID as MAC, Error: ${error}`)
@@ -377,8 +375,12 @@ export abstract class deviceBase {
377
375
  return { body: response, statusCode }
378
376
  }
379
377
 
380
- async successfulStatusCodes<T extends { statusCode: number }>(deviceStatus: T) {
381
- return (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)
378
+ /**
379
+ * Check if status code indicates success (100 or 200)
380
+ * @deprecated Use isSuccessfulStatusCode from utils.ts instead
381
+ */
382
+ async successfulStatusCodes<T extends { statusCode: number }>(deviceStatus: T): Promise<boolean> {
383
+ return isSuccessfulStatusCode(deviceStatus.statusCode)
382
384
  }
383
385
 
384
386
  /**
@@ -584,6 +586,12 @@ export abstract class deviceBase {
584
586
  bleModelName: SwitchBotBLEModelName.LockPro,
585
587
  bleModelFriendlyName: SwitchBotBLEModelFriendlyName.LockPro,
586
588
  },
589
+ 'Smart Lock Ultra': {
590
+ model: SwitchBotModel.LockPro,
591
+ bleModel: SwitchBotBLEModel.LockPro,
592
+ bleModelName: SwitchBotBLEModelName.LockPro,
593
+ bleModelFriendlyName: SwitchBotBLEModelFriendlyName.LockPro,
594
+ },
587
595
  'Color Bulb': {
588
596
  model: SwitchBotModel.ColorBulb,
589
597
  bleModel: SwitchBotBLEModel.ColorBulb,
@@ -709,103 +717,174 @@ export abstract class deviceBase {
709
717
  .updateValue(deviceVersion)
710
718
  accessory.context.version = deviceVersion
711
719
  this.debugSuccessLog(`version: ${accessory.context.version}`)
720
+
721
+ // Expose effective webhook setting on accessory context (parity with Matter)
722
+ try {
723
+ const effectiveWebhook = device.webhook !== undefined ? device.webhook : (this.config.options?.webhook === true ? true : undefined)
724
+ if (effectiveWebhook !== undefined) {
725
+ accessory.context.webhook = effectiveWebhook
726
+ this.debugLog(`Effective webhook for ${device.deviceId}: ${String(effectiveWebhook)}`)
727
+ }
728
+ } catch (e: any) {
729
+ this.debugLog(`Failed to set webhook context for ${device.deviceId}: ${e?.message ?? e}`)
730
+ }
712
731
  }
713
732
 
714
733
  async statusCode(statusCode: number): Promise<void> {
715
- const statusMessages = {
716
- 151: 'Command not supported by this deviceType',
717
- 152: 'Device not found',
718
- 160: 'Command is not supported',
719
- 161: 'Device is offline',
720
- 171: `Hub Device is offline. Hub: ${this.device.hubDeviceId}`,
721
- 190: 'Device internal error due to device states not synchronized with server, or command format is invalid',
722
- 100: 'Command successfully sent',
723
- 200: 'Request successful',
724
- 400: 'Bad Request, an invalid payload request',
725
- 401: 'Unauthorized, Authorization for the API is required, but the request has not been authenticated',
726
- 403: 'Forbidden, The request has been authenticated but does not have appropriate permissions, or a requested resource is not found',
727
- 404: 'Not Found, Specifies the requested path does not exist',
728
- 406: 'Not Acceptable, a MIME type has been requested via the Accept header for a value not supported by the server',
729
- 415: 'Unsupported Media Type, a contentType header has been defined that is not supported by the server',
730
- 422: 'Unprocessable Entity: The server cannot process the request, often due to exceeded API limits.',
731
- 429: 'Too Many Requests, exceeded the number of requests allowed for a given time window',
732
- 500: 'Internal Server Error, An unexpected error occurred. These errors should be rare',
733
- }
734
- if (statusCode === 171 && (this.device.hubDeviceId === this.device.deviceId || this.device.hubDeviceId === '000000000000')) {
735
- this.debugErrorLog(`statusCode 171 changed to 161: hubDeviceId ${this.device.hubDeviceId} matches deviceId ${this.device.deviceId}, device is its own hub.`)
736
- statusCode = 161
737
- }
738
- const logMessage = statusMessages[statusCode] || `Unknown statusCode: ${statusCode}, Submit Bugs Here: https://tinyurl.com/SwitchBotBug`
739
- const logMethod = [100, 200].includes(statusCode) ? 'debugLog' : statusMessages[statusCode] ? 'errorLog' : 'infoLog'
740
- this[logMethod](`${logMessage}, statusCode: ${statusCode}`)
734
+ await logDeviceStatusCode(
735
+ statusCode,
736
+ {
737
+ debugLog: this.debugLog.bind(this),
738
+ debugErrorLog: this.debugErrorLog.bind(this),
739
+ errorLog: this.errorLog.bind(this),
740
+ infoLog: this.infoLog.bind(this),
741
+ },
742
+ this.device.deviceId,
743
+ this.device.hubDeviceId,
744
+ )
741
745
  }
742
746
 
743
747
  /**
744
748
  * Logging for Device
749
+ * Modernized logging methods with improved type safety and formatting
745
750
  */
746
- infoLog(...log: any[]): void {
747
- if (this.enablingDeviceLogging()) {
748
- this.log.info(`${this.device.deviceType}: ${this.accessory.displayName}`, String(...log))
751
+ infoLog(message: string, ...args: unknown[]): void {
752
+ if (!this.enablingDeviceLogging()) {
753
+ return
749
754
  }
755
+ const prefix = `${this.device.deviceType}: ${this.accessory.displayName}`
756
+ this.logWith('info', prefix, undefined, this.formatLogMessage(message, ...args))
750
757
  }
751
758
 
752
- successLog(...log: any[]): void {
753
- if (this.enablingDeviceLogging()) {
754
- this.log.success(`${this.device.deviceType}: ${this.accessory.displayName}`, String(...log))
759
+ successLog(message: string, ...args: unknown[]): void {
760
+ if (!this.enablingDeviceLogging()) {
761
+ return
755
762
  }
763
+ const prefix = `${this.device.deviceType}: ${this.accessory.displayName}`
764
+ this.logWith('success', prefix, undefined, this.formatLogMessage(message, ...args))
756
765
  }
757
766
 
758
- debugSuccessLog(...log: any[]): void {
759
- if (this.enablingDeviceLogging()) {
760
- if (this.loggingIsDebug()) {
761
- this.log.success(`[DEBUG] ${this.device.deviceType}: ${this.accessory.displayName}`, String(...log))
762
- }
767
+ debugSuccessLog(message: string, ...args: unknown[]): void {
768
+ if (!this.enablingDeviceLogging() || !this.loggingIsDebug()) {
769
+ return
763
770
  }
771
+ const prefix = `[DEBUG] ${this.device.deviceType}: ${this.accessory.displayName}`
772
+ this.logWith('success', prefix, 'debugSuccessLog', this.formatLogMessage(message, ...args))
764
773
  }
765
774
 
766
- warnLog(...log: any[]): void {
767
- if (this.enablingDeviceLogging()) {
768
- this.log.warn(`${this.device.deviceType}: ${this.accessory.displayName}`, String(...log))
775
+ warnLog(message: string, ...args: unknown[]): void {
776
+ if (!this.enablingDeviceLogging()) {
777
+ return
769
778
  }
779
+ const prefix = `${this.device.deviceType}: ${this.accessory.displayName}`
780
+ this.logWith('warn', prefix, undefined, this.formatLogMessage(message, ...args))
770
781
  }
771
782
 
772
- debugWarnLog(...log: any[]): void {
773
- if (this.enablingDeviceLogging()) {
774
- if (this.loggingIsDebug()) {
775
- this.log.warn(`[DEBUG] ${this.device.deviceType}: ${this.accessory.displayName}`, String(...log))
776
- }
783
+ debugWarnLog(message: string, ...args: unknown[]): void {
784
+ if (!this.enablingDeviceLogging() || !this.loggingIsDebug()) {
785
+ return
777
786
  }
787
+ const prefix = `[DEBUG] ${this.device.deviceType}: ${this.accessory.displayName}`
788
+ this.logWith('warn', prefix, 'debugWarnLog', this.formatLogMessage(message, ...args))
778
789
  }
779
790
 
780
- errorLog(...log: any[]): void {
781
- if (this.enablingDeviceLogging()) {
782
- this.log.error(`${this.device.deviceType}: ${this.accessory.displayName}`, String(...log))
791
+ errorLog(message: string, ...args: unknown[]): void {
792
+ if (!this.enablingDeviceLogging()) {
793
+ return
783
794
  }
795
+ const prefix = `${this.device.deviceType}: ${this.accessory.displayName}`
796
+ this.logWith('error', prefix, undefined, this.formatLogMessage(message, ...args))
784
797
  }
785
798
 
786
- debugErrorLog(...log: any[]): void {
787
- if (this.enablingDeviceLogging()) {
788
- if (this.loggingIsDebug()) {
789
- this.log.error(`[DEBUG] ${this.device.deviceType}: ${this.accessory.displayName}`, String(...log))
790
- }
799
+ debugErrorLog(message: string, ...args: unknown[]): void {
800
+ if (!this.enablingDeviceLogging() || !this.loggingIsDebug()) {
801
+ return
791
802
  }
803
+ const prefix = `[DEBUG] ${this.device.deviceType}: ${this.accessory.displayName}`
804
+ this.logWith('error', prefix, 'debugErrorLog', this.formatLogMessage(message, ...args))
792
805
  }
793
806
 
794
- debugLog(...log: any[]): void {
795
- if (this.enablingDeviceLogging()) {
796
- if (this.deviceLogging === 'debug') {
797
- this.log.info(`[DEBUG] ${this.device.deviceType}: ${this.accessory.displayName}`, String(...log))
798
- } else if (this.deviceLogging === 'debugMode') {
799
- this.log.debug(`${this.device.deviceType}: ${this.accessory.displayName}`, String(...log))
807
+ debugLog(message: string, ...args: unknown[]): void {
808
+ if (!this.enablingDeviceLogging()) {
809
+ return
810
+ }
811
+ // debug behaves differently depending on debug vs debugMode
812
+ const prefix = this.deviceLogging === 'debug'
813
+ ? `[DEBUG] ${this.device.deviceType}: ${this.accessory.displayName}`
814
+ : `${this.device.deviceType}: ${this.accessory.displayName}`
815
+ this.logWith('debug', prefix, 'debugLog', this.formatLogMessage(message, ...args))
816
+ }
817
+
818
+ /**
819
+ * Format log message with arguments - handles both legacy and modern patterns
820
+ */
821
+ private formatLogMessage(message: string | any, ...args: unknown[]): string {
822
+ // Handle legacy String(...log) pattern for backward compatibility
823
+ if (typeof message !== 'string' && args.length === 0) {
824
+ return String(message)
825
+ }
826
+
827
+ // Modern pattern: format message with arguments
828
+ if (args.length === 0) {
829
+ return String(message)
830
+ }
831
+
832
+ // Format arguments similar to how platform logging works
833
+ const formattedArgs = args.map((arg) => {
834
+ if (typeof arg === 'string') {
835
+ return arg
836
+ }
837
+ try {
838
+ return JSON.stringify(arg)
839
+ } catch {
840
+ return String(arg)
841
+ }
842
+ })
843
+
844
+ return args.length > 0 ? `${message} ${formattedArgs.join(' ')}` : String(message)
845
+ }
846
+
847
+ // Generic logger used by device log helpers. Attempts to call a platform
848
+ // helper method first (if present), then falls back to the local logger.
849
+ protected logWith(level: string, message: string, platformMethodName?: string, payload?: string): void {
850
+ const method = platformMethodName ?? `${level}Log`
851
+ const pFn = (this.platform as any)?.[method]
852
+ if (typeof pFn === 'function') {
853
+ try {
854
+ // Forward both a short message (title) and optional payload to the
855
+ // platform logger so it can format/attach metadata consistently.
856
+ if (payload !== undefined) {
857
+ pFn(message, payload)
858
+ } else {
859
+ pFn(message)
860
+ }
861
+ return
862
+ } catch (_err) {
863
+ // fallthrough to local logger
864
+ }
865
+ }
866
+ const map: Record<string, string> = {
867
+ info: 'info',
868
+ success: 'success',
869
+ debug: 'debug',
870
+ warn: 'warn',
871
+ error: 'error',
872
+ }
873
+ const local = (this.log as any)[map[level] ?? level]
874
+ if (typeof local === 'function') {
875
+ if (payload !== undefined) {
876
+ local.call(this.log, message, payload)
877
+ } else {
878
+ local.call(this.log, message)
800
879
  }
801
880
  }
802
881
  }
803
882
 
804
883
  loggingIsDebug(): boolean {
805
- return this.deviceLogging === 'debugMode' || this.deviceLogging === 'debug'
884
+ return deviceLoggingIsDebug(this.deviceLogging)
806
885
  }
807
886
 
808
887
  enablingDeviceLogging(): boolean {
809
- return this.deviceLogging === 'debugMode' || this.deviceLogging === 'debug' || this.deviceLogging === 'standard'
888
+ return deviceLoggingEnabled(this.deviceLogging, this.platform.platformLogging)
810
889
  }
811
890
  }
@@ -329,7 +329,7 @@ export class Fan extends deviceBase {
329
329
  // Update HomeKit
330
330
  if (serviceData.model === SwitchBotBLEModel.Unknown && SwitchBotBLEModelName.Unknown) {
331
331
  this.serviceData = serviceData
332
- if (serviceData !== undefined || serviceData !== null) {
332
+ if (serviceData !== undefined && serviceData !== null) {
333
333
  await this.BLEparseStatus()
334
334
  await this.updateHomeKitCharacteristics()
335
335
  } else {
@@ -355,7 +355,7 @@ export class Fan extends deviceBase {
355
355
  this.platform.bleEventHandler[this.device.bleMac] = async (context: batteryCirculatorFanServiceData) => {
356
356
  try {
357
357
  this.serviceData = context
358
- if (context !== undefined || context !== null) {
358
+ if (context !== undefined && context !== null) {
359
359
  this.debugLog(`received BLE: ${JSON.stringify(context)}`)
360
360
  await this.BLEparseStatus()
361
361
  await this.updateHomeKitCharacteristics()
@@ -400,7 +400,7 @@ export class Fan extends deviceBase {
400
400
  this.platform.webhookEventHandler[this.device.deviceId] = async (context: batteryCirculatorFanWebhookContext) => {
401
401
  try {
402
402
  this.webhookContext = context
403
- if (context !== undefined || context !== null) {
403
+ if (context !== undefined && context !== null) {
404
404
  this.debugLog(`received Webhook: ${JSON.stringify(context)}`)
405
405
  await this.parseStatusWebhook()
406
406
  await this.updateHomeKitCharacteristics()
@@ -468,13 +468,18 @@ export class Fan extends deviceBase {
468
468
  switchBotBLE
469
469
  .discover({ model: this.device.bleModel, id: this.device.bleMac })
470
470
  .then(async (device_list: SwitchbotDevice[]) => {
471
+ this.debugLog(`device_list: ${JSON.stringify(device_list)}`)
471
472
  return await this.retryBLE({
472
473
  max: this.maxRetryBLE(),
473
474
  fn: async () => {
474
- if (this.Fan.Active) {
475
- return await (device_list[0] as any).turnOn()
475
+ if (Array.isArray(device_list) && device_list.length > 0) {
476
+ if (this.Fan.Active) {
477
+ return await (device_list[0] as any).turnOn()
478
+ } else {
479
+ return await (device_list[0] as any).turnOff()
480
+ }
476
481
  } else {
477
- return await (device_list[0] as any).turnOff()
482
+ throw new Error('No devices found during discovery.')
478
483
  }
479
484
  },
480
485
  })
@@ -211,8 +211,9 @@ export class Hub extends deviceBase {
211
211
 
212
212
  // CurrentTemperature
213
213
  if (!(this.device as hubConfig).hide_temperature && this.TemperatureSensor?.Service) {
214
- this.TemperatureSensor.CurrentTemperature = this.deviceStatus.temperature
215
- this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}°c`)
214
+ // OpenAPI returns Celsius; convert if user configured a different unit
215
+ this.TemperatureSensor.CurrentTemperature = convertUnits(this.deviceStatus.temperature, 'CELSIUS', (this.device as hubConfig).convertUnitTo)
216
+ this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}`)
216
217
  }
217
218
 
218
219
  // LightSensor
@@ -297,7 +298,7 @@ export class Hub extends deviceBase {
297
298
  if ((serviceData.model === SwitchBotBLEModel.Hub2 && serviceData.modelName === SwitchBotBLEModelName.Hub2)
298
299
  || (serviceData.model === SwitchBotBLEModel.Hub3 && serviceData.modelName === SwitchBotBLEModelName.Hub3)) {
299
300
  this.serviceData = serviceData
300
- if (serviceData !== undefined || serviceData !== null) {
301
+ if (serviceData !== undefined && serviceData !== null) {
301
302
  await this.BLEparseStatus()
302
303
  await this.updateHomeKitCharacteristics()
303
304
  } else {
@@ -323,7 +324,7 @@ export class Hub extends deviceBase {
323
324
  this.platform.bleEventHandler[this.device.bleMac] = async (context: hub2ServiceData) => {
324
325
  try {
325
326
  this.serviceData = context
326
- if (context !== undefined || context !== null) {
327
+ if (context !== undefined && context !== null) {
327
328
  this.debugLog(`received BLE: ${JSON.stringify(context)}`)
328
329
  await this.BLEparseStatus()
329
330
  await this.updateHomeKitCharacteristics()
@@ -368,7 +369,7 @@ export class Hub extends deviceBase {
368
369
  this.platform.webhookEventHandler[this.device.deviceId] = async (context: hub2WebhookContext) => {
369
370
  try {
370
371
  this.webhookContext = context
371
- if (context !== undefined || context !== null) {
372
+ if (context !== undefined && context !== null) {
372
373
  this.debugLog(`received Webhook: ${JSON.stringify(context)}`)
373
374
  await this.parseStatusWebhook()
374
375
  await this.updateHomeKitCharacteristics()
@@ -42,6 +42,12 @@ export class Humidifier extends deviceBase {
42
42
  CurrentTemperature: CharacteristicValue
43
43
  }
44
44
 
45
+ private DryingFilterSwitch?: {
46
+ Name: CharacteristicValue
47
+ Service: Service
48
+ On: CharacteristicValue
49
+ }
50
+
45
51
  // OpenAPI
46
52
  deviceStatus!: humidifierStatus | humidifier2Status
47
53
 
@@ -132,6 +138,31 @@ export class Humidifier extends deviceBase {
132
138
  })
133
139
  }
134
140
 
141
+ // Initialize optional Drying Filter Switch (Humidifier2 only)
142
+ if ((device as humidifierConfig).configDeviceType === 'Humidifier2' && (device as humidifierConfig).activate_dryingfilter) {
143
+ accessory.context.DryingFilterSwitch = accessory.context.DryingFilterSwitch ?? {}
144
+ this.DryingFilterSwitch = {
145
+ Name: `${accessory.displayName} Drying Filter`,
146
+ Service: accessory.getService(`${accessory.displayName} Drying Filter`) ?? accessory.addService(this.hap.Service.Switch, `${accessory.displayName} Drying Filter`, 'DryingFilterSwitch') as Service,
147
+ On: false,
148
+ }
149
+ accessory.context.DryingFilterSwitch = this.DryingFilterSwitch as object
150
+
151
+ this.DryingFilterSwitch.Service.setCharacteristic(this.hap.Characteristic.Name, this.DryingFilterSwitch.Name)
152
+ .getCharacteristic(this.hap.Characteristic.On)
153
+ .onGet(() => {
154
+ return this.DryingFilterSwitch!.On
155
+ })
156
+ .onSet(this.DryingFilterOnSet.bind(this))
157
+ this.debugLog('Initialized Drying Filter Switch Service')
158
+ } else {
159
+ const svc = this.accessory.getServiceById(this.hap.Service.Switch, 'DryingFilterSwitch')
160
+ if (svc) {
161
+ this.debugLog('Removing Drying Filter Switch Service')
162
+ this.accessory.removeService(svc)
163
+ }
164
+ }
165
+
135
166
  // Retrieve initial values and updateHomekit
136
167
  try {
137
168
  this.debugLog('Retrieve initial values and update Homekit')
@@ -326,7 +357,7 @@ export class Humidifier extends deviceBase {
326
357
  // Update HomeKit
327
358
  if (serviceData.model === SwitchBotBLEModel.Humidifier && serviceData.modelName === SwitchBotBLEModelName.Humidifier) {
328
359
  this.serviceData = serviceData
329
- if (serviceData !== undefined || serviceData !== null) {
360
+ if (serviceData !== undefined && serviceData !== null) {
330
361
  await this.BLEparseStatus()
331
362
  await this.updateHomeKitCharacteristics()
332
363
  } else {
@@ -352,7 +383,7 @@ export class Humidifier extends deviceBase {
352
383
  this.platform.bleEventHandler[this.device.bleMac] = async (context: humidifierServiceData | humidifier2ServiceData) => {
353
384
  try {
354
385
  this.serviceData = context
355
- if (context !== undefined || context !== null) {
386
+ if (context !== undefined && context !== null) {
356
387
  this.debugLog(`received BLE: ${JSON.stringify(context)}`)
357
388
  await this.BLEparseStatus()
358
389
  await this.updateHomeKitCharacteristics()
@@ -397,7 +428,7 @@ export class Humidifier extends deviceBase {
397
428
  this.platform.webhookEventHandler[this.device.deviceId] = async (context: humidifierWebhookContext | humidifier2WebhookContext) => {
398
429
  try {
399
430
  this.webhookContext = context
400
- if (context !== undefined || context !== null) {
431
+ if (context !== undefined && context !== null) {
401
432
  this.debugLog(`received Webhook: ${JSON.stringify(context)}`)
402
433
  await this.parseStatusWebhook()
403
434
  await this.updateHomeKitCharacteristics()
@@ -446,7 +477,17 @@ export class Humidifier extends deviceBase {
446
477
  switchBotBLE
447
478
  .discover({ model: this.device.bleModel, quick: true, id: this.device.bleMac })
448
479
  .then(async (device_list: SwitchbotDevice[]) => {
449
- return await (device_list[0] as WoHumi).percentage(Number(this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold))
480
+ this.debugLog(`device_list: ${JSON.stringify(device_list)}`)
481
+ return await this.retryBLE({
482
+ max: this.maxRetryBLE(),
483
+ fn: async () => {
484
+ if (device_list.length > 0) {
485
+ return await (device_list[0] as WoHumi).percentage(Number(this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold))
486
+ } else {
487
+ throw new Error('No devices found during discovery.')
488
+ }
489
+ },
490
+ })
450
491
  })
451
492
  .then(async () => {
452
493
  this.successLog(`RelativeHumidityHumidifierThreshold: ${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold} sent over BLE, sent successfully`)
@@ -630,6 +671,58 @@ export class Humidifier extends deviceBase {
630
671
  if (!(this.device as humidifierConfig).hide_temperature && this.TemperatureSensor?.Service) {
631
672
  await this.updateCharacteristic(this.TemperatureSensor.Service, this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor.CurrentTemperature, 'CurrentTemperature')
632
673
  }
674
+ // DryingFilter switch is momentary; ensure it reflects current cached state
675
+ if (this.DryingFilterSwitch?.Service) {
676
+ await this.updateCharacteristic(this.DryingFilterSwitch.Service, this.hap.Characteristic.On, this.DryingFilterSwitch.On, 'DryingFilter.On')
677
+ }
678
+ }
679
+
680
+ /**
681
+ * Handle requests to set the Drying Filter switch (Humidifier2 only)
682
+ */
683
+ async DryingFilterOnSet(value: CharacteristicValue): Promise<void> {
684
+ if (!this.DryingFilterSwitch?.Service) {
685
+ this.debugLog('DryingFilterOnSet called but service not initialized')
686
+ return
687
+ }
688
+ this.DryingFilterSwitch.On = value
689
+ if (value !== true) {
690
+ // Switch is momentary; ignore turning off manually
691
+ this.debugLog('Drying Filter switch set to false (ignored)')
692
+ return
693
+ }
694
+
695
+ this.infoLog('Trigger Drying Filter mode')
696
+ if (this.OpenAPI && this.platform.config.credentials?.token) {
697
+ const bodyChange: bodyChange = {
698
+ command: 'setMode',
699
+ parameter: '8',
700
+ commandType: 'command',
701
+ }
702
+ this.debugLog(`DryingFilterOnSet, SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`)
703
+ try {
704
+ const deviceStatus = await this.pushChangeRequest(bodyChange)
705
+ this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`)
706
+ if (await this.successfulStatusCodes(deviceStatus)) {
707
+ this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`)
708
+ } else {
709
+ await this.statusCode(deviceStatus.statusCode)
710
+ }
711
+ } catch (e: any) {
712
+ await this.apiError(e)
713
+ this.errorLog(`failed DryingFilterOnSet with ${this.device.connectionType} Connection, Error Message: ${JSON.stringify(e.message)}`)
714
+ }
715
+ } else {
716
+ this.debugWarnLog('Drying Filter requires OpenAPI; BLE not supported for this action')
717
+ }
718
+
719
+ // Auto-reset the momentary switch back to off after a short delay
720
+ setTimeout(async () => {
721
+ if (this.DryingFilterSwitch) {
722
+ this.DryingFilterSwitch.On = false
723
+ await this.updateHomeKitCharacteristics()
724
+ }
725
+ }, 1500)
633
726
  }
634
727
 
635
728
  async BLEPushConnection() {
@@ -178,27 +178,41 @@ export class IOSensor extends deviceBase {
178
178
  async BLEparseStatus(): Promise<void> {
179
179
  this.debugLog('BLEparseStatus')
180
180
  this.debugLog(`(battery, temperature, humidity) = BLE:(${this.serviceData.battery}, ${this.serviceData.celsius}, ${this.serviceData.humidity}), current:(${this.Battery.BatteryLevel}, ${this.TemperatureSensor?.CurrentTemperature}, ${this.HumiditySensor?.CurrentRelativeHumidity})`)
181
+
181
182
  // Battery Info
182
183
  if ('battery' in this.serviceData) {
183
- // BatteryLevel
184
- this.Battery.BatteryLevel = this.serviceData.battery
185
- this.debugLog(`BatteryLevel: ${this.Battery.BatteryLevel}`)
186
- // StatusLowBattery
187
- this.Battery.StatusLowBattery = this.Battery.BatteryLevel < 10
188
- ? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW
189
- : this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL
190
- this.debugLog(`StatusLowBattery: ${this.Battery.StatusLowBattery}`)
184
+ if (typeof this.serviceData.battery === 'number') {
185
+ this.Battery.BatteryLevel = this.serviceData.battery
186
+ this.debugLog(`BatteryLevel: ${this.Battery.BatteryLevel}`)
187
+ this.Battery.StatusLowBattery = this.Battery.BatteryLevel < 10
188
+ ? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW
189
+ : this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL
190
+ this.debugLog(`StatusLowBattery: ${this.Battery.StatusLowBattery}`)
191
+ } else {
192
+ this.debugWarnLog(`BLE battery is undefined; skipping battery update.`)
193
+ }
191
194
  }
195
+
192
196
  // CurrentRelativeHumidity
193
197
  if (!(this.device as indoorOutdoorSensorConfig).hide_humidity && this.HumiditySensor?.Service) {
194
- this.HumiditySensor.CurrentRelativeHumidity = validHumidity(this.serviceData.humidity, 0, 100)
195
- this.debugLog(`CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}%`)
198
+ if (typeof this.serviceData.humidity === 'number') {
199
+ this.HumiditySensor.CurrentRelativeHumidity = validHumidity(this.serviceData.humidity, 0, 100)
200
+ this.debugLog(`CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}%`)
201
+ } else {
202
+ this.debugWarnLog(`BLE humidity is undefined; skipping humidity update.`)
203
+ }
196
204
  }
205
+
197
206
  // Current Temperature
198
207
  if (!(this.device as indoorOutdoorSensorConfig).hide_temperature && this.TemperatureSensor?.Service) {
199
- const CELSIUS = this.serviceData.celsius < 0 ? 0 : this.serviceData.celsius > 100 ? 100 : this.serviceData.celsius
200
- this.TemperatureSensor.CurrentTemperature = CELSIUS
201
- this.debugLog(`Temperature: ${this.TemperatureSensor.CurrentTemperature}°c`)
208
+ if (typeof this.serviceData.celsius === 'number') {
209
+ const c = this.serviceData.celsius
210
+ const CELSIUS = c < 0 ? 0 : c > 100 ? 100 : c
211
+ this.TemperatureSensor.CurrentTemperature = CELSIUS
212
+ this.debugLog(`Temperature: ${this.TemperatureSensor.CurrentTemperature}°c`)
213
+ } else {
214
+ this.debugWarnLog(`BLE temperature is undefined; skipping temperature update.`)
215
+ }
202
216
  }
203
217
  }
204
218
 
@@ -224,8 +238,9 @@ export class IOSensor extends deviceBase {
224
238
 
225
239
  // Current Temperature
226
240
  if (!(this.device as indoorOutdoorSensorConfig).hide_temperature && this.TemperatureSensor?.Service) {
227
- this.TemperatureSensor.CurrentTemperature = this.deviceStatus.temperature
228
- this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}°c`)
241
+ // OpenAPI returns Celsius; convert if user configured a different unit
242
+ this.TemperatureSensor.CurrentTemperature = convertUnits(this.deviceStatus.temperature as number, 'CELSIUS', (this.device as indoorOutdoorSensorConfig).convertUnitTo)
243
+ this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}`)
229
244
  }
230
245
 
231
246
  // Firmware Version
@@ -252,12 +267,12 @@ export class IOSensor extends deviceBase {
252
267
  this.warnLog(`received a non-CELSIUS Webhook scale: ${this.webhookContext.scale}, Use the *convertUnitsTo* config under Hub settings, if displaying incorrectly in HomeKit.`)
253
268
  }
254
269
  // CurrentRelativeHumidity
255
- if ((this.device as indoorOutdoorSensorConfig).hide_humidity && this.HumiditySensor?.Service) {
270
+ if (!(this.device as indoorOutdoorSensorConfig).hide_humidity && this.HumiditySensor?.Service) {
256
271
  this.HumiditySensor.CurrentRelativeHumidity = this.webhookContext.humidity
257
272
  this.debugLog(`CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}%`)
258
273
  }
259
274
  // CurrentTemperature
260
- if ((this.device as indoorOutdoorSensorConfig).hide_temperature && this.TemperatureSensor?.Service) {
275
+ if (!(this.device as indoorOutdoorSensorConfig).hide_temperature && this.TemperatureSensor?.Service) {
261
276
  this.TemperatureSensor.CurrentTemperature = convertUnits(this.webhookContext.temperature, this.webhookContext.scale, (this.device as indoorOutdoorSensorConfig).convertUnitTo)
262
277
  this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}°c`)
263
278
  }
@@ -290,7 +305,7 @@ export class IOSensor extends deviceBase {
290
305
  // Update HomeKit
291
306
  if (serviceData.model === SwitchBotBLEModel.OutdoorMeter && serviceData.modelName === SwitchBotBLEModelName.OutdoorMeter) {
292
307
  this.serviceData = serviceData
293
- if (serviceData !== undefined || serviceData !== null) {
308
+ if (serviceData !== undefined && serviceData !== null) {
294
309
  await this.BLEparseStatus()
295
310
  await this.updateHomeKitCharacteristics()
296
311
  } else {
@@ -316,7 +331,7 @@ export class IOSensor extends deviceBase {
316
331
  this.platform.bleEventHandler[this.device.bleMac] = async (context: outdoorMeterServiceData) => {
317
332
  try {
318
333
  this.serviceData = context
319
- if (context !== undefined || context !== null) {
334
+ if (context !== undefined && context !== null) {
320
335
  this.debugLog(`received BLE: ${JSON.stringify(context)}`)
321
336
  await this.BLEparseStatus()
322
337
  await this.updateHomeKitCharacteristics()
@@ -351,7 +366,7 @@ export class IOSensor extends deviceBase {
351
366
  }
352
367
  } catch (e: any) {
353
368
  await this.apiError(e)
354
- this.errorLog(`failed openAPIRefreshStatus with ${this.device.connectionType} Connection, Error Message: ${JSON.stringify(e.message)}`)
369
+ this.errorLog(`failed openAPIRefreshStatus with ${this.device.connectionType} Connection, Error: ${e.message ?? e}`)
355
370
  }
356
371
  }
357
372
 
@@ -361,7 +376,7 @@ export class IOSensor extends deviceBase {
361
376
  this.platform.webhookEventHandler[this.device.deviceId] = async (context: outdoorMeterWebhookContext) => {
362
377
  try {
363
378
  this.webhookContext = context
364
- if (context !== undefined || context !== null) {
379
+ if (context !== undefined && context !== null) {
365
380
  this.debugLog(`received Webhook: ${JSON.stringify(context)}`)
366
381
  await this.parseStatusWebhook()
367
382
  await this.updateHomeKitCharacteristics()