@switchbot/homebridge-switchbot 1.10.2-beta.3 → 1.10.2-beta.4

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 (134) hide show
  1. package/dist/{devices/bots.d.ts → device/bot.d.ts} +1 -1
  2. package/dist/device/bot.d.ts.map +1 -0
  3. package/dist/{devices/bots.js → device/bot.js} +1 -1
  4. package/dist/device/bot.js.map +1 -0
  5. package/dist/{devices → device}/colorbulb.d.ts +6 -0
  6. package/dist/device/colorbulb.d.ts.map +1 -0
  7. package/dist/{devices → device}/colorbulb.js +6 -0
  8. package/dist/device/colorbulb.js.map +1 -0
  9. package/dist/{devices → device}/contact.d.ts +0 -0
  10. package/dist/device/contact.d.ts.map +1 -0
  11. package/dist/{devices → device}/contact.js +0 -0
  12. package/dist/device/contact.js.map +1 -0
  13. package/dist/{devices/curtains.d.ts → device/curtain.d.ts} +1 -1
  14. package/dist/device/curtain.d.ts.map +1 -0
  15. package/dist/{devices/curtains.js → device/curtain.js} +2 -2
  16. package/dist/device/curtain.js.map +1 -0
  17. package/dist/{devices/humidifiers.d.ts → device/humidifier.d.ts} +1 -1
  18. package/dist/device/humidifier.d.ts.map +1 -0
  19. package/dist/{devices/humidifiers.js → device/humidifier.js} +1 -1
  20. package/dist/device/humidifier.js.map +1 -0
  21. package/dist/{devices/plugs.d.ts → device/lock.d.ts} +12 -11
  22. package/dist/device/lock.d.ts.map +1 -0
  23. package/dist/device/lock.js +299 -0
  24. package/dist/device/lock.js.map +1 -0
  25. package/dist/{devices/meters.d.ts → device/meter.d.ts} +2 -1
  26. package/dist/device/meter.d.ts.map +1 -0
  27. package/dist/{devices/meters.js → device/meter.js} +15 -2
  28. package/dist/device/meter.js.map +1 -0
  29. package/dist/{devices → device}/motion.d.ts +0 -0
  30. package/dist/device/motion.d.ts.map +1 -0
  31. package/dist/{devices → device}/motion.js +0 -0
  32. package/dist/device/motion.js.map +1 -0
  33. package/dist/device/plug.d.ts +54 -0
  34. package/dist/device/plug.d.ts.map +1 -0
  35. package/dist/{devices/plugs.js → device/plug.js} +26 -10
  36. package/dist/device/plug.js.map +1 -0
  37. package/dist/{irdevices/airconditioners.d.ts → irdevice/airconditioner.d.ts} +1 -1
  38. package/dist/irdevice/airconditioner.d.ts.map +1 -0
  39. package/dist/{irdevices/airconditioners.js → irdevice/airconditioner.js} +1 -1
  40. package/dist/irdevice/airconditioner.js.map +1 -0
  41. package/dist/{irdevices/airpurifiers.d.ts → irdevice/airpurifier.d.ts} +1 -1
  42. package/dist/irdevice/airpurifier.d.ts.map +1 -0
  43. package/dist/{irdevices/airpurifiers.js → irdevice/airpurifier.js} +1 -1
  44. package/dist/irdevice/airpurifier.js.map +1 -0
  45. package/dist/{irdevices/cameras.d.ts → irdevice/camera.d.ts} +1 -1
  46. package/dist/irdevice/camera.d.ts.map +1 -0
  47. package/dist/{irdevices/cameras.js → irdevice/camera.js} +1 -1
  48. package/dist/irdevice/camera.js.map +1 -0
  49. package/dist/{irdevices/fans.d.ts → irdevice/fan.d.ts} +1 -1
  50. package/dist/irdevice/fan.d.ts.map +1 -0
  51. package/dist/{irdevices/fans.js → irdevice/fan.js} +1 -1
  52. package/dist/irdevice/fan.js.map +1 -0
  53. package/dist/{irdevices/lights.d.ts → irdevice/light.d.ts} +1 -1
  54. package/dist/irdevice/light.d.ts.map +1 -0
  55. package/dist/{irdevices/lights.js → irdevice/light.js} +1 -1
  56. package/dist/irdevice/light.js.map +1 -0
  57. package/dist/{irdevices/others.d.ts → irdevice/other.d.ts} +1 -1
  58. package/dist/irdevice/other.d.ts.map +1 -0
  59. package/dist/{irdevices/others.js → irdevice/other.js} +1 -1
  60. package/dist/irdevice/other.js.map +1 -0
  61. package/dist/{irdevices/tvs.d.ts → irdevice/tv.d.ts} +1 -1
  62. package/dist/irdevice/tv.d.ts.map +1 -0
  63. package/dist/{irdevices/tvs.js → irdevice/tv.js} +1 -1
  64. package/dist/irdevice/tv.js.map +1 -0
  65. package/dist/{irdevices/vacuumcleaners.d.ts → irdevice/vacuumcleaner.d.ts} +1 -1
  66. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -0
  67. package/dist/{irdevices/vacuumcleaners.js → irdevice/vacuumcleaner.js} +1 -1
  68. package/dist/irdevice/vacuumcleaner.js.map +1 -0
  69. package/dist/{irdevices/waterheaters.d.ts → irdevice/waterheater.d.ts} +1 -1
  70. package/dist/irdevice/waterheater.d.ts.map +1 -0
  71. package/dist/{irdevices/waterheaters.js → irdevice/waterheater.js} +1 -1
  72. package/dist/irdevice/waterheater.js.map +1 -0
  73. package/dist/platform.d.ts +1 -0
  74. package/dist/platform.d.ts.map +1 -1
  75. package/dist/platform.js +106 -45
  76. package/dist/platform.js.map +1 -1
  77. package/dist/settings.d.ts +7 -0
  78. package/dist/settings.d.ts.map +1 -1
  79. package/dist/settings.js.map +1 -1
  80. package/package.json +1 -1
  81. package/src/{devices/bots.ts → device/bot.ts} +0 -0
  82. package/src/{devices → device}/colorbulb.ts +6 -0
  83. package/src/{devices → device}/contact.ts +0 -0
  84. package/src/{devices/curtains.ts → device/curtain.ts} +1 -1
  85. package/src/{devices/humidifiers.ts → device/humidifier.ts} +0 -0
  86. package/src/device/lock.ts +346 -0
  87. package/src/{devices/meters.ts → device/meter.ts} +13 -1
  88. package/src/{devices → device}/motion.ts +0 -0
  89. package/src/{devices/plugs.ts → device/plug.ts} +28 -13
  90. package/src/{irdevices/airconditioners.ts → irdevice/airconditioner.ts} +0 -0
  91. package/src/{irdevices/airpurifiers.ts → irdevice/airpurifier.ts} +0 -0
  92. package/src/{irdevices/cameras.ts → irdevice/camera.ts} +0 -0
  93. package/src/{irdevices/fans.ts → irdevice/fan.ts} +0 -0
  94. package/src/{irdevices/lights.ts → irdevice/light.ts} +0 -0
  95. package/src/{irdevices/others.ts → irdevice/other.ts} +0 -0
  96. package/src/{irdevices/tvs.ts → irdevice/tv.ts} +0 -0
  97. package/src/{irdevices/vacuumcleaners.ts → irdevice/vacuumcleaner.ts} +0 -0
  98. package/src/{irdevices/waterheaters.ts → irdevice/waterheater.ts} +0 -0
  99. package/src/platform.ts +82 -17
  100. package/src/settings.ts +24 -11
  101. package/dist/devices/bots.d.ts.map +0 -1
  102. package/dist/devices/bots.js.map +0 -1
  103. package/dist/devices/colorbulb.d.ts.map +0 -1
  104. package/dist/devices/colorbulb.js.map +0 -1
  105. package/dist/devices/contact.d.ts.map +0 -1
  106. package/dist/devices/contact.js.map +0 -1
  107. package/dist/devices/curtains.d.ts.map +0 -1
  108. package/dist/devices/curtains.js.map +0 -1
  109. package/dist/devices/humidifiers.d.ts.map +0 -1
  110. package/dist/devices/humidifiers.js.map +0 -1
  111. package/dist/devices/meters.d.ts.map +0 -1
  112. package/dist/devices/meters.js.map +0 -1
  113. package/dist/devices/motion.d.ts.map +0 -1
  114. package/dist/devices/motion.js.map +0 -1
  115. package/dist/devices/plugs.d.ts.map +0 -1
  116. package/dist/devices/plugs.js.map +0 -1
  117. package/dist/irdevices/airconditioners.d.ts.map +0 -1
  118. package/dist/irdevices/airconditioners.js.map +0 -1
  119. package/dist/irdevices/airpurifiers.d.ts.map +0 -1
  120. package/dist/irdevices/airpurifiers.js.map +0 -1
  121. package/dist/irdevices/cameras.d.ts.map +0 -1
  122. package/dist/irdevices/cameras.js.map +0 -1
  123. package/dist/irdevices/fans.d.ts.map +0 -1
  124. package/dist/irdevices/fans.js.map +0 -1
  125. package/dist/irdevices/lights.d.ts.map +0 -1
  126. package/dist/irdevices/lights.js.map +0 -1
  127. package/dist/irdevices/others.d.ts.map +0 -1
  128. package/dist/irdevices/others.js.map +0 -1
  129. package/dist/irdevices/tvs.d.ts.map +0 -1
  130. package/dist/irdevices/tvs.js.map +0 -1
  131. package/dist/irdevices/vacuumcleaners.d.ts.map +0 -1
  132. package/dist/irdevices/vacuumcleaners.js.map +0 -1
  133. package/dist/irdevices/waterheaters.d.ts.map +0 -1
  134. package/dist/irdevices/waterheaters.js.map +0 -1
