@switchbot/homebridge-switchbot 3.5.1-beta.9 → 3.5.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 (159) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/config.schema.json +19 -3
  3. package/dist/device/blindtilt.d.ts +6 -4
  4. package/dist/device/blindtilt.d.ts.map +1 -1
  5. package/dist/device/blindtilt.js +60 -46
  6. package/dist/device/blindtilt.js.map +1 -1
  7. package/dist/device/bot.d.ts +6 -3
  8. package/dist/device/bot.d.ts.map +1 -1
  9. package/dist/device/bot.js +59 -25
  10. package/dist/device/bot.js.map +1 -1
  11. package/dist/device/ceilinglight.d.ts +6 -3
  12. package/dist/device/ceilinglight.d.ts.map +1 -1
  13. package/dist/device/ceilinglight.js +47 -29
  14. package/dist/device/ceilinglight.js.map +1 -1
  15. package/dist/device/colorbulb.d.ts +6 -3
  16. package/dist/device/colorbulb.d.ts.map +1 -1
  17. package/dist/device/colorbulb.js +53 -36
  18. package/dist/device/colorbulb.js.map +1 -1
  19. package/dist/device/contact.d.ts +6 -3
  20. package/dist/device/contact.d.ts.map +1 -1
  21. package/dist/device/contact.js +45 -31
  22. package/dist/device/contact.js.map +1 -1
  23. package/dist/device/curtain.d.ts +6 -4
  24. package/dist/device/curtain.d.ts.map +1 -1
  25. package/dist/device/curtain.js +41 -27
  26. package/dist/device/curtain.js.map +1 -1
  27. package/dist/device/device.d.ts +6 -3
  28. package/dist/device/device.d.ts.map +1 -1
  29. package/dist/device/device.js +59 -152
  30. package/dist/device/device.js.map +1 -1
  31. package/dist/device/fan.d.ts +6 -3
  32. package/dist/device/fan.d.ts.map +1 -1
  33. package/dist/device/fan.js +53 -32
  34. package/dist/device/fan.js.map +1 -1
  35. package/dist/device/hub.d.ts +6 -3
  36. package/dist/device/hub.d.ts.map +1 -1
  37. package/dist/device/hub.js +49 -30
  38. package/dist/device/hub.js.map +1 -1
  39. package/dist/device/humidifier.d.ts +6 -3
  40. package/dist/device/humidifier.d.ts.map +1 -1
  41. package/dist/device/humidifier.js +47 -28
  42. package/dist/device/humidifier.js.map +1 -1
  43. package/dist/device/iosensor.d.ts +6 -3
  44. package/dist/device/iosensor.d.ts.map +1 -1
  45. package/dist/device/iosensor.js +44 -27
  46. package/dist/device/iosensor.js.map +1 -1
  47. package/dist/device/lightstrip.d.ts +6 -3
  48. package/dist/device/lightstrip.d.ts.map +1 -1
  49. package/dist/device/lightstrip.js +46 -28
  50. package/dist/device/lightstrip.js.map +1 -1
  51. package/dist/device/lock.d.ts +6 -3
  52. package/dist/device/lock.d.ts.map +1 -1
  53. package/dist/device/lock.js +38 -25
  54. package/dist/device/lock.js.map +1 -1
  55. package/dist/device/meter.d.ts +6 -3
  56. package/dist/device/meter.d.ts.map +1 -1
  57. package/dist/device/meter.js +43 -27
  58. package/dist/device/meter.js.map +1 -1
  59. package/dist/device/meterplus.d.ts +6 -3
  60. package/dist/device/meterplus.d.ts.map +1 -1
  61. package/dist/device/meterplus.js +42 -27
  62. package/dist/device/meterplus.js.map +1 -1
  63. package/dist/device/motion.d.ts +6 -3
  64. package/dist/device/motion.d.ts.map +1 -1
  65. package/dist/device/motion.js +37 -24
  66. package/dist/device/motion.js.map +1 -1
  67. package/dist/device/plug.d.ts +6 -3
  68. package/dist/device/plug.d.ts.map +1 -1
  69. package/dist/device/plug.js +30 -17
  70. package/dist/device/plug.js.map +1 -1
  71. package/dist/device/robotvacuumcleaner.d.ts +6 -3
  72. package/dist/device/robotvacuumcleaner.d.ts.map +1 -1
  73. package/dist/device/robotvacuumcleaner.js +37 -22
  74. package/dist/device/robotvacuumcleaner.js.map +1 -1
  75. package/dist/device/waterdetector.d.ts +6 -3
  76. package/dist/device/waterdetector.d.ts.map +1 -1
  77. package/dist/device/waterdetector.js +43 -26
  78. package/dist/device/waterdetector.js.map +1 -1
  79. package/dist/homebridge-ui/public/index.html +2 -2
  80. package/dist/irdevice/airconditioner.d.ts +1 -3
  81. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  82. package/dist/irdevice/airconditioner.js +67 -177
  83. package/dist/irdevice/airconditioner.js.map +1 -1
  84. package/dist/irdevice/airpurifier.d.ts +0 -1
  85. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  86. package/dist/irdevice/airpurifier.js +24 -53
  87. package/dist/irdevice/airpurifier.js.map +1 -1
  88. package/dist/irdevice/camera.d.ts.map +1 -1
  89. package/dist/irdevice/camera.js +12 -17
  90. package/dist/irdevice/camera.js.map +1 -1
  91. package/dist/irdevice/fan.d.ts.map +1 -1
  92. package/dist/irdevice/fan.js +22 -38
  93. package/dist/irdevice/fan.js.map +1 -1
  94. package/dist/irdevice/irdevice.d.ts +22 -10
  95. package/dist/irdevice/irdevice.d.ts.map +1 -1
  96. package/dist/irdevice/irdevice.js +94 -100
  97. package/dist/irdevice/irdevice.js.map +1 -1
  98. package/dist/irdevice/light.d.ts.map +1 -1
  99. package/dist/irdevice/light.js +20 -38
  100. package/dist/irdevice/light.js.map +1 -1
  101. package/dist/irdevice/other.d.ts.map +1 -1
  102. package/dist/irdevice/other.js +79 -77
  103. package/dist/irdevice/other.js.map +1 -1
  104. package/dist/irdevice/tv.d.ts.map +1 -1
  105. package/dist/irdevice/tv.js +38 -53
  106. package/dist/irdevice/tv.js.map +1 -1
  107. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  108. package/dist/irdevice/vacuumcleaner.js +11 -16
  109. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  110. package/dist/irdevice/waterheater.d.ts.map +1 -1
  111. package/dist/irdevice/waterheater.js +11 -16
  112. package/dist/irdevice/waterheater.js.map +1 -1
  113. package/dist/platform.d.ts +4 -6
  114. package/dist/platform.d.ts.map +1 -1
  115. package/dist/platform.js +564 -665
  116. package/dist/platform.js.map +1 -1
  117. package/dist/settings.d.ts +10 -8
  118. package/dist/settings.d.ts.map +1 -1
  119. package/dist/settings.js.map +1 -1
  120. package/dist/types/bledevicestatus.d.ts +2 -1
  121. package/dist/types/bledevicestatus.d.ts.map +1 -1
  122. package/dist/types/devicestatus.d.ts +1 -1
  123. package/dist/types/devicestatus.d.ts.map +1 -1
  124. package/package.json +11 -11
  125. package/src/device/blindtilt.ts +64 -48
  126. package/src/device/bot.ts +79 -26
  127. package/src/device/ceilinglight.ts +57 -31
  128. package/src/device/colorbulb.ts +57 -43
  129. package/src/device/contact.ts +49 -31
  130. package/src/device/curtain.ts +51 -28
  131. package/src/device/device.ts +61 -135
  132. package/src/device/fan.ts +67 -33
  133. package/src/device/hub.ts +62 -33
  134. package/src/device/humidifier.ts +60 -33
  135. package/src/device/iosensor.ts +51 -30
  136. package/src/device/lightstrip.ts +52 -29
  137. package/src/device/lock.ts +55 -26
  138. package/src/device/meter.ts +60 -29
  139. package/src/device/meterplus.ts +56 -29
  140. package/src/device/motion.ts +50 -26
  141. package/src/device/plug.ts +38 -18
  142. package/src/device/robotvacuumcleaner.ts +53 -24
  143. package/src/device/waterdetector.ts +62 -28
  144. package/src/homebridge-ui/public/index.html +2 -2
  145. package/src/irdevice/airconditioner.ts +76 -165
  146. package/src/irdevice/airpurifier.ts +29 -49
  147. package/src/irdevice/camera.ts +13 -16
  148. package/src/irdevice/fan.ts +25 -35
  149. package/src/irdevice/irdevice.ts +97 -96
  150. package/src/irdevice/light.ts +21 -35
  151. package/src/irdevice/other.ts +79 -77
  152. package/src/irdevice/tv.ts +40 -51
  153. package/src/irdevice/vacuumcleaner.ts +12 -15
  154. package/src/irdevice/waterheater.ts +12 -15
  155. package/src/platform.ts +566 -696
  156. package/src/settings.ts +11 -8
  157. package/src/types/bledevicestatus.ts +4 -2
  158. package/src/types/devicestatus.ts +1 -1
  159. package/.github/npm-version-script.cjs +0 -81
