@switchbot/homebridge-switchbot 2.0.0-beta.2 → 2.0.0-beta.21

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 (113) hide show
  1. package/CHANGELOG.md +8 -1
  2. package/README.md +1 -2
  3. package/config.schema.json +75 -20
  4. package/dist/device/bot.d.ts +32 -28
  5. package/dist/device/bot.d.ts.map +1 -1
  6. package/dist/device/bot.js +646 -648
  7. package/dist/device/bot.js.map +1 -1
  8. package/dist/device/colorbulb.d.ts +34 -19
  9. package/dist/device/colorbulb.d.ts.map +1 -1
  10. package/dist/device/colorbulb.js +429 -304
  11. package/dist/device/colorbulb.js.map +1 -1
  12. package/dist/device/contact.d.ts +16 -7
  13. package/dist/device/contact.d.ts.map +1 -1
  14. package/dist/device/contact.js +288 -230
  15. package/dist/device/contact.js.map +1 -1
  16. package/dist/device/curtain.d.ts +21 -16
  17. package/dist/device/curtain.d.ts.map +1 -1
  18. package/dist/device/curtain.js +519 -489
  19. package/dist/device/curtain.js.map +1 -1
  20. package/dist/device/humidifier.d.ts +22 -18
  21. package/dist/device/humidifier.d.ts.map +1 -1
  22. package/dist/device/humidifier.js +415 -360
  23. package/dist/device/humidifier.js.map +1 -1
  24. package/dist/device/lock.d.ts +22 -8
  25. package/dist/device/lock.d.ts.map +1 -1
  26. package/dist/device/lock.js +253 -147
  27. package/dist/device/lock.js.map +1 -1
  28. package/dist/device/meter.d.ts +21 -16
  29. package/dist/device/meter.d.ts.map +1 -1
  30. package/dist/device/meter.js +325 -255
  31. package/dist/device/meter.js.map +1 -1
  32. package/dist/device/meterplus.d.ts +23 -12
  33. package/dist/device/meterplus.d.ts.map +1 -1
  34. package/dist/device/meterplus.js +346 -208
  35. package/dist/device/meterplus.js.map +1 -1
  36. package/dist/device/motion.d.ts +17 -8
  37. package/dist/device/motion.d.ts.map +1 -1
  38. package/dist/device/motion.js +261 -209
  39. package/dist/device/motion.js.map +1 -1
  40. package/dist/device/plug.d.ts +24 -16
  41. package/dist/device/plug.d.ts.map +1 -1
  42. package/dist/device/plug.js +267 -242
  43. package/dist/device/plug.js.map +1 -1
  44. package/dist/device/striplight.d.ts +51 -16
  45. package/dist/device/striplight.d.ts.map +1 -1
  46. package/dist/device/striplight.js +527 -185
  47. package/dist/device/striplight.js.map +1 -1
  48. package/dist/irdevice/airconditioner.d.ts +7 -12
  49. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  50. package/dist/irdevice/airconditioner.js +261 -230
  51. package/dist/irdevice/airconditioner.js.map +1 -1
  52. package/dist/irdevice/airpurifier.d.ts +3 -3
  53. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  54. package/dist/irdevice/airpurifier.js +137 -104
  55. package/dist/irdevice/airpurifier.js.map +1 -1
  56. package/dist/irdevice/camera.d.ts +9 -8
  57. package/dist/irdevice/camera.d.ts.map +1 -1
  58. package/dist/irdevice/camera.js +116 -86
  59. package/dist/irdevice/camera.js.map +1 -1
  60. package/dist/irdevice/fan.d.ts +8 -7
  61. package/dist/irdevice/fan.d.ts.map +1 -1
  62. package/dist/irdevice/fan.js +143 -109
  63. package/dist/irdevice/fan.js.map +1 -1
  64. package/dist/irdevice/light.d.ts +9 -8
  65. package/dist/irdevice/light.d.ts.map +1 -1
  66. package/dist/irdevice/light.js +120 -103
  67. package/dist/irdevice/light.js.map +1 -1
  68. package/dist/irdevice/other.d.ts +9 -8
  69. package/dist/irdevice/other.d.ts.map +1 -1
  70. package/dist/irdevice/other.js +128 -98
  71. package/dist/irdevice/other.js.map +1 -1
  72. package/dist/irdevice/tv.d.ts +10 -9
  73. package/dist/irdevice/tv.d.ts.map +1 -1
  74. package/dist/irdevice/tv.js +126 -93
  75. package/dist/irdevice/tv.js.map +1 -1
  76. package/dist/irdevice/vacuumcleaner.d.ts +7 -10
  77. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  78. package/dist/irdevice/vacuumcleaner.js +115 -89
  79. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  80. package/dist/irdevice/waterheater.d.ts +6 -9
  81. package/dist/irdevice/waterheater.d.ts.map +1 -1
  82. package/dist/irdevice/waterheater.js +115 -89
  83. package/dist/irdevice/waterheater.js.map +1 -1
  84. package/dist/platform.d.ts +5 -5
  85. package/dist/platform.d.ts.map +1 -1
  86. package/dist/platform.js +227 -199
  87. package/dist/platform.js.map +1 -1
  88. package/dist/settings.d.ts +15 -11
  89. package/dist/settings.d.ts.map +1 -1
  90. package/dist/settings.js.map +1 -1
  91. package/package.json +12 -10
  92. package/src/device/bot.ts +641 -657
  93. package/src/device/colorbulb.ts +440 -339
  94. package/src/device/contact.ts +289 -233
  95. package/src/device/curtain.ts +523 -503
  96. package/src/device/humidifier.ts +421 -396
  97. package/src/device/lock.ts +257 -147
  98. package/src/device/meter.ts +335 -268
  99. package/src/device/meterplus.ts +354 -213
  100. package/src/device/motion.ts +264 -213
  101. package/src/device/plug.ts +270 -251
  102. package/src/device/striplight.ts +552 -207
  103. package/src/irdevice/airconditioner.ts +263 -241
  104. package/src/irdevice/airpurifier.ts +132 -103
  105. package/src/irdevice/camera.ts +112 -85
  106. package/src/irdevice/fan.ts +138 -106
  107. package/src/irdevice/light.ts +116 -103
  108. package/src/irdevice/other.ts +124 -97
  109. package/src/irdevice/tv.ts +123 -93
  110. package/src/irdevice/vacuumcleaner.ts +112 -89
  111. package/src/irdevice/waterheater.ts +111 -89
  112. package/src/platform.ts +216 -214
  113. package/src/settings.ts +15 -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 VacuumCleaner {
16
17
 
17
18
  // Characteristic Values
18
19
  On!: CharacteristicValue;
19
- OnCached!: CharacteristicValue;
20
20
 
21
21
  // Config
22
22
  deviceLogging!: string;
@@ -25,23 +25,22 @@ export class VacuumCleaner {
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
43
42
  (this.switchService = accessory.getService(this.platform.Service.Switch) || accessory.addService(this.platform.Service.Switch)),
44
- `${accessory.displayName} Vacuum`;
43
+ `${accessory.displayName} ${device.remoteType}`;
45
44
 
46
45
  // To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
47
46
  // when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
@@ -56,8 +55,9 @@ export class VacuumCleaner {
56
55
  }
57
56
 
58
57
  async OnSet(value: CharacteristicValue): Promise<void> {
59
- this.debugLog(`Vacuum Cleaner: ${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
  await this.pushOnChanges();
63
63
  } else {
@@ -65,28 +65,15 @@ export class VacuumCleaner {
65
65
  }
66
66
  }
67
67
 
68
- async updateHomeKitCharacteristics(): Promise<void> {
69
- if (this.On === undefined) {
70
- this.debugLog(`Vacuum Cleaner: ${this.accessory.displayName} On: ${this.On}`);
71
- } else {
72
- this.switchService?.updateCharacteristic(this.platform.Characteristic.On, this.On);
73
- this.debugLog(`Vacuum Cleaner: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
74
- }
75
- }
76
-
77
68
  /**
78
69
  * Pushes the requested changes to the SwitchBot API
79
- * 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
70
+ * deviceType CommandType Command Parameter Description
71
+ * Vacuum Cleaner "command" "turnOff" "default" set to OFF state
72
+ * Vacuum Cleaner "command" "turnOn" "default" set to ON state
86
73
  */
87
74
  async pushOnChanges(): Promise<void> {
88
75
  if (this.On) {
89
- const body = JSON.stringify({
76
+ const body = superStringify({
90
77
  'command': 'turnOn',
91
78
  'parameter': 'default',
92
79
  'commandType': 'command',
@@ -97,7 +84,7 @@ export class VacuumCleaner {
97
84
 
98
85
  async pushOffChanges(): Promise<void> {
99
86
  if (!this.On) {
100
- const body = JSON.stringify({
87
+ const body = superStringify({
101
88
  'command': 'turnOff',
102
89
  'parameter': 'default',
103
90
  'commandType': 'command',
@@ -106,94 +93,127 @@ export class VacuumCleaner {
106
93
  }
107
94
  }
108
95
 
109
- async pushChanges(body): Promise<void> {
110
- try {
96
+ async pushChanges(body: any[]): Promise<void> {
97
+ if (this.device.connectionType === 'OpenAPI') {
98
+ try {
111
99
  // 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(`Vacuum Cleaner: ${this.accessory.displayName} sign: ${sign}`);
120
- this.infoLog(`Vacuum Cleaner: ${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(`Vacuum Cleaner: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
137
- this.statusCode({ res });
138
- res.on('data', d => {
139
- this.debugLog(`Vacuum Cleaner: ${this.accessory.displayName} d: ${d}`);
100
+ const t = Date.now();
101
+ const nonce = 'requestID';
102
+ const data = this.platform.config.credentials?.token + t + nonce;
103
+ const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret)
104
+ .update(Buffer.from(data, 'utf-8'))
105
+ .digest();
106
+ const sign = signTerm.toString('base64');
107
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} sign: ${sign}`);
108
+ this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Sending request to SwitchBot API. body: ${body},`);
109
+ const options = {
110
+ hostname: HostDomain,
111
+ port: 443,
112
+ path: `${DevicePath}/${this.device.deviceId}/commands`,
113
+ method: 'POST',
114
+ headers: {
115
+ 'Authorization': this.platform.config.credentials?.token,
116
+ 'sign': sign,
117
+ 'nonce': nonce,
118
+ 't': t,
119
+ 'Content-Type': 'application/json',
120
+ 'Content-Length': body.length,
121
+ },
122
+ };
123
+ const req = https.request(options, res => {
124
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushchanges statusCode: ${res.statusCode}`);
125
+ this.statusCode({ res });
126
+ res.on('data', d => {
127
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} d: ${d}`);
128
+ });
140
129
  });
141
- });
142
- req.on('error', (e: any) => {
143
- this.errorLog(`Vacuum Cleaner: ${this.accessory.displayName} error message: ${e.message}`);
144
- });
145
- req.write(body);
146
- req.end();
147
- this.debugLog(`Vacuum Cleaner: ${this.accessory.displayName} pushchanges: ${JSON.stringify(req)}`);
148
- this.OnCached = this.On;
149
- this.accessory.context.On = this.OnCached;
150
- this.updateHomeKitCharacteristics();
151
- } catch (e: any) {
152
- this.errorLog(`Vacuum Cleaner: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`);
153
- if (this.deviceLogging.includes('debug')) {
154
- this.errorLog(
155
- `Vacuum Cleaner: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` +
156
- ` Error Message: ${JSON.stringify(e.message)}`,
130
+ req.on('error', (e: any) => {
131
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} error message: ${e.message}`);
132
+ });
133
+ req.write(body);
134
+ req.end();
135
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushchanges: ${superStringify(req)}`);
136
+ this.accessory.context.On = this.On;
137
+ this.updateHomeKitCharacteristics();
138
+ } catch (e: any) {
139
+ this.apiError(e);
140
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,`
141
+ + ` Error Message: ${superStringify(e.message)}`,
157
142
  );
158
143
  }
159
- this.apiError(e);
144
+ } else {
145
+ this.warnLog(`${this.device.remoteType}: ${this.accessory.displayName}`
146
+ + ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`);
147
+ }
148
+ }
149
+
150
+ async updateHomeKitCharacteristics(): Promise<void> {
151
+ if (this.On === undefined) {
152
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} On: ${this.On}`);
153
+ } else {
154
+ this.accessory.context.On = this.On;
155
+ this.switchService?.updateCharacteristic(this.platform.Characteristic.On, this.On);
156
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
160
157
  }
161
158
  }
162
159
 
163
160
  async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
164
161
  switch (res.statusCode) {
165
162
  case 151:
166
- this.errorLog(`Vacuum Cleaner: ${this.accessory.displayName} Command not supported by this device type.`);
163
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command not supported by this device type.`);
167
164
  break;
168
165
  case 152:
169
- this.errorLog(`Vacuum Cleaner: ${this.accessory.displayName} Device not found.`);
166
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device not found.`);
170
167
  break;
171
168
  case 160:
172
- this.errorLog(`Vacuum Cleaner: ${this.accessory.displayName} Command is not supported.`);
169
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command is not supported.`);
173
170
  break;
174
171
  case 161:
175
- this.errorLog(`Vacuum Cleaner: ${this.accessory.displayName} Device is offline.`);
172
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline.`);
176
173
  break;
177
174
  case 171:
178
- this.errorLog(`Vacuum Cleaner: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
175
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
179
176
  break;
180
177
  case 190:
181
178
  this.errorLog(
182
- `Vacuum Cleaner: ${this.accessory.displayName} Device internal error due to device states not synchronized` +
183
- ` with server, Or command: ${JSON.stringify(res)} format is invalid`,
179
+ `${this.device.remoteType}: ${this.accessory.displayName} Device internal error due to device states not synchronized` +
180
+ ` with server, Or command: ${superStringify(res)} format is invalid`,
184
181
  );
185
182
  break;
186
183
  case 100:
187
- this.debugLog(`Vacuum Cleaner: ${this.accessory.displayName} Command successfully sent.`);
184
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent.`);
188
185
  break;
189
186
  default:
190
- this.debugLog(`Vacuum Cleaner: ${this.accessory.displayName} Unknown statusCode.`);
187
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode.`);
191
188
  }
192
189
  }
193
190
 
194
191
  async apiError(e: any): Promise<void> {
195
192
  this.switchService.updateCharacteristic(this.platform.Characteristic.On, e);
196
- //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
193
+ }
194
+
195
+ FirmwareRevision(accessory: PlatformAccessory, device: irdevice & irDevicesConfig): string {
196
+ let FirmwareRevision: string;
197
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName}`
198
+ + ` accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
199
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
200
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
201
+ if (accessory.context.FirmwareRevision) {
202
+ FirmwareRevision = accessory.context.FirmwareRevision;
203
+ } else if (device.firmware) {
204
+ FirmwareRevision = device.firmware;
205
+ } else {
206
+ FirmwareRevision = this.platform.version;
207
+ }
208
+ return FirmwareRevision;
209
+ }
210
+
211
+ async context() {
212
+ if (this.On === undefined) {
213
+ this.On = false;
214
+ } else {
215
+ this.On = this.accessory.context.On;
216
+ }
197
217
  }
198
218
 
199
219
  async config(device: irdevice & irDevicesConfig): Promise<void> {
@@ -204,24 +224,27 @@ export class VacuumCleaner {
204
224
  if (device.logging !== undefined) {
205
225
  config['logging'] = device.logging;
206
226
  }
227
+ if (device.connectionType !== undefined) {
228
+ config['connectionType'] = device.connectionType;
229
+ }
207
230
  if (Object.entries(config).length !== 0) {
208
- this.infoLog(`Vacuum Cleaner: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
231
+ this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Config: ${superStringify(config)}`);
209
232
  }
210
233
  }
211
234
 
212
235
  async logs(device: irdevice & irDevicesConfig): Promise<void> {
213
236
  if (this.platform.debugMode) {
214
237
  this.deviceLogging = this.accessory.context.logging = 'debugMode';
215
- this.debugLog(`Vacuum Cleaner: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
238
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
216
239
  } else if (device.logging) {
217
240
  this.deviceLogging = this.accessory.context.logging = device.logging;
218
- this.debugLog(`Vacuum Cleaner: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
241
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
219
242
  } else if (this.platform.config.options?.logging) {
220
243
  this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
221
- this.debugLog(`Vacuum Cleaner: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
244
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
222
245
  } else {
223
246
  this.deviceLogging = this.accessory.context.logging = 'standard';
224
- this.debugLog(`Vacuum Cleaner: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
247
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
225
248
  }
226
249
  }
227
250
 
@@ -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 WaterHeater {
16
17
 
17
18
  // Characteristic Values
18
19
  Active!: CharacteristicValue;
19
- ActiveCached!: CharacteristicValue;
20
20
 
21
21
  // Config
22
22
  deviceLogging!: string;
@@ -25,23 +25,22 @@ export class WaterHeater {
25
25
  // default placeholders
26
26
  this.logs(device);
27
27
  this.config(device);
28
- if (this.Active === undefined) {
29
- this.Active = this.platform.Characteristic.Active.INACTIVE;
30
- } else {
31
- this.Active = this.accessory.context.Active;
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
43
42
  (this.valveService = accessory.getService(this.platform.Service.Valve) || accessory.addService(this.platform.Service.Valve)),
44
- `${accessory.displayName} Water Heater`;
43
+ `${accessory.displayName} ${device.remoteType}`;
45
44
 
46
45
  // To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
47
46
  // when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
@@ -59,7 +58,9 @@ export class WaterHeater {
59
58
  }
60
59
 
61
60
  async ActiveSet(value: CharacteristicValue): Promise<void> {
62
- this.debugLog(`Water Heater: ${this.accessory.displayName} Active: ${value}`);
61
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${value}`);
62
+ this.Active = value;
63
+ this.accessory.context.Active = this.Active;
63
64
  if (value === this.platform.Characteristic.Active.INACTIVE) {
64
65
  await this.pushWaterHeaterOffChanges();
65
66
  this.valveService.setCharacteristic(this.platform.Characteristic.InUse, this.platform.Characteristic.InUse.NOT_IN_USE);
@@ -67,33 +68,17 @@ export class WaterHeater {
67
68
  await this.pushWaterHeaterOnChanges();
68
69
  this.valveService.setCharacteristic(this.platform.Characteristic.InUse, this.platform.Characteristic.InUse.IN_USE);
69
70
  }
70
- this.Active = value;
71
- this.ActiveCached = this.Active;
72
- this.accessory.context.Active = this.ActiveCached;
73
- }
74
-
75
- async updateHomeKitCharacteristics(): Promise<void> {
76
- if (this.Active === undefined) {
77
- this.debugLog(`Water Heater: ${this.accessory.displayName} Active: ${this.Active}`);
78
- } else {
79
- this.valveService?.updateCharacteristic(this.platform.Characteristic.Active, this.Active);
80
- this.debugLog(`Water Heater: ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`);
81
- }
82
71
  }
83
72
 
84
73
  /**
85
74
  * Pushes the requested changes to the SwitchBot API
86
- * deviceType commandType Command command parameter Description
87
- * WaterHeater: "command" "turnOff" "default" = set to OFF state
88
- * WaterHeater: "command" "turnOn" "default" = set to ON state
89
- * WaterHeater: "command" "volumeAdd" "default" = volume up
90
- * WaterHeater: "command" "volumeSub" "default" = volume down
91
- * WaterHeater: "command" "channelAdd" "default" = next channel
92
- * WaterHeater: "command" "channelSub" "default" = previous channel
75
+ * deviceType Command Type Command Parameter Description
76
+ * WaterHeater "command" "turnOff" "default" set to OFF state
77
+ * WaterHeater "command" "turnOn" "default" set to ON state
93
78
  */
94
79
  async pushWaterHeaterOnChanges(): Promise<void> {
95
80
  if (this.Active !== 1) {
96
- const body = JSON.stringify({
81
+ const body = superStringify({
97
82
  'command': 'turnOn',
98
83
  'parameter': 'default',
99
84
  'commandType': 'command',
@@ -104,7 +89,7 @@ export class WaterHeater {
104
89
 
105
90
  async pushWaterHeaterOffChanges(): Promise<void> {
106
91
  if (this.Active !== 0) {
107
- const body = JSON.stringify({
92
+ const body = superStringify({
108
93
  'command': 'turnOff',
109
94
  'parameter': 'default',
110
95
  'commandType': 'command',
@@ -114,90 +99,124 @@ export class WaterHeater {
114
99
  }
115
100
 
116
101
  async pushChanges(body): Promise<void> {
117
- try {
102
+ if (this.device.connectionType === 'OpenAPI') {
103
+ try {
118
104
  // Make Push On request to the API
119
- const t = Date.now();
120
- const nonce = 'requestID';
121
- const data = this.platform.config.credentials?.token + t + nonce;
122
- const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret)
123
- .update(Buffer.from(data, 'utf-8'))
124
- .digest();
125
- const sign = signTerm.toString('base64');
126
- this.debugLog(`Water Heater: ${this.accessory.displayName} sign: ${sign}`);
127
- this.infoLog(`Water Heater: ${this.accessory.displayName} Sending request to SwitchBot API. body: ${body},`);
128
- const options = {
129
- hostname: HostDomain,
130
- port: 443,
131
- path: `${DevicePath}/${this.device.deviceId}/commands`,
132
- method: 'POST',
133
- headers: {
134
- 'Authorization': this.platform.config.credentials?.token,
135
- 'sign': sign,
136
- 'nonce': nonce,
137
- 't': t,
138
- 'Content-Type': 'application/json',
139
- 'Content-Length': body.length,
140
- },
141
- };
142
- const req = https.request(options, res => {
143
- this.debugLog(`Water Heater: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
144
- this.statusCode({ res });
145
- res.on('data', d => {
146
- this.debugLog(`Water Heater: ${this.accessory.displayName} d: ${d}`);
105
+ const t = Date.now();
106
+ const nonce = 'requestID';
107
+ const data = this.platform.config.credentials?.token + t + nonce;
108
+ const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret)
109
+ .update(Buffer.from(data, 'utf-8'))
110
+ .digest();
111
+ const sign = signTerm.toString('base64');
112
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} sign: ${sign}`);
113
+ this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Sending request to SwitchBot API. body: ${body},`);
114
+ const options = {
115
+ hostname: HostDomain,
116
+ port: 443,
117
+ path: `${DevicePath}/${this.device.deviceId}/commands`,
118
+ method: 'POST',
119
+ headers: {
120
+ 'Authorization': this.platform.config.credentials?.token,
121
+ 'sign': sign,
122
+ 'nonce': nonce,
123
+ 't': t,
124
+ 'Content-Type': 'application/json',
125
+ 'Content-Length': body.length,
126
+ },
127
+ };
128
+ const req = https.request(options, res => {
129
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushchanges statusCode: ${res.statusCode}`);
130
+ this.statusCode({ res });
131
+ res.on('data', d => {
132
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} d: ${d}`);
133
+ });
147
134
  });
148
- });
149
- req.on('error', (e: any) => {
150
- this.errorLog(`Water Heater: ${this.accessory.displayName} error message: ${e.message}`);
151
- });
152
- req.write(body);
153
- req.end();
154
- this.debugLog(`Water Heater: ${this.accessory.displayName} pushchanges: ${JSON.stringify(req)}`);
155
- this.updateHomeKitCharacteristics();
156
- } catch (e: any) {
157
- this.errorLog(`Water Heater: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`);
158
- if (this.deviceLogging.includes('debug')) {
159
- this.errorLog(
160
- `Water Heater: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + ` Error Message: ${JSON.stringify(e.message)}`,
161
- );
135
+ req.on('error', (e: any) => {
136
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} error message: ${e.message}`);
137
+ });
138
+ req.write(body);
139
+ req.end();
140
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushchanges: ${superStringify(req)}`);
141
+ this.updateHomeKitCharacteristics();
142
+ } catch (e: any) {
143
+ this.apiError(e);
144
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,`
145
+ + ` Error Message: ${superStringify(e.message)}`);
162
146
  }
163
- 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.Active === undefined) {
155
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${this.Active}`);
156
+ } else {
157
+ this.accessory.context.Active = this.Active;
158
+ this.valveService?.updateCharacteristic(this.platform.Characteristic.Active, this.Active);
159
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`);
164
160
  }
165
161
  }
166
162
 
167
163
  async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
168
164
  switch (res.statusCode) {
169
165
  case 151:
170
- this.errorLog(`Water Heater: ${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.`);
171
167
  break;
172
168
  case 152:
173
- this.errorLog(`Water Heater: ${this.accessory.displayName} Device not found.`);
169
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device not found.`);
174
170
  break;
175
171
  case 160:
176
- this.errorLog(`Water Heater: ${this.accessory.displayName} Command is not supported.`);
172
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command is not supported.`);
177
173
  break;
178
174
  case 161:
179
- this.errorLog(`Water Heater: ${this.accessory.displayName} Device is offline.`);
175
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline.`);
180
176
  break;
181
177
  case 171:
182
- this.errorLog(`Water Heater: ${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}`);
183
179
  break;
184
180
  case 190:
185
181
  this.errorLog(
186
- `Water Heater: ${this.accessory.displayName} Device internal error due to device states not synchronized` +
187
- ` 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`,
188
184
  );
189
185
  break;
190
186
  case 100:
191
- this.debugLog(`Water Heater: ${this.accessory.displayName} Command successfully sent.`);
187
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent.`);
192
188
  break;
193
189
  default:
194
- this.debugLog(`Water Heater: ${this.accessory.displayName} Unknown statusCode.`);
190
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode.`);
195
191
  }
196
192
  }
197
193
 
198
194
  async apiError(e: any): Promise<void> {
199
195
  this.valveService.updateCharacteristic(this.platform.Characteristic.Active, e);
200
- //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.Active === undefined) {
216
+ this.Active = this.platform.Characteristic.Active.INACTIVE;
217
+ } else {
218
+ this.Active = this.accessory.context.Active;
219
+ }
201
220
  }
202
221
 
203
222
  async config(device: irdevice & irDevicesConfig): Promise<void> {
@@ -208,24 +227,27 @@ export class WaterHeater {
208
227
  if (device.logging !== undefined) {
209
228
  config['logging'] = device.logging;
210
229
  }
230
+ if (device.connectionType !== undefined) {
231
+ config['connectionType'] = device.connectionType;
232
+ }
211
233
  if (Object.entries(config).length !== 0) {
212
- this.infoLog(`Water Heater: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
234
+ this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Config: ${superStringify(config)}`);
213
235
  }
214
236
  }
215
237
 
216
238
  async logs(device: irdevice & irDevicesConfig): Promise<void> {
217
239
  if (this.platform.debugMode) {
218
240
  this.deviceLogging = this.accessory.context.logging = 'debugMode';
219
- this.debugLog(`Water Heater: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
241
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
220
242
  } else if (device.logging) {
221
243
  this.deviceLogging = this.accessory.context.logging = device.logging;
222
- this.debugLog(`Water Heater: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
244
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
223
245
  } else if (this.platform.config.options?.logging) {
224
246
  this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
225
- this.debugLog(`Water Heater: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
247
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
226
248
  } else {
227
249
  this.deviceLogging = this.accessory.context.logging = 'standard';
228
- this.debugLog(`Water Heater: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
250
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
229
251
  }
230
252
  }
231
253