@switchbot/homebridge-switchbot 2.0.0-beta.4 → 2.0.0-beta.40

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 (119) hide show
  1. package/CHANGELOG.md +12 -2
  2. package/README.md +3 -2
  3. package/config.schema.json +108 -20
  4. package/dist/device/bot.d.ts +34 -28
  5. package/dist/device/bot.d.ts.map +1 -1
  6. package/dist/device/bot.js +657 -648
  7. package/dist/device/bot.js.map +1 -1
  8. package/dist/device/ceilinglight.d.ts +137 -0
  9. package/dist/device/ceilinglight.d.ts.map +1 -0
  10. package/dist/device/ceilinglight.js +1127 -0
  11. package/dist/device/ceilinglight.js.map +1 -0
  12. package/dist/device/colorbulb.d.ts +36 -19
  13. package/dist/device/colorbulb.d.ts.map +1 -1
  14. package/dist/device/colorbulb.js +486 -309
  15. package/dist/device/colorbulb.js.map +1 -1
  16. package/dist/device/contact.d.ts +18 -8
  17. package/dist/device/contact.d.ts.map +1 -1
  18. package/dist/device/contact.js +306 -237
  19. package/dist/device/contact.js.map +1 -1
  20. package/dist/device/curtain.d.ts +23 -16
  21. package/dist/device/curtain.d.ts.map +1 -1
  22. package/dist/device/curtain.js +566 -502
  23. package/dist/device/curtain.js.map +1 -1
  24. package/dist/device/humidifier.d.ts +24 -18
  25. package/dist/device/humidifier.d.ts.map +1 -1
  26. package/dist/device/humidifier.js +453 -369
  27. package/dist/device/humidifier.js.map +1 -1
  28. package/dist/device/lock.d.ts +27 -8
  29. package/dist/device/lock.d.ts.map +1 -1
  30. package/dist/device/lock.js +305 -148
  31. package/dist/device/lock.js.map +1 -1
  32. package/dist/device/meter.d.ts +22 -16
  33. package/dist/device/meter.d.ts.map +1 -1
  34. package/dist/device/meter.js +367 -267
  35. package/dist/device/meter.js.map +1 -1
  36. package/dist/device/meterplus.d.ts +24 -12
  37. package/dist/device/meterplus.d.ts.map +1 -1
  38. package/dist/device/meterplus.js +381 -213
  39. package/dist/device/meterplus.js.map +1 -1
  40. package/dist/device/motion.d.ts +18 -8
  41. package/dist/device/motion.d.ts.map +1 -1
  42. package/dist/device/motion.js +279 -216
  43. package/dist/device/motion.js.map +1 -1
  44. package/dist/device/plug.d.ts +27 -17
  45. package/dist/device/plug.d.ts.map +1 -1
  46. package/dist/device/plug.js +295 -251
  47. package/dist/device/plug.js.map +1 -1
  48. package/dist/device/striplight.d.ts +43 -19
  49. package/dist/device/striplight.d.ts.map +1 -1
  50. package/dist/device/striplight.js +506 -262
  51. package/dist/device/striplight.js.map +1 -1
  52. package/dist/irdevice/airconditioner.d.ts +9 -12
  53. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  54. package/dist/irdevice/airconditioner.js +278 -230
  55. package/dist/irdevice/airconditioner.js.map +1 -1
  56. package/dist/irdevice/airpurifier.d.ts +5 -3
  57. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  58. package/dist/irdevice/airpurifier.js +154 -104
  59. package/dist/irdevice/airpurifier.js.map +1 -1
  60. package/dist/irdevice/camera.d.ts +11 -8
  61. package/dist/irdevice/camera.d.ts.map +1 -1
  62. package/dist/irdevice/camera.js +133 -86
  63. package/dist/irdevice/camera.js.map +1 -1
  64. package/dist/irdevice/fan.d.ts +10 -7
  65. package/dist/irdevice/fan.d.ts.map +1 -1
  66. package/dist/irdevice/fan.js +160 -109
  67. package/dist/irdevice/fan.js.map +1 -1
  68. package/dist/irdevice/light.d.ts +11 -8
  69. package/dist/irdevice/light.d.ts.map +1 -1
  70. package/dist/irdevice/light.js +137 -103
  71. package/dist/irdevice/light.js.map +1 -1
  72. package/dist/irdevice/other.d.ts +11 -8
  73. package/dist/irdevice/other.d.ts.map +1 -1
  74. package/dist/irdevice/other.js +145 -98
  75. package/dist/irdevice/other.js.map +1 -1
  76. package/dist/irdevice/tv.d.ts +12 -9
  77. package/dist/irdevice/tv.d.ts.map +1 -1
  78. package/dist/irdevice/tv.js +143 -93
  79. package/dist/irdevice/tv.js.map +1 -1
  80. package/dist/irdevice/vacuumcleaner.d.ts +9 -10
  81. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  82. package/dist/irdevice/vacuumcleaner.js +132 -89
  83. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  84. package/dist/irdevice/waterheater.d.ts +8 -9
  85. package/dist/irdevice/waterheater.d.ts.map +1 -1
  86. package/dist/irdevice/waterheater.js +132 -89
  87. package/dist/irdevice/waterheater.js.map +1 -1
  88. package/dist/platform.d.ts +15 -12
  89. package/dist/platform.d.ts.map +1 -1
  90. package/dist/platform.js +574 -454
  91. package/dist/platform.js.map +1 -1
  92. package/dist/settings.d.ts +21 -11
  93. package/dist/settings.d.ts.map +1 -1
  94. package/dist/settings.js.map +1 -1
  95. package/homebridge-ui/public/index.html +1 -0
  96. package/package.json +13 -11
  97. package/src/device/bot.ts +656 -659
  98. package/src/device/ceilinglight.ts +1208 -0
  99. package/src/device/colorbulb.ts +490 -344
  100. package/src/device/contact.ts +309 -241
  101. package/src/device/curtain.ts +571 -516
  102. package/src/device/humidifier.ts +456 -403
  103. package/src/device/lock.ts +314 -149
  104. package/src/device/meter.ts +378 -280
  105. package/src/device/meterplus.ts +391 -219
  106. package/src/device/motion.ts +285 -222
  107. package/src/device/plug.ts +299 -260
  108. package/src/device/striplight.ts +522 -289
  109. package/src/irdevice/airconditioner.ts +282 -241
  110. package/src/irdevice/airpurifier.ts +151 -103
  111. package/src/irdevice/camera.ts +131 -85
  112. package/src/irdevice/fan.ts +157 -106
  113. package/src/irdevice/light.ts +135 -103
  114. package/src/irdevice/other.ts +143 -97
  115. package/src/irdevice/tv.ts +142 -93
  116. package/src/irdevice/vacuumcleaner.ts +131 -89
  117. package/src/irdevice/waterheater.ts +130 -89
  118. package/src/platform.ts +567 -476
  119. package/src/settings.ts +22 -11
