@switchbot/homebridge-switchbot 0.1.2-beta.7 → 0.2.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/feature-request.yml +1 -1
- package/.github/dependabot.yml +2 -0
- package/.github/workflows/codeql-analysis.yml +71 -0
- package/CHANGELOG.md +5 -1
- package/README.md +2 -1
- package/config.schema.json +420 -471
- package/dist/devices/bots.d.ts +19 -5
- package/dist/devices/bots.d.ts.map +1 -1
- package/dist/devices/bots.js +172 -148
- package/dist/devices/bots.js.map +1 -1
- package/dist/devices/bulb.d.ts +8 -6
- package/dist/devices/bulb.d.ts.map +1 -1
- package/dist/devices/bulb.js +45 -28
- package/dist/devices/bulb.js.map +1 -1
- package/dist/devices/contact.d.ts +10 -5
- package/dist/devices/contact.d.ts.map +1 -1
- package/dist/devices/contact.js +94 -63
- package/dist/devices/contact.js.map +1 -1
- package/dist/devices/curtains.d.ts +14 -5
- package/dist/devices/curtains.d.ts.map +1 -1
- package/dist/devices/curtains.js +174 -120
- package/dist/devices/curtains.js.map +1 -1
- package/dist/devices/humidifiers.d.ts +6 -6
- package/dist/devices/humidifiers.d.ts.map +1 -1
- package/dist/devices/humidifiers.js +78 -94
- package/dist/devices/humidifiers.js.map +1 -1
- package/dist/devices/indoorcam.d.ts +43 -0
- package/dist/devices/indoorcam.d.ts.map +1 -0
- package/dist/devices/indoorcam.js +241 -0
- package/dist/devices/indoorcam.js.map +1 -0
- package/dist/devices/meters.d.ts +8 -5
- package/dist/devices/meters.d.ts.map +1 -1
- package/dist/devices/meters.js +126 -92
- package/dist/devices/meters.js.map +1 -1
- package/dist/devices/motion.d.ts +9 -5
- package/dist/devices/motion.d.ts.map +1 -1
- package/dist/devices/motion.js +74 -59
- package/dist/devices/motion.js.map +1 -1
- package/dist/devices/plugs.d.ts +5 -4
- package/dist/devices/plugs.d.ts.map +1 -1
- package/dist/devices/plugs.js +65 -56
- package/dist/devices/plugs.js.map +1 -1
- package/dist/irdevices/airconditioners.d.ts +3 -3
- package/dist/irdevices/airconditioners.d.ts.map +1 -1
- package/dist/irdevices/airconditioners.js +12 -7
- package/dist/irdevices/airconditioners.js.map +1 -1
- package/dist/irdevices/airpurifiers.d.ts +3 -3
- package/dist/irdevices/airpurifiers.d.ts.map +1 -1
- package/dist/irdevices/airpurifiers.js +1 -1
- package/dist/irdevices/airpurifiers.js.map +1 -1
- package/dist/irdevices/cameras.d.ts +3 -3
- package/dist/irdevices/cameras.d.ts.map +1 -1
- package/dist/irdevices/cameras.js +1 -1
- package/dist/irdevices/cameras.js.map +1 -1
- package/dist/irdevices/fans.d.ts +3 -3
- package/dist/irdevices/fans.d.ts.map +1 -1
- package/dist/irdevices/fans.js +11 -12
- package/dist/irdevices/fans.js.map +1 -1
- package/dist/irdevices/lights.d.ts +3 -3
- package/dist/irdevices/lights.d.ts.map +1 -1
- package/dist/irdevices/lights.js +1 -1
- package/dist/irdevices/lights.js.map +1 -1
- package/dist/irdevices/others.d.ts +3 -3
- package/dist/irdevices/others.d.ts.map +1 -1
- package/dist/irdevices/others.js +8 -8
- package/dist/irdevices/others.js.map +1 -1
- package/dist/irdevices/tvs.d.ts +3 -3
- package/dist/irdevices/tvs.d.ts.map +1 -1
- package/dist/irdevices/tvs.js +16 -6
- package/dist/irdevices/tvs.js.map +1 -1
- package/dist/irdevices/vacuumcleaners.d.ts +3 -3
- package/dist/irdevices/vacuumcleaners.d.ts.map +1 -1
- package/dist/irdevices/vacuumcleaners.js +2 -3
- package/dist/irdevices/vacuumcleaners.js.map +1 -1
- package/dist/irdevices/waterheaters.d.ts +3 -3
- package/dist/irdevices/waterheaters.d.ts.map +1 -1
- package/dist/irdevices/waterheaters.js +2 -2
- package/dist/irdevices/waterheaters.js.map +1 -1
- package/dist/platform.d.ts +14 -5
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +420 -418
- package/dist/platform.js.map +1 -1
- package/dist/settings.d.ts +18 -15
- package/dist/settings.d.ts.map +1 -1
- package/package.json +65 -65
- package/src/devices/bots.ts +188 -165
- package/src/devices/bulb.ts +48 -40
- package/src/devices/contact.ts +100 -73
- package/src/devices/curtains.ts +192 -140
- package/src/devices/humidifiers.ts +82 -124
- package/src/devices/indoorcam.ts +303 -0
- package/src/devices/meters.ts +130 -110
- package/src/devices/motion.ts +80 -69
- package/src/devices/plugs.ts +67 -65
- package/src/irdevices/airconditioners.ts +14 -10
- package/src/irdevices/airpurifiers.ts +4 -4
- package/src/irdevices/cameras.ts +4 -4
- package/src/irdevices/fans.ts +14 -18
- package/src/irdevices/lights.ts +4 -4
- package/src/irdevices/others.ts +12 -14
- package/src/irdevices/tvs.ts +21 -10
- package/src/irdevices/vacuumcleaners.ts +5 -9
- package/src/irdevices/waterheaters.ts +5 -5
- package/src/platform.ts +455 -619
- package/src/settings.ts +19 -15
- package/.github/workflows/issue-validate.yml +0 -18
|
@@ -2,8 +2,7 @@ import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
|
|
|
2
2
|
import { SwitchBotPlatform } from '../platform';
|
|
3
3
|
import { interval, Subject } from 'rxjs';
|
|
4
4
|
import { debounceTime, skipWhile, tap } from 'rxjs/operators';
|
|
5
|
-
import { DeviceURL, device,
|
|
6
|
-
import { AxiosResponse } from 'axios';
|
|
5
|
+
import { DeviceURL, device, devicesConfig } from '../settings';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Platform Accessory
|
|
@@ -25,16 +24,17 @@ export class Humidifier {
|
|
|
25
24
|
WaterLevel!: CharacteristicValue;
|
|
26
25
|
|
|
27
26
|
// Others
|
|
28
|
-
deviceStatus!:
|
|
27
|
+
deviceStatus!: any;
|
|
29
28
|
|
|
30
29
|
// Updates
|
|
31
30
|
humidifierUpdateInProgress!: boolean;
|
|
32
31
|
doHumidifierUpdate;
|
|
32
|
+
set_minStep: number | undefined;
|
|
33
33
|
|
|
34
34
|
constructor(
|
|
35
35
|
private readonly platform: SwitchBotPlatform,
|
|
36
36
|
private accessory: PlatformAccessory,
|
|
37
|
-
public device: device,
|
|
37
|
+
public device: device & devicesConfig,
|
|
38
38
|
) {
|
|
39
39
|
// default placeholders
|
|
40
40
|
this.CurrentRelativeHumidity = 0;
|
|
@@ -63,7 +63,7 @@ export class Humidifier {
|
|
|
63
63
|
// you can create multiple services for each accessory
|
|
64
64
|
(this.service =
|
|
65
65
|
accessory.getService(this.platform.Service.HumidifierDehumidifier) ||
|
|
66
|
-
accessory.addService(this.platform.Service.HumidifierDehumidifier)), `${
|
|
66
|
+
accessory.addService(this.platform.Service.HumidifierDehumidifier)), `${accessory.displayName} Humidifier`;
|
|
67
67
|
|
|
68
68
|
// To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
|
|
69
69
|
// when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
|
|
@@ -100,25 +100,23 @@ export class Humidifier {
|
|
|
100
100
|
validValueRanges: [0, 100],
|
|
101
101
|
minValue: 0,
|
|
102
102
|
maxValue: 100,
|
|
103
|
-
minStep: this.
|
|
103
|
+
minStep: this.minStep(),
|
|
104
104
|
})
|
|
105
105
|
.onSet(this.handleRelativeHumidityHumidifierThresholdSet.bind(this));
|
|
106
106
|
|
|
107
107
|
// create a new Temperature Sensor service
|
|
108
108
|
// Temperature Sensor Service
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
this.platform.log.error('Removing service');
|
|
112
|
-
}
|
|
109
|
+
if (device.humidifier?.hide_temperature) {
|
|
110
|
+
this.platform.device('Removing Temerature Sensor Service');
|
|
113
111
|
this.temperatureservice = this.accessory.getService(this.platform.Service.TemperatureSensor);
|
|
114
112
|
accessory.removeService(this.temperatureservice!);
|
|
115
113
|
} else if (!this.temperatureservice) {
|
|
116
|
-
|
|
117
|
-
this.platform.log.warn('Adding service');
|
|
118
|
-
}
|
|
114
|
+
this.platform.device('Adding Temerature Sensor Service');
|
|
119
115
|
(this.temperatureservice =
|
|
120
116
|
this.accessory.getService(this.platform.Service.TemperatureSensor) ||
|
|
121
|
-
this.accessory.addService(this.platform.Service.TemperatureSensor)), `${
|
|
117
|
+
this.accessory.addService(this.platform.Service.TemperatureSensor)), `${accessory.displayName} Temperature Sensor`;
|
|
118
|
+
|
|
119
|
+
this.temperatureservice.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Temperature Sensor`);
|
|
122
120
|
|
|
123
121
|
this.temperatureservice
|
|
124
122
|
.getCharacteristic(this.platform.Characteristic.CurrentTemperature)
|
|
@@ -129,11 +127,11 @@ export class Humidifier {
|
|
|
129
127
|
minStep: 0.1,
|
|
130
128
|
})
|
|
131
129
|
.onGet(() => {
|
|
132
|
-
return this.CurrentTemperature;
|
|
130
|
+
return Number.isNaN(this.CurrentTemperature);
|
|
133
131
|
});
|
|
134
132
|
} else {
|
|
135
133
|
if (this.platform.config.options?.debug) {
|
|
136
|
-
this.platform.
|
|
134
|
+
this.platform.device('Temperature Sensor Not Added');
|
|
137
135
|
}
|
|
138
136
|
}
|
|
139
137
|
|
|
@@ -168,6 +166,15 @@ export class Humidifier {
|
|
|
168
166
|
});
|
|
169
167
|
}
|
|
170
168
|
|
|
169
|
+
private minStep(): number | undefined {
|
|
170
|
+
if (this.device.humidifier?.set_minStep) {
|
|
171
|
+
this.set_minStep = this.device.humidifier?.set_minStep;
|
|
172
|
+
} else {
|
|
173
|
+
this.set_minStep = 1;
|
|
174
|
+
}
|
|
175
|
+
return this.set_minStep;
|
|
176
|
+
}
|
|
177
|
+
|
|
171
178
|
/**
|
|
172
179
|
* Parse the device status from the SwitchBot api
|
|
173
180
|
*/
|
|
@@ -175,9 +182,7 @@ export class Humidifier {
|
|
|
175
182
|
// Current Relative Humidity
|
|
176
183
|
this.CurrentRelativeHumidity = this.deviceStatus.body.humidity!;
|
|
177
184
|
this.platform.debug(`Humidifier ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
|
|
178
|
-
|
|
179
|
-
this.platform.log.warn(JSON.stringify(this.deviceStatus.body));
|
|
180
|
-
}
|
|
185
|
+
this.platform.device(JSON.stringify(this.deviceStatus.body));
|
|
181
186
|
// Water Level
|
|
182
187
|
if (this.deviceStatus.body.lackWater) {
|
|
183
188
|
this.WaterLevel = 0;
|
|
@@ -220,8 +225,8 @@ export class Humidifier {
|
|
|
220
225
|
this.platform.debug(`Humidifier ${this.accessory.displayName} RelativeHumidityHumidifierThreshold: ${this.RelativeHumidityHumidifierThreshold}`);
|
|
221
226
|
this.platform.debug(`Humidifier ${this.accessory.displayName} CurrentHumidifierDehumidifierState: ${this.CurrentHumidifierDehumidifierState}`);
|
|
222
227
|
// Current Temperature
|
|
223
|
-
if (!this.
|
|
224
|
-
this.CurrentTemperature = this.deviceStatus.body.temperature
|
|
228
|
+
if (!this.device.humidifier?.hide_temperature) {
|
|
229
|
+
this.CurrentTemperature = this.deviceStatus.body.temperature;
|
|
225
230
|
this.platform.debug(`Humidifier ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`);
|
|
226
231
|
}
|
|
227
232
|
}
|
|
@@ -231,18 +236,17 @@ export class Humidifier {
|
|
|
231
236
|
*/
|
|
232
237
|
async refreshStatus() {
|
|
233
238
|
try {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
).data;
|
|
237
|
-
if (deviceStatus.message === 'success') {
|
|
238
|
-
this.deviceStatus = deviceStatus;
|
|
239
|
+
this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
|
|
240
|
+
if (this.deviceStatus.message === 'success') {
|
|
239
241
|
this.platform.debug(`Humidifier ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
|
|
240
242
|
this.parseStatus();
|
|
241
243
|
this.updateHomeKitCharacteristics();
|
|
244
|
+
} else {
|
|
245
|
+
this.platform.debug(this.deviceStatus);
|
|
242
246
|
}
|
|
243
247
|
} catch (e: any) {
|
|
244
|
-
this.platform.log.error(`Humidifier
|
|
245
|
-
this.platform.debug(`Humidifier ${this.accessory.displayName} ${JSON.stringify(e)}`);
|
|
248
|
+
this.platform.log.error(`Humidifier ${this.accessory.displayName}, Failed to update status. Error Message: ${JSON.stringify(e.message)}`);
|
|
249
|
+
this.platform.debug(`Humidifier ${this.accessory.displayName}, Error: ${JSON.stringify(e)}`);
|
|
246
250
|
this.apiError(e);
|
|
247
251
|
}
|
|
248
252
|
}
|
|
@@ -273,11 +277,11 @@ export class Humidifier {
|
|
|
273
277
|
'commandType:',
|
|
274
278
|
payload.commandType,
|
|
275
279
|
);
|
|
276
|
-
this.platform.debug(`Humidifier ${this.accessory.displayName} pushChanges
|
|
280
|
+
this.platform.debug(`Humidifier ${this.accessory.displayName} pushChanges: ${JSON.stringify(payload)}`);
|
|
277
281
|
|
|
278
282
|
// Make the API request
|
|
279
|
-
const push = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload);
|
|
280
|
-
this.platform.debug(`Humidifier ${this.accessory.displayName} Changes pushed
|
|
283
|
+
const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload));
|
|
284
|
+
this.platform.debug(`Humidifier ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`);
|
|
281
285
|
this.statusCode(push);
|
|
282
286
|
} else if (
|
|
283
287
|
this.TargetHumidifierDehumidifierState ===
|
|
@@ -317,12 +321,12 @@ export class Humidifier {
|
|
|
317
321
|
'commandType:',
|
|
318
322
|
payload.commandType,
|
|
319
323
|
);
|
|
320
|
-
this.platform.debug(`Humidifier ${this.accessory.displayName} pushAutoChanges
|
|
324
|
+
this.platform.debug(`Humidifier ${this.accessory.displayName} pushAutoChanges: ${JSON.stringify(payload)}`);
|
|
321
325
|
|
|
322
326
|
// Make the API request
|
|
323
|
-
const
|
|
324
|
-
this.platform.debug(`Humidifier ${this.accessory.displayName} Changes pushed
|
|
325
|
-
this.
|
|
327
|
+
const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload));
|
|
328
|
+
this.platform.debug(`Humidifier ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`);
|
|
329
|
+
this.statusCode(push);
|
|
326
330
|
}
|
|
327
331
|
} catch (e: any) {
|
|
328
332
|
this.platform.log.error(JSON.stringify(e.message));
|
|
@@ -354,12 +358,12 @@ export class Humidifier {
|
|
|
354
358
|
'commandType:',
|
|
355
359
|
payload.commandType,
|
|
356
360
|
);
|
|
357
|
-
this.platform.debug(`Humidifier ${this.accessory.displayName} pushActiveChanges
|
|
361
|
+
this.platform.debug(`Humidifier ${this.accessory.displayName} pushActiveChanges: ${JSON.stringify(payload)}`);
|
|
358
362
|
|
|
359
363
|
// Make the API request
|
|
360
|
-
const
|
|
361
|
-
this.platform.debug(`Humidifier ${this.accessory.displayName} Changes pushed
|
|
362
|
-
this.
|
|
364
|
+
const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload));
|
|
365
|
+
this.platform.debug(`Humidifier ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`);
|
|
366
|
+
this.statusCode(push);
|
|
363
367
|
}
|
|
364
368
|
} catch (e: any) {
|
|
365
369
|
this.platform.log.error(JSON.stringify(e.message));
|
|
@@ -372,41 +376,51 @@ export class Humidifier {
|
|
|
372
376
|
* Updates the status for each of the HomeKit Characteristics
|
|
373
377
|
*/
|
|
374
378
|
updateHomeKitCharacteristics() {
|
|
375
|
-
if (this.CurrentRelativeHumidity
|
|
376
|
-
this.
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
);
|
|
379
|
+
if (this.CurrentRelativeHumidity === undefined) {
|
|
380
|
+
this.platform.debug(`Humidifier ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
|
|
381
|
+
} else {
|
|
382
|
+
this.service.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity);
|
|
383
|
+
this.platform.device(`Humidifier ${this.accessory.displayName} updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
|
|
380
384
|
}
|
|
381
|
-
if (this.WaterLevel
|
|
385
|
+
if (this.WaterLevel === undefined) {
|
|
386
|
+
this.platform.debug(`Humidifier ${this.accessory.displayName} WaterLevel: ${this.WaterLevel}`);
|
|
387
|
+
} else {
|
|
382
388
|
this.service.updateCharacteristic(this.platform.Characteristic.WaterLevel, this.WaterLevel);
|
|
389
|
+
this.platform.device(`Humidifier ${this.accessory.displayName} updateCharacteristic WaterLevel: ${this.WaterLevel}`);
|
|
383
390
|
}
|
|
384
|
-
if (this.CurrentHumidifierDehumidifierState
|
|
385
|
-
this.
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
391
|
+
if (this.CurrentHumidifierDehumidifierState === undefined) {
|
|
392
|
+
this.platform.debug(`Humidifier ${this.accessory.displayName} CurrentHumidifierDehumidifierState: ${this.CurrentHumidifierDehumidifierState}`);
|
|
393
|
+
} else {
|
|
394
|
+
this.service.updateCharacteristic(this.platform.Characteristic.CurrentHumidifierDehumidifierState, this.CurrentHumidifierDehumidifierState);
|
|
395
|
+
this.platform.device(`Humidifier ${this.accessory.displayName}`
|
|
396
|
+
+ ` updateCharacteristic CurrentHumidifierDehumidifierState: ${this.CurrentHumidifierDehumidifierState}`);
|
|
389
397
|
}
|
|
390
|
-
if (this.TargetHumidifierDehumidifierState
|
|
391
|
-
this.
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
398
|
+
if (this.TargetHumidifierDehumidifierState === undefined) {
|
|
399
|
+
this.platform.debug(`Humidifier ${this.accessory.displayName} TargetHumidifierDehumidifierState: ${this.TargetHumidifierDehumidifierState}`);
|
|
400
|
+
} else {
|
|
401
|
+
this.service.updateCharacteristic(this.platform.Characteristic.TargetHumidifierDehumidifierState, this.TargetHumidifierDehumidifierState);
|
|
402
|
+
this.platform.device(`Humidifier ${this.accessory.displayName}`
|
|
403
|
+
+ ` updateCharacteristic TargetHumidifierDehumidifierState: ${this.TargetHumidifierDehumidifierState}`);
|
|
395
404
|
}
|
|
396
|
-
if (this.Active
|
|
405
|
+
if (this.Active === undefined) {
|
|
406
|
+
this.platform.debug(`Humidifier ${this.accessory.displayName} Active: ${this.Active}`);
|
|
407
|
+
} else {
|
|
397
408
|
this.service.updateCharacteristic(this.platform.Characteristic.Active, this.Active);
|
|
409
|
+
this.platform.device(`Humidifier ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`);
|
|
398
410
|
}
|
|
399
|
-
if (this.RelativeHumidityHumidifierThreshold
|
|
400
|
-
this.
|
|
401
|
-
this.
|
|
402
|
-
|
|
403
|
-
);
|
|
411
|
+
if (this.RelativeHumidityHumidifierThreshold === undefined) {
|
|
412
|
+
this.platform.debug(`Humidifier ${this.accessory.displayName}`
|
|
413
|
+
+ ` RelativeHumidityHumidifierThreshold: ${this.RelativeHumidityHumidifierThreshold}`);
|
|
414
|
+
} else {
|
|
415
|
+
this.service.updateCharacteristic(this.platform.Characteristic.RelativeHumidityHumidifierThreshold, this.RelativeHumidityHumidifierThreshold);
|
|
416
|
+
this.platform.device(`Humidifier ${this.accessory.displayName}`
|
|
417
|
+
+ ` updateCharacteristic RelativeHumidityHumidifierThreshold: ${this.RelativeHumidityHumidifierThreshold}`);
|
|
404
418
|
}
|
|
405
|
-
if (!this.
|
|
406
|
-
this.
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
);
|
|
419
|
+
if (!this.device.humidifier?.hide_temperature || this.CurrentTemperature === undefined) {
|
|
420
|
+
this.platform.debug(`Humidifier ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`);
|
|
421
|
+
} else {
|
|
422
|
+
this.temperatureservice!.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.CurrentTemperature);
|
|
423
|
+
this.platform.device(`Humidifier ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`);
|
|
410
424
|
}
|
|
411
425
|
}
|
|
412
426
|
|
|
@@ -417,12 +431,12 @@ export class Humidifier {
|
|
|
417
431
|
this.service.updateCharacteristic(this.platform.Characteristic.TargetHumidifierDehumidifierState, e);
|
|
418
432
|
this.service.updateCharacteristic(this.platform.Characteristic.Active, e);
|
|
419
433
|
this.service.updateCharacteristic(this.platform.Characteristic.RelativeHumidityHumidifierThreshold, e);
|
|
420
|
-
if (!this.
|
|
434
|
+
if (!this.device.humidifier?.hide_temperature) {
|
|
421
435
|
this.temperatureservice!.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, e);
|
|
422
436
|
}
|
|
423
437
|
}
|
|
424
438
|
|
|
425
|
-
private statusCode(push:
|
|
439
|
+
private statusCode(push: { data: { statusCode: any; }; }) {
|
|
426
440
|
switch (push.data.statusCode) {
|
|
427
441
|
case 151:
|
|
428
442
|
this.platform.log.error('Command not supported by this device type.');
|
|
@@ -450,62 +464,6 @@ export class Humidifier {
|
|
|
450
464
|
}
|
|
451
465
|
}
|
|
452
466
|
|
|
453
|
-
private statusCodeAuto(pushAuto: AxiosResponse<any>) {
|
|
454
|
-
switch (pushAuto.data.statusCode) {
|
|
455
|
-
case 151:
|
|
456
|
-
this.platform.log.error('Command not supported by this device type.');
|
|
457
|
-
break;
|
|
458
|
-
case 152:
|
|
459
|
-
this.platform.log.error('Device not found.');
|
|
460
|
-
break;
|
|
461
|
-
case 160:
|
|
462
|
-
this.platform.log.error('Command is not supported.');
|
|
463
|
-
break;
|
|
464
|
-
case 161:
|
|
465
|
-
this.platform.log.error('Device is offline.');
|
|
466
|
-
break;
|
|
467
|
-
case 171:
|
|
468
|
-
this.platform.log.error('Hub Device is offline.');
|
|
469
|
-
break;
|
|
470
|
-
case 190:
|
|
471
|
-
this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.');
|
|
472
|
-
break;
|
|
473
|
-
case 100:
|
|
474
|
-
this.platform.debug('Command successfully sent.');
|
|
475
|
-
break;
|
|
476
|
-
default:
|
|
477
|
-
this.platform.debug('Unknown statusCode.');
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
private statusCodeActive(pushActive: AxiosResponse<any>) {
|
|
482
|
-
switch (pushActive.data.statusCode) {
|
|
483
|
-
case 151:
|
|
484
|
-
this.platform.log.error('Command not supported by this device type.');
|
|
485
|
-
break;
|
|
486
|
-
case 152:
|
|
487
|
-
this.platform.log.error('Device not found.');
|
|
488
|
-
break;
|
|
489
|
-
case 160:
|
|
490
|
-
this.platform.log.error('Command is not supported.');
|
|
491
|
-
break;
|
|
492
|
-
case 161:
|
|
493
|
-
this.platform.log.error('Device is offline.');
|
|
494
|
-
break;
|
|
495
|
-
case 171:
|
|
496
|
-
this.platform.log.error('Hub Device is offline.');
|
|
497
|
-
break;
|
|
498
|
-
case 190:
|
|
499
|
-
this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.');
|
|
500
|
-
break;
|
|
501
|
-
case 100:
|
|
502
|
-
this.platform.debug('Command successfully sent.');
|
|
503
|
-
break;
|
|
504
|
-
default:
|
|
505
|
-
this.platform.debug('Unknown statusCode.');
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
467
|
/**
|
|
510
468
|
* Handle requests to set the "Target Humidifier Dehumidifier State" characteristic
|
|
511
469
|
*/
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { Service, PlatformAccessory, CharacteristicValue, MacAddress } from 'homebridge';
|
|
2
|
+
import { SwitchBotPlatform } from '../platform';
|
|
3
|
+
import { interval, Subject } from 'rxjs';
|
|
4
|
+
import { debounceTime, skipWhile, tap } from 'rxjs/operators';
|
|
5
|
+
import { DeviceURL, device, devicesConfig } from '../settings';
|
|
6
|
+
|
|
7
|
+
export class IndoorCam {
|
|
8
|
+
// Services
|
|
9
|
+
private service: Service;
|
|
10
|
+
|
|
11
|
+
// Characteristic Values
|
|
12
|
+
On!: CharacteristicValue;
|
|
13
|
+
OutletInUse!: CharacteristicValue;
|
|
14
|
+
|
|
15
|
+
// Others
|
|
16
|
+
deviceStatus!: any;
|
|
17
|
+
switchbot!: {
|
|
18
|
+
discover: (
|
|
19
|
+
arg0:
|
|
20
|
+
{
|
|
21
|
+
duration: any;
|
|
22
|
+
model: string;
|
|
23
|
+
quick: boolean;
|
|
24
|
+
id: MacAddress;
|
|
25
|
+
}
|
|
26
|
+
) => Promise<any>;
|
|
27
|
+
wait: (
|
|
28
|
+
arg0: number
|
|
29
|
+
) => any;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Updates
|
|
33
|
+
cameraUpdateInProgress!: boolean;
|
|
34
|
+
doCameraUpdate;
|
|
35
|
+
|
|
36
|
+
constructor(
|
|
37
|
+
private readonly platform: SwitchBotPlatform,
|
|
38
|
+
private accessory: PlatformAccessory,
|
|
39
|
+
public device: device & devicesConfig,
|
|
40
|
+
) {
|
|
41
|
+
// default placeholders
|
|
42
|
+
this.On = false;
|
|
43
|
+
this.OutletInUse = true;
|
|
44
|
+
|
|
45
|
+
// BLE Connections
|
|
46
|
+
if (device.ble) {
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
48
|
+
const SwitchBot = require('node-switchbot');
|
|
49
|
+
this.switchbot = new SwitchBot();
|
|
50
|
+
const colon = device.deviceId!.match(/.{1,2}/g);
|
|
51
|
+
const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
|
|
52
|
+
this.device.bleMac = bleMac.toLowerCase();
|
|
53
|
+
this.platform.device(this.device.bleMac.toLowerCase());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// this is subject we use to track when we need to POST changes to the SwitchBot API
|
|
57
|
+
this.doCameraUpdate = new Subject();
|
|
58
|
+
this.cameraUpdateInProgress = false;
|
|
59
|
+
|
|
60
|
+
// Retrieve initial values and updateHomekit
|
|
61
|
+
this.refreshStatus();
|
|
62
|
+
|
|
63
|
+
// set accessory information
|
|
64
|
+
accessory
|
|
65
|
+
.getService(this.platform.Service.AccessoryInformation)!
|
|
66
|
+
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
|
|
67
|
+
.setCharacteristic(this.platform.Characteristic.Model, 'SWITCHBOT-CAMERA-')
|
|
68
|
+
.setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!);
|
|
69
|
+
|
|
70
|
+
// get the WindowCovering service if it exists, otherwise create a new WindowCovering service
|
|
71
|
+
// you can create multiple services for each accessory
|
|
72
|
+
(this.service =
|
|
73
|
+
accessory.getService(this.platform.Service.Outlet) ||
|
|
74
|
+
accessory.addService(this.platform.Service.Outlet)), `${accessory.displayName} Indoor Camera`;
|
|
75
|
+
|
|
76
|
+
// To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
|
|
77
|
+
// when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
|
|
78
|
+
// accessory.getService('NAME') ?? accessory.addService(this.platform.Service.Lightbulb, 'NAME', 'USER_DEFINED_SUBTYPE');
|
|
79
|
+
|
|
80
|
+
// set the service name, this is what is displayed as the default name on the Home app
|
|
81
|
+
// in this example we are using the name we stored in the `accessory.context` in the `discoverDevices` method.
|
|
82
|
+
this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName);
|
|
83
|
+
|
|
84
|
+
// each service must implement at-minimum the "required characteristics" for the given service type
|
|
85
|
+
// see https://developers.homebridge.io/#/service/WindowCovering
|
|
86
|
+
|
|
87
|
+
// create handlers for required characteristics
|
|
88
|
+
this.service.getCharacteristic(this.platform.Characteristic.On).onSet(this.OnSet.bind(this));
|
|
89
|
+
|
|
90
|
+
this.service.setCharacteristic(this.platform.Characteristic.OutletInUse, this.OutletInUse || true);
|
|
91
|
+
|
|
92
|
+
// Update Homekit
|
|
93
|
+
this.updateHomeKitCharacteristics();
|
|
94
|
+
|
|
95
|
+
// Start an update interval
|
|
96
|
+
interval(this.platform.config.options!.refreshRate! * 1000)
|
|
97
|
+
.pipe(skipWhile(() => this.cameraUpdateInProgress))
|
|
98
|
+
.subscribe(() => {
|
|
99
|
+
this.refreshStatus();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Watch for Camera change events
|
|
103
|
+
// We put in a debounce of 100ms so we don't make duplicate calls
|
|
104
|
+
this.doCameraUpdate
|
|
105
|
+
.pipe(
|
|
106
|
+
tap(() => {
|
|
107
|
+
this.cameraUpdateInProgress = true;
|
|
108
|
+
}),
|
|
109
|
+
debounceTime(this.platform.config.options!.pushRate! * 1000),
|
|
110
|
+
)
|
|
111
|
+
.subscribe(async () => {
|
|
112
|
+
try {
|
|
113
|
+
await this.pushChanges();
|
|
114
|
+
} catch (e: any) {
|
|
115
|
+
this.platform.log.error(JSON.stringify(e.message));
|
|
116
|
+
this.platform.debug(`Camera ${accessory.displayName} - ${JSON.stringify(e)}`);
|
|
117
|
+
this.apiError(e);
|
|
118
|
+
}
|
|
119
|
+
this.cameraUpdateInProgress = false;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
parseStatus() {
|
|
124
|
+
switch (this.deviceStatus.body.power) {
|
|
125
|
+
case 'on':
|
|
126
|
+
this.On = true;
|
|
127
|
+
break;
|
|
128
|
+
default:
|
|
129
|
+
this.On = false;
|
|
130
|
+
}
|
|
131
|
+
this.platform.debug(`Camera ${this.accessory.displayName} On: ${this.On}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async refreshStatus() {
|
|
135
|
+
if (this.device.ble) {
|
|
136
|
+
this.platform.device('BLE');
|
|
137
|
+
await this.BLErefreshStatus();
|
|
138
|
+
} else {
|
|
139
|
+
this.platform.device('OpenAPI');
|
|
140
|
+
await this.openAPIRefreshStatus();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private async BLErefreshStatus() {
|
|
145
|
+
this.platform.debug('IndoorCam BLE Device RefreshStatus');
|
|
146
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
147
|
+
const Switchbot = require('node-switchbot');
|
|
148
|
+
const switchbot = new Switchbot();
|
|
149
|
+
const colon = this.device.deviceId!.match(/.{1,2}/g);
|
|
150
|
+
const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
|
|
151
|
+
this.device.bleMac = bleMac.toLowerCase();
|
|
152
|
+
this.platform.device(this.device.bleMac!);
|
|
153
|
+
switchbot.onadvertisement = (ad: any) => {
|
|
154
|
+
this.platform.debug(JSON.stringify(ad, null, ' '));
|
|
155
|
+
this.platform.device(`ad: ${JSON.stringify(ad)}`);
|
|
156
|
+
};
|
|
157
|
+
switchbot
|
|
158
|
+
.startScan({
|
|
159
|
+
id: this.device.bleMac,
|
|
160
|
+
})
|
|
161
|
+
.then(() => {
|
|
162
|
+
return switchbot.wait(this.platform.config.options!.refreshRate! * 1000);
|
|
163
|
+
})
|
|
164
|
+
.then(() => {
|
|
165
|
+
switchbot.stopScan();
|
|
166
|
+
})
|
|
167
|
+
.catch(async (error: any) => {
|
|
168
|
+
this.platform.log.error(error);
|
|
169
|
+
await this.openAPIRefreshStatus();
|
|
170
|
+
});
|
|
171
|
+
setInterval(() => {
|
|
172
|
+
this.platform.log.info('Start scan ' + this.device.deviceName + '(' + this.device.bleMac + ')');
|
|
173
|
+
switchbot
|
|
174
|
+
.startScan({
|
|
175
|
+
mode: 'T',
|
|
176
|
+
id: bleMac,
|
|
177
|
+
})
|
|
178
|
+
.then(() => {
|
|
179
|
+
return switchbot.wait(this.platform.config.options!.refreshRate! * 1000);
|
|
180
|
+
})
|
|
181
|
+
.then(() => {
|
|
182
|
+
switchbot.stopScan();
|
|
183
|
+
this.platform.log.info('Stop scan ' + this.device.deviceName + '(' + this.device.bleMac + ')');
|
|
184
|
+
})
|
|
185
|
+
.catch(async (error: any) => {
|
|
186
|
+
this.platform.log.error(error);
|
|
187
|
+
await this.openAPIRefreshStatus();
|
|
188
|
+
});
|
|
189
|
+
}, this.platform.config.options!.refreshRate! * 60000);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private async openAPIRefreshStatus() {
|
|
193
|
+
try {
|
|
194
|
+
this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
|
|
195
|
+
this.platform.device(`Camera ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
|
|
196
|
+
this.parseStatus();
|
|
197
|
+
this.updateHomeKitCharacteristics();
|
|
198
|
+
} catch (e: any) {
|
|
199
|
+
this.platform.log.error(`IndoorCam ${this.accessory.displayName} failed to refresh status, Error Message: ${JSON.stringify(e.message)}`);
|
|
200
|
+
this.platform.debug(`IndoorCam ${this.accessory.displayName}, Error: ${JSON.stringify(e)}`);
|
|
201
|
+
this.apiError(e);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Pushes the requested changes to the SwitchBot API
|
|
207
|
+
* deviceType commandType Command command parameter Description
|
|
208
|
+
* Camera - "command" "turnOff" "default" = set to OFF state
|
|
209
|
+
* Camera - "command" "turnOn" "default" = set to ON state
|
|
210
|
+
*/
|
|
211
|
+
async pushChanges() {
|
|
212
|
+
const payload = {
|
|
213
|
+
commandType: 'command',
|
|
214
|
+
parameter: 'default',
|
|
215
|
+
} as any;
|
|
216
|
+
|
|
217
|
+
if (this.On) {
|
|
218
|
+
payload.command = 'turnOn';
|
|
219
|
+
} else {
|
|
220
|
+
payload.command = 'turnOff';
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
this.platform.log.info(
|
|
224
|
+
'Sending request for',
|
|
225
|
+
this.accessory.displayName,
|
|
226
|
+
'to SwitchBot API. command:',
|
|
227
|
+
payload.command,
|
|
228
|
+
'parameter:',
|
|
229
|
+
payload.parameter,
|
|
230
|
+
'commandType:',
|
|
231
|
+
payload.commandType,
|
|
232
|
+
);
|
|
233
|
+
this.platform.debug(`Camera ${this.accessory.displayName} pushchanges: ${JSON.stringify(payload)}`);
|
|
234
|
+
|
|
235
|
+
// Make the API request
|
|
236
|
+
const push: any = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload);
|
|
237
|
+
this.platform.debug(`Camera ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`);
|
|
238
|
+
this.statusCode(push);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
updateHomeKitCharacteristics() {
|
|
242
|
+
if (this.On === undefined) {
|
|
243
|
+
this.platform.debug(`Camera ${this.accessory.displayName} On: ${this.On}`);
|
|
244
|
+
} else {
|
|
245
|
+
this.service.updateCharacteristic(this.platform.Characteristic.On, this.On);
|
|
246
|
+
this.platform.device(`Camera ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
|
|
247
|
+
}
|
|
248
|
+
if (this.OutletInUse === undefined) {
|
|
249
|
+
this.platform.debug(`Camera ${this.accessory.displayName} OutletInUse: ${this.OutletInUse}`);
|
|
250
|
+
} else {
|
|
251
|
+
this.service.updateCharacteristic(this.platform.Characteristic.OutletInUse, this.OutletInUse);
|
|
252
|
+
this.platform.device(`Camera ${this.accessory.displayName} updateCharacteristic OutletInUse: ${this.OutletInUse}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
public apiError(e: any) {
|
|
257
|
+
this.service.updateCharacteristic(this.platform.Characteristic.On, e);
|
|
258
|
+
this.service.updateCharacteristic(this.platform.Characteristic.OutletInUse, e);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
private statusCode(push: { data: { statusCode: any; }; }) {
|
|
263
|
+
switch (push.data.statusCode) {
|
|
264
|
+
case 151:
|
|
265
|
+
this.platform.log.error('Command not supported by this device type.');
|
|
266
|
+
break;
|
|
267
|
+
case 152:
|
|
268
|
+
this.platform.log.error('Device not found.');
|
|
269
|
+
break;
|
|
270
|
+
case 160:
|
|
271
|
+
this.platform.log.error('Command is not supported.');
|
|
272
|
+
break;
|
|
273
|
+
case 161:
|
|
274
|
+
this.platform.log.error('Device is offline.');
|
|
275
|
+
break;
|
|
276
|
+
case 171:
|
|
277
|
+
this.platform.log.error('Hub Device is offline.');
|
|
278
|
+
break;
|
|
279
|
+
case 190:
|
|
280
|
+
this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.');
|
|
281
|
+
break;
|
|
282
|
+
case 100:
|
|
283
|
+
if (this.platform.config.options?.debug) {
|
|
284
|
+
this.platform.log.info('Command successfully sent.');
|
|
285
|
+
}
|
|
286
|
+
break;
|
|
287
|
+
default:
|
|
288
|
+
this.platform.debug('Unknown statusCode.');
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Handle requests to set the value of the "On" characteristic
|
|
294
|
+
*/
|
|
295
|
+
OnSet(value: CharacteristicValue) {
|
|
296
|
+
this.platform.debug(`Camera ${this.accessory.displayName} - Set On: ${value}`);
|
|
297
|
+
|
|
298
|
+
this.On = value;
|
|
299
|
+
this.doCameraUpdate.next();
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
}
|