@@ -0,0 +1,346 @@
1
+ import { AxiosResponse } from 'axios';
2
+ import { interval, Subject } from 'rxjs';
3
+ import { SwitchBotPlatform } from '../platform';
4
+ import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
5
+ import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
6
+ import { DeviceURL, device, devicesConfig, deviceStatusResponse, payload, deviceStatus } from '../settings';
7
+
8
+ export class Lock {
9
+ // Services
10
+ private service: Service;
11
+
12
+ // Characteristic Values
13
+ LockCurrentState!: CharacteristicValue;
14
+ LockTargetState!: CharacteristicValue;
15
+ LockTargetStateCached!: CharacteristicValue;
16
+
17
+ // OpenAPI Others
18
+ doorState!: deviceStatus['doorState'];
19
+ lockState!: deviceStatus['lockState'];
20
+ deviceStatus!: deviceStatusResponse;
21
+
22
+ // Config
23
+ scanDuration!: number;
24
+ deviceLogging!: string;
25
+ deviceRefreshRate!: number;
26
+
27
+ // Updates
28
+ lockUpdateInProgress!: boolean;
29
+ doLockUpdate!: Subject<void>;
30
+
31
+ constructor(private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, public device: device & devicesConfig) {
32
+ // default placeholders
33
+ this.logs(device);
34
+ this.scan(device);
35
+ this.refreshRate(device);
36
+ this.config(device);
37
+ if (this.LockTargetState === undefined) {
38
+ this.LockTargetState = false;
39
+ } else {
40
+ this.LockTargetState = this.accessory.context.On;
41
+ }
42
+
43
+ // Lock Config
44
+ this.debugLog(`Lock: ${this.accessory.displayName} Config: (offline: ${device.offline})`);
45
+
46
+ // this is subject we use to track when we need to POST changes to the SwitchBot API
47
+ this.doLockUpdate = new Subject();
48
+ this.lockUpdateInProgress = false;
49
+
50
+ // Retrieve initial values and updateHomekit
51
+ this.refreshStatus();
52
+
53
+ // set accessory information
54
+ accessory
55
+ .getService(this.platform.Service.AccessoryInformation)!
56
+ .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
57
+ .setCharacteristic(this.platform.Characteristic.Model, 'SWITCHBOT-LOCK-W1601700')
58
+ .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!);
59
+
60
+ // get the WindowCovering service if it exists, otherwise create a new WindowCovering service
61
+ // you can create multiple services for each accessory
62
+ (this.service = accessory.getService(this.platform.Service.Outlet) || accessory.addService(this.platform.Service.Outlet)),
63
+ `${device.deviceName} ${device.deviceType}`;
64
+
65
+ // To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
66
+ // when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
67
+ // accessory.getService('NAME') ?? accessory.addService(this.platform.Service.Lightbulb, 'NAME', 'USER_DEFINED_SUBTYPE');
68
+
69
+ // set the service name, this is what is displayed as the default name on the Home app
70
+ // in this example we are using the name we stored in the `accessory.context` in the `discoverDevices` method.
71
+ this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName);
72
+
73
+ // each service must implement at-minimum the "required characteristics" for the given service type
74
+ // see https://developers.homebridge.io/#/service/WindowCovering
75
+
76
+ // create handlers for required characteristics
77
+ this.service.getCharacteristic(this.platform.Characteristic.LockCurrentState).onSet(this.LockTargetStateSet.bind(this));
78
+
79
+ // Update Homekit
80
+ this.updateHomeKitCharacteristics();
81
+
82
+ // Start an update interval
83
+ interval(this.deviceRefreshRate * 1000)
84
+ .pipe(skipWhile(() => this.lockUpdateInProgress))
85
+ .subscribe(() => {
86
+ this.refreshStatus();
87
+ });
88
+
89
+ // Watch for Lock change events
90
+ // We put in a debounce of 100ms so we don't make duplicate calls
91
+ this.doLockUpdate
92
+ .pipe(
93
+ tap(() => {
94
+ this.lockUpdateInProgress = true;
95
+ }),
96
+ debounceTime(this.platform.config.options!.pushRate! * 1000),
97
+ )
98
+ .subscribe(async () => {
99
+ try {
100
+ await this.pushChanges();
101
+ } catch (e: any) {
102
+ this.errorLog(`Lock: ${this.accessory.displayName} failed pushChanges`);
103
+ if (this.deviceLogging === 'debug') {
104
+ this.errorLog(`Lock: ${this.accessory.displayName} failed pushChanges,` + ` Error Message: ${JSON.stringify(e.message)}`);
105
+ }
106
+ if (this.platform.debugMode) {
107
+ this.errorLog(`Lock: ${this.accessory.displayName} failed pushChanges,` + ` Error: ${JSON.stringify(e)}`);
108
+ }
109
+ this.apiError(e);
110
+ }
111
+ this.lockUpdateInProgress = false;
112
+ });
113
+ }
114
+
115
+ parseStatus() {
116
+ switch (this.lockState) {
117
+ case 'on':
118
+ this.LockTargetState = this.platform.Characteristic.LockTargetState.UNSECURED;
119
+ break;
120
+ default:
121
+ this.LockTargetState = this.platform.Characteristic.LockTargetState.SECURED;
122
+ }
123
+ this.debugLog(`Lock ${this.accessory.displayName} On: ${this.LockTargetState}`);
124
+ }
125
+
126
+ private async refreshStatus() {
127
+ try {
128
+ this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
129
+ this.debugLog(`Lock: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
130
+ this.lockState = this.deviceStatus.body.lockState;
131
+ this.warnLog(this.lockState);
132
+ this.doorState = this.deviceStatus.body.doorState;
133
+ this.warnLog(this.doorState);
134
+ this.parseStatus();
135
+ this.updateHomeKitCharacteristics();
136
+ } catch (e: any) {
137
+ this.errorLog(`Lock: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`);
138
+ if (this.deviceLogging === 'debug') {
139
+ this.errorLog(
140
+ `Lock: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + ` Error Message: ${JSON.stringify(e.message)}`,
141
+ );
142
+ }
143
+ if (this.platform.debugMode) {
144
+ this.errorLog(`Lock: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + ` Error: ${JSON.stringify(e)}`);
145
+ }
146
+ this.apiError(e);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Pushes the requested changes to the SwitchBot API
152
+ * deviceType commandType Command command parameter Description
153
+ * Lock - "command" "????" "????" = set to ???? state
154
+ * Lock - "command" "????" "????" = set to ???? state - LockCurrentState
155
+ */
156
+ async pushChanges() {
157
+ if (this.LockTargetState !== this.LockTargetStateCached) {
158
+ const payload = {
159
+ commandType: 'command',
160
+ parameter: 'default',
161
+ } as payload;
162
+
163
+ if (this.LockTargetState) {
164
+ payload.command = 'turnOn';
165
+ } else {
166
+ payload.command = 'turnOff';
167
+ }
168
+
169
+ this.infoLog(
170
+ `Lock: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` +
171
+ ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`,
172
+ );
173
+
174
+ // Make the API request
175
+ const push: any = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload);
176
+ this.debugLog(`Lock: ${this.accessory.displayName} pushchanges: ${JSON.stringify(push.data)}`);
177
+ this.statusCode(push);
178
+ this.LockTargetStateCached = this.LockTargetState;
179
+ this.accessory.context.On = this.LockTargetStateCached;
180
+ }
181
+ interval(5000)
182
+ .pipe(skipWhile(() => this.lockUpdateInProgress))
183
+ .pipe(take(1))
184
+ .subscribe(() => {
185
+ this.refreshStatus();
186
+ });
187
+ }
188
+
189
+ updateHomeKitCharacteristics() {
190
+ if (this.LockTargetState === undefined) {
191
+ this.debugLog(`Lock: ${this.accessory.displayName} LockTargetState: ${this.LockTargetState}`);
192
+ } else {
193
+ this.service.updateCharacteristic(this.platform.Characteristic.LockTargetState, this.LockTargetState);
194
+ this.debugLog(`Lock: ${this.accessory.displayName} updateCharacteristic LockTargetState: ${this.LockTargetState}`);
195
+ }
196
+ if (this.LockCurrentState === undefined) {
197
+ this.debugLog(`Lock: ${this.accessory.displayName} LockCurrentState: ${this.LockCurrentState}`);
198
+ } else {
199
+ this.service.updateCharacteristic(this.platform.Characteristic.LockCurrentState, this.LockCurrentState);
200
+ this.debugLog(`Lock: ${this.accessory.displayName} updateCharacteristic LockTargetState: ${this.LockCurrentState}`);
201
+ }
202
+ }
203
+
204
+ public apiError(e: any) {
205
+ this.service.updateCharacteristic(this.platform.Characteristic.On, e);
206
+ //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
207
+ }
208
+
209
+ private statusCode(push: AxiosResponse<{ statusCode: number }>) {
210
+ switch (push.data.statusCode) {
211
+ case 151:
212
+ this.errorLog(`Lock: ${this.accessory.displayName} Command not supported by this device type.`);
213
+ break;
214
+ case 152:
215
+ this.errorLog(`Lock: ${this.accessory.displayName} Device not found.`);
216
+ break;
217
+ case 160:
218
+ this.errorLog(`Lock: ${this.accessory.displayName} Command is not supported.`);
219
+ break;
220
+ case 161:
221
+ this.errorLog(`Lock: ${this.accessory.displayName} Device is offline.`);
222
+ break;
223
+ case 171:
224
+ this.errorLog(`Lock: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
225
+ break;
226
+ case 190:
227
+ this.errorLog(
228
+ `Lock: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
229
+ ` Or command: ${JSON.stringify(push.data)} format is invalid`,
230
+ );
231
+ break;
232
+ case 100:
233
+ this.debugLog(`Lock: ${this.accessory.displayName} Command successfully sent.`);
234
+ break;
235
+ default:
236
+ this.debugLog(`Lock: ${this.accessory.displayName} Unknown statusCode.`);
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Handle requests to set the value of the "On" characteristic
242
+ */
243
+ LockTargetStateSet(value: CharacteristicValue) {
244
+ this.debugLog(`Lock: ${this.accessory.displayName} Set LockTargetState: ${value}`);
245
+
246
+ this.LockTargetState = value;
247
+ this.doLockUpdate.next();
248
+ }
249
+
250
+ config(device: device & devicesConfig) {
251
+ let config = {};
252
+ if (device.lock) {
253
+ config = device.lock;
254
+ }
255
+ if (device.ble !== undefined) {
256
+ config['ble'] = device.ble;
257
+ }
258
+ if (device.logging !== undefined) {
259
+ config['logging'] = device.logging;
260
+ }
261
+ if (device.refreshRate !== undefined) {
262
+ config['refreshRate'] = device.refreshRate;
263
+ }
264
+ if (device.scanDuration !== undefined) {
265
+ config['scanDuration'] = device.scanDuration;
266
+ }
267
+ if (Object.entries(config).length !== 0) {
268
+ this.warnLog(`Lock: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
269
+ }
270
+ }
271
+
272
+ refreshRate(device: device & devicesConfig) {
273
+ if (device.refreshRate) {
274
+ this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
275
+ this.debugLog(`Lock: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
276
+ } else if (this.platform.config.options!.refreshRate) {
277
+ this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
278
+ this.debugLog(`Lock: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
279
+ }
280
+ }
281
+
282
+ scan(device: device & devicesConfig) {
283
+ if (device.scanDuration) {
284
+ this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
285
+ if (device.ble) {
286
+ this.debugLog(`Lock: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
287
+ }
288
+ } else {
289
+ this.scanDuration = this.accessory.context.scanDuration = 1;
290
+ if (this.device.ble) {
291
+ this.debugLog(`Lock: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
292
+ }
293
+ }
294
+ }
295
+
296
+ logs(device: device & devicesConfig) {
297
+ if (this.platform.debugMode) {
298
+ this.deviceLogging = this.accessory.context.logging = 'debugMode';
299
+ this.debugLog(`Lock: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
300
+ } else if (device.logging) {
301
+ this.deviceLogging = this.accessory.context.logging = device.logging;
302
+ this.debugLog(`Lock: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
303
+ } else if (this.platform.config.options?.logging) {
304
+ this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
305
+ this.debugLog(`Lock: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
306
+ } else {
307
+ this.deviceLogging = this.accessory.context.logging = 'standard';
308
+ this.debugLog(`Lock: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Logging for Device
314
+ */
315
+ infoLog(...log: any[]) {
316
+ if (this.enablingDeviceLogging()) {
317
+ this.platform.log.info(String(...log));
318
+ }
319
+ }
320
+
321
+ warnLog(...log: any[]) {
322
+ if (this.enablingDeviceLogging()) {
323
+ this.platform.log.warn(String(...log));
324
+ }
325
+ }
326
+
327
+ errorLog(...log: any[]) {
328
+ if (this.enablingDeviceLogging()) {
329
+ this.platform.log.error(String(...log));
330
+ }
331
+ }
332
+
333
+ debugLog(...log: any[]) {
334
+ if (this.enablingDeviceLogging()) {
335
+ if (this.deviceLogging === 'debug') {
336
+ this.platform.log.info('[DEBUG]', String(...log));
337
+ } else {
338
+ this.platform.log.debug(String(...log));
339
+ }
340
+ }
341
+ }
342
+
343
+ enablingDeviceLogging(): boolean {
344
+ return this.deviceLogging === 'debug' || this.deviceLogging === 'standard';
345
+ }
346
+ }
@@ -83,7 +83,7 @@ export class Meter {
83
83
  accessory
84
84
  .getService(this.platform.Service.AccessoryInformation)!
85
85
  .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
86
- .setCharacteristic(this.platform.Characteristic.Model, 'SWITCHBOT-METERTH-S1')
86
+ .setCharacteristic(this.platform.Characteristic.Model, this.model(device))
87
87
  .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!);
88
88
 
89
89
  // Temperature Sensor Service
@@ -393,6 +393,18 @@ export class Meter {
393
393
  //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
394
394
  }
395
395
 
396
+ model(device: device & devicesConfig): CharacteristicValue {
397
+ let model: string;
398
+ if (device.deviceType === 'Meter Plus (US)') {
399
+ model = 'SWITCHBOT-METERTH-W2301500';
400
+ } else if (device.deviceType === 'Meter Plus (JP)') {
401
+ model = 'SWITCHBOT-METERTH-W2201500';
402
+ } else {
403
+ model = 'SWITCHBOT-METERTH-S1';
404
+ }
405
+ return model;
406
+ }
407
+
396
408
  config(device: device & devicesConfig) {
397
409
  let config = {};
398
410
  if (device.meter) {
File without changes
@@ -52,7 +52,7 @@ export class Plug {
52
52
  accessory
53
53
  .getService(this.platform.Service.AccessoryInformation)!
54
54
  .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
55
- .setCharacteristic(this.platform.Characteristic.Model, 'SWITCHBOT-PLUG-SP11')
55
+ .setCharacteristic(this.platform.Characteristic.Model, this.model(device))
56
56
  .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!);
57
57
 
58
58
  // get the WindowCovering service if it exists, otherwise create a new WindowCovering service
@@ -145,8 +145,11 @@ export class Plug {
145
145
  /**
146
146
  * Pushes the requested changes to the SwitchBot API
147
147
  * deviceType commandType Command command parameter Description
148
- * Plug - "command" "turnOff" "default" = set to OFF state
149
- * Plug - "command" "turnOn" "default" = set to ON state
148
+ * Plug - "command" "turnOff" "default" = set to OFF state
149
+ * Plug - "command" "turnOn" "default" = set to ON state
150
+ * Plug Mini (US/JP) - "command" turnOn default = set to ON state
151
+ * Plug Mini (US/JP) - "command" turnOff default = set to OFF state
152
+ * Plug Mini (US/JP) - "command" toggle default = toggle state
150
153
  */
151
154
  async pushChanges() {
152
155
  if (this.On !== this.OnCached) {
@@ -228,7 +231,7 @@ export class Plug {
228
231
  }
229
232
  }
230
233
 
231
- private offlineOff() {
234
+ async offlineOff(): Promise<void> {
232
235
  if (this.device.offline) {
233
236
  this.On = false;
234
237
  this.service.getCharacteristic(this.platform.Characteristic.On).updateValue(this.On);
@@ -238,14 +241,26 @@ export class Plug {
238
241
  /**
239
242
  * Handle requests to set the value of the "On" characteristic
240
243
  */
241
- OnSet(value: CharacteristicValue) {
244
+ async OnSet(value: CharacteristicValue): Promise<void> {
242
245
  this.debugLog(`Plug: ${this.accessory.displayName} - Set On: ${value}`);
243
246
 
244
247
  this.On = value;
245
248
  this.doPlugUpdate.next();
246
249
  }
247
250
 
248
- config(device: device & devicesConfig) {
251
+ model(device: device & devicesConfig): string {
252
+ let model: string;
253
+ if (device.deviceType === 'Plug Mini (US)') {
254
+ model = 'SWITCHBOT-PLUG-W1901400';
255
+ } else if (device.deviceType === 'Plug Mini (JP)') {
256
+ model = 'SWITCHBOT-PLUG-W2001400';
257
+ } else {
258
+ model = 'SWITCHBOT-PLUG-SP11';
259
+ }
260
+ return model;
261
+ }
262
+
263
+ async config(device: device & devicesConfig): Promise<void> {
249
264
  let config = {};
250
265
  if (device.plug) {
251
266
  config = device.plug;
@@ -267,7 +282,7 @@ export class Plug {
267
282
  }
268
283
  }
269
284
 
270
- refreshRate(device: device & devicesConfig) {
285
+ async refreshRate(device: device & devicesConfig): Promise<void> {
271
286
  if (device.refreshRate) {
272
287
  this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
273
288
  this.debugLog(`Plug: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
@@ -277,7 +292,7 @@ export class Plug {
277
292
  }
278
293
  }
279
294
 
280
- scan(device: device & devicesConfig) {
295
+ async scan(device: device & devicesConfig): Promise<void> {
281
296
  if (device.scanDuration) {
282
297
  this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
283
298
  if (device.ble) {
@@ -291,7 +306,7 @@ export class Plug {
291
306
  }
292
307
  }
293
308
 
294
- logs(device: device & devicesConfig) {
309
+ async logs(device: device & devicesConfig): Promise<void> {
295
310
  if (this.platform.debugMode) {
296
311
  this.deviceLogging = this.accessory.context.logging = 'debugMode';
297
312
  this.debugLog(`Plug: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
@@ -310,25 +325,25 @@ export class Plug {
310
325
  /**
311
326
  * Logging for Device
312
327
  */
313
- infoLog(...log: any[]) {
328
+ infoLog(...log: any[]): void {
314
329
  if (this.enablingDeviceLogging()) {
315
330
  this.platform.log.info(String(...log));
316
331
  }
317
332
  }
318
333
 
319
- warnLog(...log: any[]) {
334
+ warnLog(...log: any[]): void {
320
335
  if (this.enablingDeviceLogging()) {
321
336
  this.platform.log.warn(String(...log));
322
337
  }
323
338
  }
324
339
 
325
- errorLog(...log: any[]) {
340
+ errorLog(...log: any[]): void {
326
341
  if (this.enablingDeviceLogging()) {
327
342
  this.platform.log.error(String(...log));
328
343
  }
329
344
  }
330
345
 
331
- debugLog(...log: any[]) {
346
+ debugLog(...log: any[]): void {
332
347
  if (this.enablingDeviceLogging()) {
333
348
  if (this.deviceLogging === 'debug') {
334
349
  this.platform.log.info('[DEBUG]', String(...log));
File without changes
File without changes
File without changes
File without changes
File without changes
package/src/platform.ts CHANGED
@@ -1,20 +1,21 @@
1
- import { Bot } from './devices/bots';
2
- import { Plug } from './devices/plugs';
3
- import { Meter } from './devices/meters';
4
- import { Motion } from './devices/motion';
5
- import { Contact } from './devices/contact';
6
- import { Curtain } from './devices/curtains';
7
- import { ColorBulb } from './devices/colorbulb';
8
- import { Humidifier } from './devices/humidifiers';
9
- import { TV } from './irdevices/tvs';
10
- import { Fan } from './irdevices/fans';
11
- import { Light } from './irdevices/lights';
12
- import { Others } from './irdevices/others';
13
- import { Camera } from './irdevices/cameras';
14
- import { AirPurifier } from './irdevices/airpurifiers';
15
- import { WaterHeater } from './irdevices/waterheaters';
16
- import { VacuumCleaner } from './irdevices/vacuumcleaners';
17
- import { AirConditioner } from './irdevices/airconditioners';
1
+ import { Bot } from './device/bot';
2
+ import { Plug } from './device/plug';
3
+ import { Lock } from './device/lock';
4
+ import { Meter } from './device/meter';
5
+ import { Motion } from './device/motion';
6
+ import { Contact } from './device/contact';
7
+ import { Curtain } from './device/curtain';
8
+ import { ColorBulb } from './device/colorbulb';
9
+ import { Humidifier } from './device/humidifier';
10
+ import { TV } from './irdevice/tv';
11
+ import { Fan } from './irdevice/fan';
12
+ import { Light } from './irdevice/light';
13
+ import { Others } from './irdevice/other';
14
+ import { Camera } from './irdevice/camera';
15
+ import { AirPurifier } from './irdevice/airpurifier';
16
+ import { WaterHeater } from './irdevice/waterheater';
17
+ import { VacuumCleaner } from './irdevice/vacuumcleaner';
18
+ import { AirConditioner } from './irdevice/airconditioner';
18
19
  import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
19
20
  import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, Service, Characteristic } from 'homebridge';
20
21
  import { PLATFORM_NAME, PLUGIN_NAME, DeviceURL, irdevice, device, SwitchBotPlatformConfig, deviceResponses, devicesConfig } from './settings';
@@ -365,6 +366,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
365
366
  this.createBot(device);
366
367
  break;
367
368
  case 'Meter':
369
+ case 'Meter Plus (US)':
370
+ case 'Meter Plus (JP)':
368
371
  this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
369
372
  this.createMeter(device);
370
373
  break;
@@ -381,10 +384,17 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
381
384
  this.createCurtain(device);
382
385
  break;
383
386
  case 'Plug':
387
+ case 'Plug Mini (US)':
388
+ case 'Plug Mini (JP)':
384
389
  this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
385
390
  this.createPlug(device);
386
391
  break;
392
+ case 'Lock':
393
+ this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
394
+ this.createLock(device);
395
+ break;
387
396
  case 'Color Bulb':
397
+ case 'Strip Light':
388
398
  this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
389
399
  this.createColorBulb(device);
390
400
  break;
@@ -900,6 +910,61 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
900
910
  }
901
911
  }
902
912
 
913
+ private async createLock(device: device & devicesConfig) {
914
+ const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.deviceType}`);
915
+
916
+ // see if an accessory with the same uuid has already been registered and restored from
917
+ // the cached devices we stored in the `configureAccessory` method above
918
+ const existingAccessory = this.accessories.find((accessory) => accessory.UUID === uuid);
919
+
920
+ if (existingAccessory) {
921
+ // the accessory already exists
922
+ if (!device.hide_device && device.enableCloudService) {
923
+ this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`);
924
+
925
+ // if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.:
926
+ existingAccessory.context.model = device.deviceType;
927
+ existingAccessory.context.deviceID = device.deviceId;
928
+ existingAccessory.displayName = device.deviceName;
929
+ existingAccessory.context.firmwareRevision = this.version;
930
+ existingAccessory.context.deviceType = `SwitchBot: ${device.deviceType}`;
931
+ await this.connectionTypeExistingAccessory(device, existingAccessory);
932
+ this.api.updatePlatformAccessories([existingAccessory]);
933
+ // create the accessory handler for the restored accessory
934
+ // this is imported from `platformAccessory.ts`
935
+ new Lock(this, existingAccessory, device);
936
+ this.debugLog(`${device.deviceType} uuid: ${device.deviceId}-${device.deviceType}, (${existingAccessory.UUID})`);
937
+ } else {
938
+ this.unregisterPlatformAccessories(existingAccessory);
939
+ }
940
+ } else if (!device.hide_device && device.enableCloudService) {
941
+ // the accessory does not yet exist, so we need to create it
942
+ this.infoLog(`Adding new accessory: ${device.deviceName} ${device.deviceType} DeviceID: ${device.deviceId}`);
943
+
944
+ // create a new accessory
945
+ const accessory = new this.api.platformAccessory(device.deviceName, uuid);
946
+
947
+ // store a copy of the device object in the `accessory.context`
948
+ // the `context` property can be used to store any data about the accessory you may need
949
+ accessory.context.device = device;
950
+ accessory.context.model = device.deviceType;
951
+ accessory.context.deviceID = device.deviceId;
952
+ accessory.context.firmwareRevision = this.version;
953
+ accessory.context.deviceType = `SwitchBot: ${device.deviceType}`;
954
+ await this.connectionTypeNewAccessory(device, accessory);
955
+ // create the accessory handler for the newly create accessory
956
+ // this is imported from `platformAccessory.ts`
957
+ new Lock(this, accessory, device);
958
+ this.debugLog(`${device.deviceType} uuid: ${device.deviceId}-${device.deviceType}, (${accessory.UUID})`);
959
+
960
+ // link the accessory to your platform
961
+ this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
962
+ this.accessories.push(accessory);
963
+ } else {
964
+ this.debugLog(`Unable to Register new device: ${device.deviceName} ${device.deviceType} - ${device.deviceId}`);
965
+ }
966
+ }
967
+
903
968
  private async createColorBulb(device: device & devicesConfig) {
904
969
  const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.deviceType}`);
905
970