@@ -1,6 +1,7 @@
1
1
  import https from 'https';
2
2
  import crypto from 'crypto';
3
3
  import { IncomingMessage } from 'http';
4
+ import superStringify from 'super-stringify';
4
5
  import { SwitchBotPlatform } from '../platform';
5
6
  import { irDevicesConfig, irdevice, HostDomain, DevicePath } from '../settings';
6
7
  import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge';
@@ -16,7 +17,6 @@ export class Camera {
16
17
 
17
18
  // Characteristic Values
18
19
  On!: CharacteristicValue;
19
- OnCached!: CharacteristicValue;
20
20
 
21
21
  // Config
22
22
  deviceLogging!: string;
@@ -25,18 +25,17 @@ export class Camera {
25
25
  // default placeholders
26
26
  this.logs(device);
27
27
  this.config(device);
28
- if (this.On === undefined) {
29
- this.On = false;
30
- } else {
31
- this.On = this.accessory.context.On;
32
- }
28
+ this.context();
33
29
 
34
30
  // set accessory information
35
31
  accessory
36
32
  .getService(this.platform.Service.AccessoryInformation)!
37
33
  .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
38
34
  .setCharacteristic(this.platform.Characteristic.Model, device.remoteType)
39
- .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!);
35
+ .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!)
36
+ .setCharacteristic(this.platform.Characteristic.FirmwareRevision, this.FirmwareRevision(accessory, device))
37
+ .getCharacteristic(this.platform.Characteristic.FirmwareRevision)
38
+ .updateValue(this.FirmwareRevision(accessory, device));
40
39
 