@@ -5,9 +5,9 @@
5
5
 
6
6
  import { hostname } from 'os';
7
7
  import { request } from 'undici';
8
- import asyncmqtt from 'async-mqtt';
9
8
  import { Devices } from '../settings.js';
10
9
  import { BlindTiltMappingMode, sleep } from '../utils.js';
10
+ import { SwitchBotModel, SwitchBotBLEModel, SwitchBotBLEModelName, SwitchBotBLEModelFriendlyName } from 'node-switchbot';
11
11
 
12
12
  import type { MqttClient } from 'mqtt';
13
13
  import type { device } from '../types/devicelist.js';
@@ -15,7 +15,6 @@ import type { ad } from '../types/bledevicestatus.js';
15
15
  import type { SwitchBotPlatform } from '../platform.js';
16
16
  import type { SwitchBotPlatformConfig, devicesConfig } from '../settings.js';
17
17
  import type { API, CharacteristicValue, HAP, Logging, PlatformAccessory, Service } from 'homebridge';
18
- import { SwitchBotModel, SwitchBotBLEModel, SwitchBotBLEModelName, SwitchBotBLEModelFriendlyName } from 'node-switchbot';
19
18
 
20
19
  export abstract class deviceBase {
21
20
  public readonly api: API;
@@ -39,6 +38,11 @@ export abstract class deviceBase {
39
38
  protected deviceModel!: SwitchBotModel;
40
39
  protected deviceBLEModel!: SwitchBotBLEModel;
41
40
 
41
+ // MQTT
42
+ protected deviceMqttURL!: string;
43
+ protected deviceMqttOptions!: any;
44
+ protected deviceMqttPubOptions!: any;
45
+
42
46
  // BLE
43
47
  protected scanDuration!: number;
44
48
 
@@ -68,90 +72,49 @@ export abstract class deviceBase {
68
72
  this.getDeviceConfigSettings(accessory, device);
69
73
  this.getDeviceContext(accessory, device);
70
74
  this.getDeviceScanDuration(accessory, device);
71
- this.setupMqtt(device);
75
+ this.getMqttSettings(device);
72
76
 
73
77
  // Set accessory information
74
78
  accessory
75
79
  .getService(this.hap.Service.AccessoryInformation)!
76
80
  .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
77
81
  .setCharacteristic(this.hap.Characteristic.AppMatchingIdentifier, 'id1087374760')
78
- .setCharacteristic(this.hap.Characteristic.Name, device.deviceName ?? accessory.displayName)
79
- .setCharacteristic(this.hap.Characteristic.ConfiguredName, device.deviceName ?? accessory.displayName)
82
+ .setCharacteristic(this.hap.Characteristic.Name, device.configDeviceName ?? device.deviceName ?? accessory.displayName)
83
+ .setCharacteristic(this.hap.Characteristic.ConfiguredName, device.configDeviceName ?? device.deviceName ?? accessory.displayName)
80
84
  .setCharacteristic(this.hap.Characteristic.Model, device.model ?? accessory.context.model)
81
85
  .setCharacteristic(this.hap.Characteristic.ProductData, device.deviceId ?? accessory.context.deviceId)
82
86
  .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId);
83
87
  }
84
88
 
85
89
  async getDeviceLogSettings(accessory: PlatformAccessory, device: device & devicesConfig): Promise<void> {
86
- if (this.platform.debugMode) {
87
- this.deviceLogging = accessory.context.logging = 'debugMode';
88
- this.debugWarnLog(`Using Debug Mode Logging: ${this.deviceLogging}`);
89
- } else if (device.logging) {
90
- this.deviceLogging = accessory.context.logging = device.logging;
91
- this.debugWarnLog(`Using Device Config Logging: ${this.deviceLogging}`);
92
- } else if (this.config.logging) {
93
- this.deviceLogging = accessory.context.logging = this.config.logging;
94
- this.debugWarnLog(`Using Platform Config Logging: ${this.deviceLogging}`);
95
- } else {
96
- this.deviceLogging = accessory.context.logging = 'standard';
97
- this.debugWarnLog(`Logging Not Set, Using: ${this.deviceLogging}`);
98
- }
90
+ this.deviceLogging = this.platform.debugMode ? 'debugMode' : device.logging ?? this.config.logging ?? 'standard';
91
+ const logging = this.platform.debugMode ? 'Debug Mode' : device.logging ? 'Device Config' : this.config.logging ? 'Platform Config' : 'Default';
92
+ accessory.context.deviceLogging = this.deviceLogging;
93
+ await this.debugLog(`Using ${logging} Logging: ${this.deviceLogging}`);
99
94
  }
100
95
 
101
96
  async getDeviceRateSettings(accessory: PlatformAccessory, device: device & devicesConfig): Promise<void> {
102
97
  // refreshRate
103
- if (device.refreshRate) {
104
- this.deviceRefreshRate = device.refreshRate;
105
- this.debugLog(`Using Device Config refreshRate: ${this.deviceRefreshRate}`);
106
- } else if (this.config.options?.refreshRate) {
107
- this.deviceRefreshRate = this.config.options.refreshRate;
108
- this.debugLog(`Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
109
- } else {
110
- this.deviceRefreshRate = 5;
111
- this.debugLog(`Using Default refreshRate: ${this.deviceRefreshRate}`);
112
- }
98
+ this.deviceRefreshRate = device.refreshRate ?? this.config.options?.refreshRate ?? 5;
113
99
  accessory.context.deviceRefreshRate = this.deviceRefreshRate;
114
- // updateRate
115
- if (device.updateRate) {
116
- this.deviceUpdateRate = device.updateRate;
117
- this.debugLog(`Using Device Config updateRate: ${this.deviceUpdateRate}`);
118
- } else if (this.config.options?.updateRate) {
119
- this.deviceUpdateRate = this.config.options.updateRate;
120
- this.debugLog(`Using Platform Config updateRate: ${this.deviceUpdateRate}`);
121
- } else {
122
- this.deviceUpdateRate = 5;
123
- this.debugLog(`Using Default updateRate: ${this.deviceUpdateRate}`);
124
- }
100
+ const refreshRate = device.refreshRate ? 'Device Config' : this.config.options?.refreshRate ? 'Platform Config' : 'Default';
101
+ this.deviceUpdateRate = device.updateRate ?? this.config.options?.updateRate ?? 5;
125
102
  accessory.context.deviceUpdateRate = this.deviceUpdateRate;
126
- // pushRate
127
- if (device.pushRate) {
128
- this.devicePushRate = device.pushRate;
129
- this.debugLog(`Using Device Config pushRate: ${this.deviceUpdateRate}`);
130
- } else if (this.config.options?.pushRate) {
131
- this.devicePushRate = this.config.options.pushRate;
132
- this.debugLog(`Using Platform Config pushRate: ${this.deviceUpdateRate}`);
133
- } else {
134
- this.devicePushRate = 1;
135
- this.debugLog(`Using Default pushRate: ${this.deviceUpdateRate}`);
136
- }
103
+ const updateRate = device.updateRate ? 'Device Config' : this.config.options?.updateRate ? 'Platform Config' : 'Default';
104
+ this.devicePushRate = device.pushRate ?? this.config.options?.pushRate ?? 1;
137
105
  accessory.context.devicePushRate = this.devicePushRate;
106
+ const pushRate = device.pushRate ? 'Device Config' : this.config.options?.pushRate ? 'Platform Config' : 'Default';
107
+ await this.debugLog(`Using ${refreshRate} refreshRate: ${this.deviceRefreshRate}, ${updateRate} updateRate: ${this.deviceUpdateRate},`
108
+ + ` ${pushRate} pushRate: ${this.devicePushRate}`);
138
109
  }
139
110
 
140
111
  async getDeviceRetry(device: device & devicesConfig): Promise<void> {
141
- if (device.maxRetries) {
142
- this.deviceMaxRetries = device.maxRetries;
143
- this.debugLog(`Using Device Max Retries: ${this.deviceMaxRetries}`);
144
- } else {
145
- this.deviceMaxRetries = 5; // Maximum number of retries
146
- this.debugLog(`Max Retries Not Set, Using: ${this.deviceMaxRetries}`);
147
- }
148
- if (device.delayBetweenRetries) {
149
- this.deviceDelayBetweenRetries = device.delayBetweenRetries * 1000;
150
- this.debugLog(`Using Device Delay Between Retries: ${this.deviceDelayBetweenRetries}`);
151
- } else {
152
- this.deviceDelayBetweenRetries = 3000; // Delay between retries in milliseconds
153
- this.debugLog(`Delay Between Retries Not Set, Using: ${this.deviceDelayBetweenRetries}`);
154
- }
112
+ this.deviceMaxRetries = device.maxRetries ?? 5;
113
+ const maxRetries = device.maxRetries ? 'Device' : 'Default';
114
+ this.deviceDelayBetweenRetries = device.delayBetweenRetries ? (device.delayBetweenRetries * 1000) : 3000;
115
+ const delayBetweenRetries = device.delayBetweenRetries ? 'Device' : 'Default';
116
+ await this.debugLog(`Using ${maxRetries} Max Retries: ${this.deviceMaxRetries},`
117
+ + ` ${delayBetweenRetries} Delay Between Retries: ${this.deviceDelayBetweenRetries}`);
155
118
  }
156
119
 
157
120
  async retryBLE({ max, fn }: { max: number; fn: { (): any; (): Promise<any> } }): Promise<null> {
@@ -167,14 +130,12 @@ export abstract class deviceBase {
167
130
  }
168
131
 
169
132
  async maxRetryBLE(): Promise<number> {
170
- if (this.device.maxRetry) {
171
- return this.device.maxRetry;
172
- } else {
173
- return 5;
174
- }
133
+ return this.device.maxRetry ? this.device.maxRetry : 5;
175
134
  }
176
135
 
177
136
  async getDeviceScanDuration(accessory: PlatformAccessory, device: device & devicesConfig): Promise<void> {
137
+ this.scanDuration = device.scanDuration ? (this.deviceUpdateRate > device.scanDuration) ? this.deviceUpdateRate : device.scanDuration
138
+ ? (this.deviceUpdateRate > 1) ? this.deviceUpdateRate : 1 : this.deviceUpdateRate : 1;
178
139
  if (device.scanDuration) {
179
140
  if (this.deviceUpdateRate > device.scanDuration) {
180
141
  this.scanDuration = this.deviceUpdateRate;
@@ -231,6 +192,12 @@ export abstract class deviceBase {
231
192
  if (device.mqttURL !== '') {
232
193
  deviceConfig['mqttURL'] = device.mqttURL;
233
194
  }
195
+ if (device.mqttOptions) {
196
+ deviceConfig['mqttOptions'] = device.mqttOptions;
197
+ }
198
+ if (device.mqttPubOptions) {
199
+ deviceConfig['mqttPubOptions'] = device.mqttPubOptions;
200
+ }
234
201
  if (device.maxRetries !== 0) {
235
202
  deviceConfig['maxRetries'] = device.maxRetries;
236
203
  }
@@ -319,21 +286,10 @@ export abstract class deviceBase {
319
286
  const numberOfLevels = spaceBetweenLevels + 1;
320
287
  this.debugLog(`LightLevel: ${lightLevel}, set_minLux: ${set_minLux}, set_maxLux: ${set_maxLux}, spaceBetweenLevels: ${spaceBetweenLevels},`
321
288
  + ` numberOfLevels: ${numberOfLevels}`);
322
- let CurrentAmbientLightLevel: number;
323
- switch (lightLevel) {
324
- case 1:
325
- CurrentAmbientLightLevel = set_minLux;
326
- this.debugLog(`LightLevel: ${lightLevel}, set_minLux: ${set_minLux}`);
327
- break;
328
- case numberOfLevels:
329
- CurrentAmbientLightLevel = set_maxLux;
330
- this.debugLog(`LightLevel: ${lightLevel}, set_maxLux: ${set_maxLux}`);
331
- break;
332
- default:
333
- CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * (Number(lightLevel) - 1);
334
- this.debugLog(`LightLevel: ${lightLevel}`);
335
- }
336
- this.debugLog(`CurrentAmbientLightLevel: ${CurrentAmbientLightLevel}`);
289
+ const CurrentAmbientLightLevel = lightLevel === 1 ? set_minLux : lightLevel = numberOfLevels
290
+ ? set_maxLux : ((set_maxLux - set_minLux) / spaceBetweenLevels) * (Number(lightLevel) - 1);
291
+ await this.debugLog(`CurrentAmbientLightLevel: ${CurrentAmbientLightLevel}, LightLevel: ${lightLevel}, set_minLux: ${set_minLux},`
292
+ + ` set_maxLux: ${set_maxLux}`);
337
293
  return CurrentAmbientLightLevel;
338
294
  }
339
295
 
@@ -342,41 +298,29 @@ export abstract class deviceBase {
342
298
  * 'homebridge-switchbot/${this.device.deviceType}/xx:xx:xx:xx:xx:xx'
343
299
  */
344
300
  async mqttPublish(message: string, topic?: string) {
345
- const mac = this.device.deviceId
346
- ?.toLowerCase()
347
- .match(/[\s\S]{1,2}/g)
348
- ?.join(':');
349
- const options = this.device.mqttPubOptions || {};
350
- let mqttTopic: string;
351
- let mqttMessageTopic: string;
352
- if (topic) {
353
- mqttTopic = `/${topic}`;
354
- mqttMessageTopic = `${topic}/`;
355
- } else {
356
- mqttTopic = '';
357
- mqttMessageTopic = '';
358
- }
301
+ const mac = this.device.deviceId?.toLowerCase().match(/[\s\S]{1,2}/g)?.join(':');
302
+ const options = this.deviceMqttPubOptions ?? {};
303
+ const mqttTopic = topic ? `/${topic}` : '';
304
+ const mqttMessageTopic = topic ? `${topic}/` : '';
359
305
  this.mqttClient?.publish(`homebridge-switchbot/${this.device.deviceType}/${mac}${mqttTopic}`, `${message}`, options);
360
306
  this.debugLog(`MQTT message: ${mqttMessageTopic}${message} options:${JSON.stringify(options)}`);
361
307
  }
362
308
 
363
309
  /*
364
- * Setup MQTT hadler if URL is specified.
310
+ * MQTT Settings
365
311
  */
366
- async setupMqtt(device: device & devicesConfig): Promise<void> {
367
- if (device.mqttURL) {
368
- try {
369
- const { connectAsync } = asyncmqtt;
370
- this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
371
- this.debugLog('MQTT connection has been established successfully.');
372
- this.mqttClient.on('error', (e: Error) => {
373
- this.errorLog(`Failed to publish MQTT messages. ${e}`);
374
- });
375
- } catch (e) {
376
- this.mqttClient = null;
377
- this.errorLog(`Failed to establish MQTT connection. ${e}`);
378
- }
379
- }
312
+ async getMqttSettings(device: device & devicesConfig): Promise<void> {
313
+ // mqttURL
314
+ this.deviceMqttURL = device.mqttURL ?? this.config.options?.mqttURL ?? '';
315
+ const mqttURL = device.mqttURL ? 'Device Config' : this.config.options?.mqttURL ? 'Platform Config' : 'Default';
316
+ // mqttOptions
317
+ this.deviceMqttOptions = device.mqttOptions ?? this.config.options?.mqttOptions ?? {};
318
+ const mqttOptions = device.mqttOptions ? 'Device Config' : this.config.options?.mqttOptions ? 'Platform Config' : 'Default';
319
+ // mqttPubOptions
320
+ this.deviceMqttPubOptions = device.mqttPubOptions ?? this.config.options?.mqttPubOptions ?? {};
321
+ const mqttPubOptions = device.mqttPubOptions ? 'Device Config' : this.config.options?.mqttPubOptions ? 'Platform Config' : 'Default';
322
+ await this.debugLog(`Using ${mqttURL} MQTT URL: ${this.deviceMqttURL}, ${mqttOptions} mqttOptions: ${JSON.stringify(this.deviceMqttOptions)},`
323
+ + ` ${mqttPubOptions} mqttPubOptions: ${JSON.stringify(this.deviceMqttPubOptions)}`);
380
324
  }
381
325
 
382
326
  /*
@@ -715,32 +659,14 @@ export abstract class deviceBase {
715
659
  device.bleModelName = SwitchBotBLEModelName.Unknown;
716
660
  device.bleModelFriednlyName = SwitchBotBLEModelFriendlyName.Unknown;
717
661
  }
718
- this.debugLog(`Model: ${device.model}, BLE Model: ${device.bleModel}, BLE Model Name: ${device.bleModelName}, `
662
+ await this.debugLog(`Model: ${device.model}, BLE Model: ${device.bleModel}, BLE Model Name: ${device.bleModelName}, `
719
663
  + `BLE Model Friendly Name: ${device.bleModelFriednlyName}`);
720
664
  accessory.context.model = device.model;
721
665
  accessory.context.bleModel = device.bleModel;
722
666
  accessory.context.bleModelName = device.bleModelName;
723
667
  accessory.context.bleModelFriednlyName = device.bleModelFriednlyName;
724
668
 
725
- // Firmware Version
726
- let deviceFirmwareVersion: string;
727
- if (device.firmware) {
728
- deviceFirmwareVersion = device.firmware;
729
- this.debugSuccessLog(`1 FirmwareRevision: ${device.firmware}`);
730
- } else if (device.version) {
731
- deviceFirmwareVersion = JSON.stringify(device.version);
732
- this.debugSuccessLog(`2 FirmwareRevision: ${device.version}`);
733
- } else if (accessory.context.deviceVersion) {
734
- deviceFirmwareVersion = accessory.context.deviceVersion;
735
- this.debugSuccessLog(`3 FirmwareRevision: ${accessory.context.deviceVersion}`);
736
- } else {
737
- deviceFirmwareVersion = this.platform.version ?? '0.0.0';
738
- if (this.platform.version) {
739
- this.debugSuccessLog(`4 FirmwareRevision: ${this.platform.version}`);
740
- } else {
741
- this.debugSuccessLog(`5 FirmwareRevision: ${deviceFirmwareVersion}`);
742
- }
743
- }
669
+ const deviceFirmwareVersion = device.firmware ?? device.version ?? accessory.context.version ?? this.platform.version ?? '0.0.0';
744
670
  const version = deviceFirmwareVersion.toString();
745
671
  this.debugLog(`Firmware Version: ${version.replace(/^V|-.*$/g, '')}`);
746
672
  let deviceVersion: string;
@@ -759,8 +685,8 @@ export abstract class deviceBase {
759
685
  .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
760
686
  .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
761
687
  .updateValue(deviceVersion);
762
- accessory.context.deviceVersion = deviceVersion;
763
- this.debugSuccessLog(`deviceVersion: ${accessory.context.deviceVersion}`);
688
+ accessory.context.version = deviceVersion;
689
+ this.debugSuccessLog(`version: ${accessory.context.version}`);
764
690
  }
765
691
 
766
692
  async statusCode(statusCode: number): Promise<void> {
package/src/device/fan.ts CHANGED
@@ -39,6 +39,15 @@ export class Fan extends deviceBase {
39
39
  Brightness: CharacteristicValue;
40
40
  };
41
41
 
42
+ // OpenAPI
43
+ deviceStatus!: batteryCirculatorFanStatus;
44
+
45
+ //Webhook
46
+ webhookContext!: batteryCirculatorFanWebhookContext;
47
+
48
+ // BLE
49
+ serviceData!: batteryCirculatorFanServiceData;
50
+
42
51
  // Updates
43
52
  fanUpdateInProgress!: boolean;
44
53
  doFanUpdate!: Subject<void>;
@@ -49,6 +58,9 @@ export class Fan extends deviceBase {
49
58
  device: device & devicesConfig,
50
59
  ) {
51
60
  super(platform, accessory, device);
61
+ // Set category
62
+ accessory.category = this.hap.Categories.FAN;
63
+
52
64
  // this is subject we use to track when we need to POST changes to the SwitchBot API
53
65
  this.doFanUpdate = new Subject();
54
66
  this.fanUpdateInProgress = false;
@@ -149,10 +161,12 @@ export class Fan extends deviceBase {
149
161
  .onSet(this.BrightnessSet.bind(this));
150
162
 
151
163
  // Retrieve initial values and updateHomekit
164
+ this.debugLog('Retrieve initial values and update Homekit');
152
165
  this.refreshStatus();
153
166
 
154
- // Update Homekit
155
- this.updateHomeKitCharacteristics();
167
+ //regisiter webhook event handler
168
+ this.debugLog('Registering Webhook Event Handler');
169
+ this.registerWebhook();
156
170
 
157
171
  //regisiter webhook event handler
158
172
  this.registerWebhook();
@@ -184,35 +198,45 @@ export class Fan extends deviceBase {
184
198
  });
185
199
  }
186
200
 
187
- async BLEparseStatus(serviceData: batteryCirculatorFanServiceData): Promise<void> {
201
+ async BLEparseStatus(): Promise<void> {
188
202
  await this.debugLog('BLEparseStatus');
203
+ await this.debugLog(`(powerState, fanSpeed) = BLE:(${this.serviceData.state}, ${this.serviceData.fanSpeed}), current:(${this.Fan.Active},`
204
+ + ` ${this.Fan.RotationSpeed})`);
205
+
189
206
  // Active
190
- this.Fan.Active = serviceData.state === 'on' ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE;
207
+ this.Fan.Active = this.serviceData.state === 'on' ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE;
191
208
  await this.debugLog(`Active: ${this.Fan.Active}`);
209
+
210
+ // RotationSpeed
211
+ this.Fan.RotationSpeed = this.serviceData.fanSpeed;
192
212
  }
193
213
 
194
- async openAPIparseStatus(deviceStatus: batteryCirculatorFanStatus) {
214
+ async openAPIparseStatus() {
195
215
  await this.debugLog('openAPIparseStatus');
216
+ await this.debugLog(`(version, battery, powerState, oscillation, chargingStatus, fanSpeed) = OpenAPI:(${this.deviceStatus.version},`
217
+ + ` ${this.deviceStatus.battery}, ${this.deviceStatus.power}, ${this.deviceStatus.oscillation}, ${this.deviceStatus.chargingStatus},`
218
+ + ` ${this.deviceStatus.fanSpeed}), current:(${this.accessory.context.version}, ${this.Battery.BatteryLevel}, ${this.Fan.Active},`
219
+ + ` ${this.Fan.SwingMode}, ${this.Battery.ChargingState}, ${this.Fan.RotationSpeed})`);
196
220
 
197
221
  // Active
198
- this.Fan.Active = deviceStatus.power === 'on' ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE;
222
+ this.Fan.Active = this.deviceStatus.power === 'on' ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE;
199
223
  this.debugLog(`Active: ${this.Fan.Active}`);
200
224
 
201
225
  // SwingMode
202
- this.Fan.SwingMode = deviceStatus.oscillation === 'on' ?
226
+ this.Fan.SwingMode = this.deviceStatus.oscillation === 'on' ?
203
227
  this.hap.Characteristic.SwingMode.SWING_ENABLED : this.hap.Characteristic.SwingMode.SWING_DISABLED;
204
228
  await this.debugLog(`SwingMode: ${this.Fan.SwingMode}`);
205
229
 
206
230
  // RotationSpeed
207
- this.Fan.RotationSpeed = deviceStatus.fanSpeed;
231
+ this.Fan.RotationSpeed = this.deviceStatus.fanSpeed;
208
232
  await this.debugLog(`RotationSpeed: ${this.Fan.RotationSpeed}`);
209
233
 
210
234
  // ChargingState
211
- this.Battery.ChargingState = deviceStatus.chargingStatus === 'charging' ?
235
+ this.Battery.ChargingState = this.deviceStatus.chargingStatus === 'charging' ?
212
236
  this.hap.Characteristic.ChargingState.CHARGING : this.hap.Characteristic.ChargingState.NOT_CHARGING;
213
237
 
214
238
  // BatteryLevel
215
- this.Battery.BatteryLevel = Number(deviceStatus.battery);
239
+ this.Battery.BatteryLevel = this.deviceStatus.battery;
216
240
  await this.debugLog(`BatteryLevel: ${this.Battery.BatteryLevel}`);
217
241
 
218
242
  // StatusLowBattery
@@ -221,9 +245,9 @@ export class Fan extends deviceBase {
221
245
  await this.debugLog(`StatusLowBattery: ${this.Battery.StatusLowBattery}`);
222
246
 
223
247
  // Firmware Version
224
- const version = deviceStatus.version.toString();
225
- await this.debugLog(`Firmware Version: ${version.replace(/^V|-.*$/g, '')}`);
226
- if (deviceStatus.version) {
248
+ if (this.deviceStatus.version) {
249
+ const version = this.deviceStatus.version.toString();
250
+ await this.debugLog(`Firmware Version: ${version.replace(/^V|-.*$/g, '')}`);
227
251
  const deviceVersion = version.replace(/^V|-.*$/g, '') ?? '0.0.0';
228
252
  this.accessory
229
253
  .getService(this.hap.Service.AccessoryInformation)!
@@ -231,49 +255,56 @@ export class Fan extends deviceBase {
231
255
  .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
232
256
  .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
233
257
  .updateValue(deviceVersion);
234
- this.accessory.context.deviceVersion = deviceVersion;
235
- await this.debugSuccessLog(`deviceVersion: ${this.accessory.context.deviceVersion}`);
258
+ this.accessory.context.version = deviceVersion;
259
+ await this.debugSuccessLog(`version: ${this.accessory.context.version}`);
236
260
  }
237
261
  }
238
262
 
239
- async parseStatusWebhook(context: batteryCirculatorFanWebhookContext): Promise<void> {
263
+ async parseStatusWebhook(): Promise<void> {
240
264
  await this.debugLog('parseStatusWebhook');
241
- await this.debugLog(`(version, battery, powerState, oscillation, chargingStatus, fanSpeed) = Webhook:(${context.version}, ${context.battery},`
242
- + ` ${context.powerState}, ${context.oscillation}, ${context.chargingStatus}, ${context.fanSpeed}),`
243
- + ` current:(${this.accessory.context.deviceVersion}, ${this.Battery.BatteryLevel}, ${this.Fan.Active}, ${this.Fan.SwingMode},`
244
- + ` ${this.Battery.ChargingState}, ${this.Fan.RotationSpeed})`);
265
+ await this.debugLog(`(version, battery, powerState, oscillation, chargingStatus, fanSpeed) = Webhook:(${this.webhookContext.version},`
266
+ + ` ${this.webhookContext.battery}, ${this.webhookContext.powerState}, ${this.webhookContext.oscillation},`
267
+ + ` ${this.webhookContext.chargingStatus}, ${this.webhookContext.fanSpeed}), current:(${this.accessory.context.version},`
268
+ + ` ${this.Battery.BatteryLevel}, ${this.Fan.Active}, ${this.Fan.SwingMode}, ${this.Battery.ChargingState}, ${this.Fan.RotationSpeed})`);
269
+
245
270
  // Active
246
- this.Fan.Active = context.powerState === 'ON' ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE;
271
+ this.Fan.Active = this.webhookContext.powerState === 'ON' ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE;
247
272
  await this.debugLog(`Active: ${this.Fan.Active}`);
273
+
248
274
  // SwingMode
249
- this.Fan.SwingMode = context.oscillation === 'on' ?
275
+ this.Fan.SwingMode = this.webhookContext.oscillation === 'on' ?
250
276
  this.hap.Characteristic.SwingMode.SWING_ENABLED : this.hap.Characteristic.SwingMode.SWING_DISABLED;
251
277
  await this.debugLog(`SwingMode: ${this.Fan.SwingMode}`);
278
+
252
279
  // RotationSpeed
253
- this.Fan.RotationSpeed = context.fanSpeed;
280
+ this.Fan.RotationSpeed = this.webhookContext.fanSpeed;
254
281
  await this.debugLog(`RotationSpeed: ${this.Fan.RotationSpeed}`);
282
+
255
283
  // BatteryLevel
256
- this.Battery.BatteryLevel = context.battery;
284
+ this.Battery.BatteryLevel = this.webhookContext.battery;
257
285
  await this.debugLog(`BatteryLevel: ${this.Battery.BatteryLevel}`);
286
+
258
287
  // StatusLowBattery
259
288
  this.Battery.StatusLowBattery = this.Battery.BatteryLevel < 10
260
289
  ? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
261
290
  await this.debugLog(`StatusLowBattery: ${this.Battery.StatusLowBattery}`);
291
+
262
292
  // ChargingState
263
- this.Battery.ChargingState = context.chargingStatus === 'charging' ?
293
+ this.Battery.ChargingState = this.webhookContext.chargingStatus === 'charging' ?
264
294
  this.hap.Characteristic.ChargingState.CHARGING : this.hap.Characteristic.ChargingState.NOT_CHARGING;
265
295
  await this.debugLog(`ChargingState: ${this.Battery.ChargingState}`);
296
+
266
297
  // FirmwareVersion
267
- if (context.version) {
268
- const deviceVersion = context.version.replace(/^V|-.*$/g, '') ?? '0.0.0';
298
+ if (this.webhookContext.version) {
299
+ const deviceVersion = this.webhookContext.version.replace(/^V|-.*$/g, '') ?? '0.0.0';
269
300
  this.accessory
270
301
  .getService(this.hap.Service.AccessoryInformation)!
271
302
  .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
272
303
  .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
273
304
  .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
274
305
  .updateValue(deviceVersion);
275
- this.accessory.context.deviceVersion = deviceVersion;
276
- await this.debugSuccessLog(`deviceVersion: ${this.accessory.context.deviceVersion}`);
306
+ this.accessory.context.version = deviceVersion;
307
+ await this.debugSuccessLog(`version: ${this.accessory.context.version}`);
277
308
  }
278
309
  }
279
310
 
@@ -303,10 +334,11 @@ export class Fan extends deviceBase {
303
334
  // Start to monitor advertisement packets
304
335
  (async () => {
305
336
  // Start to monitor advertisement packets
306
- const serviceData = await this.monitorAdvertisementPackets(switchbot) as unknown as batteryCirculatorFanServiceData;
337
+ const serviceData = await this.monitorAdvertisementPackets(switchbot) as batteryCirculatorFanServiceData;
307
338
  // Update HomeKit
308
339
  if (serviceData.model === SwitchBotBLEModel.Unknown && SwitchBotBLEModelName.Unknown) {
309
- await this.BLEparseStatus(serviceData);
340
+ this.serviceData = serviceData;
341
+ await this.BLEparseStatus();
310
342
  await this.updateHomeKitCharacteristics();
311
343
  } else {
312
344
  await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
@@ -324,7 +356,8 @@ export class Fan extends deviceBase {
324
356
  await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`);;
325
357
  if (await this.successfulStatusCodes(statusCode, deviceStatus)) {
326
358
  await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`);
327
- await this.openAPIparseStatus(deviceStatus.body);
359
+ this.deviceStatus = deviceStatus.body;
360
+ await this.openAPIparseStatus();
328
361
  await this.updateHomeKitCharacteristics();
329
362
  } else {
330
363
  await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`);
@@ -342,7 +375,8 @@ export class Fan extends deviceBase {
342
375
  this.platform.webhookEventHandler[this.device.deviceId] = async (context: batteryCirculatorFanWebhookContext) => {
343
376
  try {
344
377
  await this.debugLog(`received Webhook: ${JSON.stringify(context)}`);
345
- await this.parseStatusWebhook(context);
378
+ this.webhookContext = context;
379
+ await this.parseStatusWebhook();
346
380
  await this.updateHomeKitCharacteristics();
347
381
  } catch (e: any) {
348
382
  await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);