41
40
  // get the Television service if it exists, otherwise create a new Television service
42
41
  // you can create multiple services for each accessory
@@ -56,8 +55,9 @@ export class Camera {
56
55
  }
57
56
 
58
57
  async OnSet(value: CharacteristicValue): Promise<void> {
59
- this.debugLog(`Camera: ${this.accessory.displayName} On: ${value}`);
58
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${value}`);
60
59
  this.On = value;
60
+ this.accessory.context.On = this.On;
61
61
  if (this.On) {
62
62
  this.pushOnChanges();
63
63
  } else {
@@ -65,28 +65,19 @@ export class Camera {
65
65
  }
66
66
  }
67
67
 
68
- async updateHomeKitCharacteristics(): Promise<void> {
69
- if (this.On === undefined) {
70
- this.debugLog(`Camera: ${this.accessory.displayName} On: ${this.On}`);
71
- } else {
72
- this.switchService?.updateCharacteristic(this.platform.Characteristic.On, this.On);
73
- this.debugLog(`Camera: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
74
- }
75
- }
76
-
77
68
  /**
78
69
  * Pushes the requested changes to the SwitchBot API
79
70
  * deviceType commandType Command command parameter Description
80
- * Light: "command" "turnOff" "default" = set to OFF state
81
- * Light: "command" "turnOn" "default" = set to ON state
82
- * Light: "command" "volumeAdd" "default" = volume up
83
- * Light: "command" "volumeSub" "default" = volume down
84
- * Light: "command" "channelAdd" "default" = next channel
85
- * Light: "command" "channelSub" "default" = previous channel
71
+ * Camera - "command" "turnOff" "default" = set to OFF state
72
+ * Camera - "command" "turnOn" "default" = set to ON state
73
+ * Camera - "command" "volumeAdd" "default" = volume up
74
+ * Camera - "command" "volumeSub" "default" = volume down
75
+ * Camera - "command" "channelAdd" "default" = next channel
76
+ * Camera - "command" "channelSub" "default" = previous channel
86
77
  */
87
78
  async pushOnChanges(): Promise<void> {
88
79
  if (this.On) {
89
- const body = JSON.stringify({
80
+ const body = superStringify({
90
81
  'command': 'turnOn',
91
82
  'parameter': 'default',
92
83
  'commandType': 'command',
@@ -97,7 +88,7 @@ export class Camera {
97
88
 
98
89
  async pushOffChanges(): Promise<void> {
99
90
  if (!this.On) {
100
- const body = JSON.stringify({
91
+ const body = superStringify({
101
92
  'command': 'turnOff',
102
93
  'parameter': 'default',
103
94
  'commandType': 'command',
@@ -107,92 +98,125 @@ export class Camera {
107
98
  }
108
99
 
109
100
  async pushChanges(body): Promise<void> {
110
- try {
101
+ if (this.device.connectionType === 'OpenAPI') {
102
+ try {
111
103
  // Make Push On request to the API
112
- const t = Date.now();
113
- const nonce = 'requestID';
114
- const data = this.platform.config.credentials?.token + t + nonce;
115
- const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret)
116
- .update(Buffer.from(data, 'utf-8'))
117
- .digest();
118
- const sign = signTerm.toString('base64');
119
- this.debugLog(`Camera: ${this.accessory.displayName} sign: ${sign}`);
120
- this.infoLog(`Camera: ${this.accessory.displayName} Sending request to SwitchBot API. body: ${body},`);
121
- const options = {
122
- hostname: HostDomain,
123
- port: 443,
124
- path: `${DevicePath}/${this.device.deviceId}/commands`,
125
- method: 'POST',
126
- headers: {
127
- 'Authorization': this.platform.config.credentials?.token,
128
- 'sign': sign,
129
- 'nonce': nonce,
130
- 't': t,
131
- 'Content-Type': 'application/json',
132
- 'Content-Length': body.length,
133
- },
134
- };
135
- const req = https.request(options, res => {
136
- this.debugLog(`Camera: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
137
- this.statusCode({ res });
138
- res.on('data', d => {
139
- this.debugLog(`Camera: ${this.accessory.displayName} d: ${d}`);
104
+ const t = Date.now();
105
+ const nonce = 'requestID';
106
+ const data = this.platform.config.credentials?.token + t + nonce;
107
+ const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret)
108
+ .update(Buffer.from(data, 'utf-8'))
109
+ .digest();
110
+ const sign = signTerm.toString('base64');
111
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} sign: ${sign}`);
112
+ this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Sending request to SwitchBot API. body: ${body},`);
113
+ const options = {
114
+ hostname: HostDomain,
115
+ port: 443,
116
+ path: `${DevicePath}/${this.device.deviceId}/commands`,
117
+ method: 'POST',
118
+ headers: {
119
+ 'Authorization': this.platform.config.credentials?.token,
120
+ 'sign': sign,
121
+ 'nonce': nonce,
122
+ 't': t,
123
+ 'Content-Type': 'application/json',
124
+ 'Content-Length': body.length,
125
+ },
126
+ };
127
+ const req = https.request(options, res => {
128
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushchanges statusCode: ${res.statusCode}`);
129
+ this.statusCode({ res });
130
+ res.on('data', d => {
131
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} d: ${d}`);
132
+ });
140
133
  });
141
- });
142
- req.on('error', (e: any) => {
143
- this.errorLog(`Camera: ${this.accessory.displayName} error message: ${e.message}`);
144
- });
145
- req.write(body);
146
- req.end();
147
- this.debugLog(`Camera: ${this.accessory.displayName} pushchanges: ${JSON.stringify(req)}`);
148
- this.updateHomeKitCharacteristics();
149
- this.OnCached = this.On;
150
- this.accessory.context.On = this.OnCached;
151
- } catch (e: any) {
152
- this.errorLog(`Camera: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`);
153
- if (this.deviceLogging.includes('debug')) {
154
- this.errorLog(
155
- `Camera: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + ` Error Message: ${JSON.stringify(e.message)}`,
134
+ req.on('error', (e: any) => {
135
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} error message: ${e.message}`);
136
+ });
137
+ req.write(body);
138
+ req.end();
139
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushchanges: ${superStringify(req)}`);
140
+ this.updateHomeKitCharacteristics();
141
+ } catch (e: any) {
142
+ this.apiError(e);
143
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,`
144
+ + ` Error Message: ${superStringify(e.message)}`,
156
145
  );
157
146
  }
158
- this.apiError(e);
147
+ } else {
148
+ this.warnLog(`${this.device.remoteType}: ${this.accessory.displayName}`
149
+ + ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`);
150
+ }
151
+ }
152
+
153
+ async updateHomeKitCharacteristics(): Promise<void> {
154
+ if (this.On === undefined) {
155
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.On}`);
156
+ } else {
157
+ this.accessory.context.On = this.On;
158
+ this.switchService?.updateCharacteristic(this.platform.Characteristic.On, this.On);
159
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
159
160
  }
160
161
  }
161
162
 
162
163
  async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
163
164
  switch (res.statusCode) {
164
165
  case 151:
165
- this.errorLog(`Camera: ${this.accessory.displayName} Command not supported by this device type.`);
166
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command not supported by this device type.`);
166
167
  break;
167
168
  case 152:
168
- this.errorLog(`Camera: ${this.accessory.displayName} Device not found.`);
169
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device not found.`);
169
170
  break;
170
171
  case 160:
171
- this.errorLog(`Camera: ${this.accessory.displayName} Command is not supported.`);
172
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command is not supported.`);
172
173
  break;
173
174
  case 161:
174
- this.errorLog(`Camera: ${this.accessory.displayName} Device is offline.`);
175
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline.`);
175
176
  break;
176
177
  case 171:
177
- this.errorLog(`Camera: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
178
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
178
179
  break;
179
180
  case 190:
180
181
  this.errorLog(
181
- `Camera: ${this.accessory.displayName} Device internal error due to device states not synchronized` +
182
- ` with server, Or command: ${JSON.stringify(res)} format is invalid`,
182
+ `${this.device.remoteType}: ${this.accessory.displayName} Device internal error due to device states not synchronized` +
183
+ ` with server, Or command: ${superStringify(res)} format is invalid`,
183
184
  );
184
185
  break;
185
186
  case 100:
186
- this.debugLog(`Camera: ${this.accessory.displayName} Command successfully sent.`);
187
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent.`);
187
188
  break;
188
189
  default:
189
- this.debugLog(`Camera: ${this.accessory.displayName} Unknown statusCode.`);
190
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode.`);
190
191
  }
191
192
  }
192
193
 
193
194
  async apiError(e: any): Promise<void> {
194
195
  this.switchService.updateCharacteristic(this.platform.Characteristic.On, e);
195
- //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
196
+ }
197
+
198
+ FirmwareRevision(accessory: PlatformAccessory, device: irdevice & irDevicesConfig): string {
199
+ let FirmwareRevision: string;
200
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName}`
201
+ + ` accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
202
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
203
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
204
+ if (accessory.context.FirmwareRevision) {
205
+ FirmwareRevision = accessory.context.FirmwareRevision;
206
+ } else if (device.firmware) {
207
+ FirmwareRevision = device.firmware;
208
+ } else {
209
+ FirmwareRevision = this.platform.version;
210
+ }
211
+ return FirmwareRevision;
212
+ }
213
+
214
+ async context() {
215
+ if (this.On === undefined) {
216
+ this.On = false;
217
+ } else {
218
+ this.On = this.accessory.context.On;
219
+ }
196
220
  }
197
221
 
198
222
  async config(device: irdevice & irDevicesConfig): Promise<void> {
@@ -203,24 +227,30 @@ export class Camera {
203
227
  if (device.logging !== undefined) {
204
228
  config['logging'] = device.logging;
205
229
  }
230
+ if (device.connectionType !== undefined) {
231
+ config['connectionType'] = device.connectionType;
232
+ }
233
+ if (device.external !== undefined) {
234
+ config['external'] = device.external;
235
+ }
206
236
  if (Object.entries(config).length !== 0) {
207
- this.infoLog(`Camera: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
237
+ this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Config: ${superStringify(config)}`);
208
238
  }
209
239
  }
210
240
 
211
241
  async logs(device: irdevice & irDevicesConfig): Promise<void> {
212
242
  if (this.platform.debugMode) {
213
243
  this.deviceLogging = this.accessory.context.logging = 'debugMode';
214
- this.debugLog(`Camera: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
244
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
215
245
  } else if (device.logging) {
216
246
  this.deviceLogging = this.accessory.context.logging = device.logging;
217
- this.debugLog(`Camera: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
247
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
218
248
  } else if (this.platform.config.options?.logging) {
219
249
  this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
220
- this.debugLog(`Camera: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
250
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
221
251
  } else {
222
252
  this.deviceLogging = this.accessory.context.logging = 'standard';
223
- this.debugLog(`Camera: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
253
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
224
254
  }
225
255
  }
226
256
 
@@ -239,12 +269,28 @@ export class Camera {
239
269
  }
240
270
  }
241
271
 
272
+ debugWarnLog(...log: any[]): void {
273
+ if (this.enablingDeviceLogging()) {
274
+ if (this.deviceLogging?.includes('debug')) {
275
+ this.platform.log.warn('[DEBUG]', String(...log));
276
+ }
277
+ }
278
+ }
279
+
242
280
  errorLog(...log: any[]): void {
243
281
  if (this.enablingDeviceLogging()) {
244
282
  this.platform.log.error(String(...log));
245
283
  }
246
284
  }
247
285
 
286
+ debugErrorLog(...log: any[]): void {
287
+ if (this.enablingDeviceLogging()) {
288
+ if (this.deviceLogging?.includes('debug')) {
289
+ this.platform.log.error('[DEBUG]', String(...log));
290
+ }
291
+ }
292
+ }
293
+
248
294
  debugLog(...log: any[]): void {
249
295
  if (this.enablingDeviceLogging()) {
250
296
  if (this.deviceLogging === 'debug') {