@switchbot/homebridge-switchbot 0.2.0-alpha.1 → 1.0.1-beta.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/workflows/nodejs-beta.yml +1 -1
- package/.github/workflows/nodejs.yml +1 -1
- package/.github/workflows/{prerelease.js → prerelease-beta.js} +0 -0
- package/CHANGELOG.md +1 -1
- package/README.md +9 -5
- package/config.schema.json +350 -126
- package/dist/devices/bots.d.ts +17 -33
- package/dist/devices/bots.d.ts.map +1 -1
- package/dist/devices/bots.js +103 -152
- package/dist/devices/bots.js.map +1 -1
- package/dist/devices/{bulb.d.ts → colorbulb.d.ts} +12 -19
- package/dist/devices/colorbulb.d.ts.map +1 -0
- package/dist/devices/{bulb.js → colorbulb.js} +32 -32
- package/dist/devices/colorbulb.js.map +1 -0
- package/dist/devices/contact.d.ts +18 -16
- package/dist/devices/contact.d.ts.map +1 -1
- package/dist/devices/contact.js +101 -44
- package/dist/devices/contact.js.map +1 -1
- package/dist/devices/curtains.d.ts +17 -51
- package/dist/devices/curtains.d.ts.map +1 -1
- package/dist/devices/curtains.js +168 -248
- package/dist/devices/curtains.js.map +1 -1
- package/dist/devices/humidifiers.d.ts +13 -4
- package/dist/devices/humidifiers.d.ts.map +1 -1
- package/dist/devices/humidifiers.js +78 -8
- package/dist/devices/humidifiers.js.map +1 -1
- package/dist/devices/indoorcam.d.ts +6 -13
- package/dist/devices/indoorcam.d.ts.map +1 -1
- package/dist/devices/indoorcam.js.map +1 -1
- package/dist/devices/meters.d.ts +12 -15
- package/dist/devices/meters.d.ts.map +1 -1
- package/dist/devices/meters.js +35 -44
- package/dist/devices/meters.js.map +1 -1
- package/dist/devices/motion.d.ts +17 -15
- package/dist/devices/motion.d.ts.map +1 -1
- package/dist/devices/motion.js +87 -40
- package/dist/devices/motion.js.map +1 -1
- package/dist/devices/plugs.d.ts +6 -13
- package/dist/devices/plugs.d.ts.map +1 -1
- package/dist/devices/plugs.js.map +1 -1
- package/dist/irdevices/airconditioners.d.ts +10 -10
- package/dist/irdevices/airconditioners.d.ts.map +1 -1
- package/dist/irdevices/airconditioners.js +29 -9
- package/dist/irdevices/airconditioners.js.map +1 -1
- package/dist/irdevices/airpurifiers.d.ts +11 -11
- package/dist/irdevices/airpurifiers.d.ts.map +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.map +1 -1
- package/dist/irdevices/fans.d.ts +6 -6
- package/dist/irdevices/fans.d.ts.map +1 -1
- package/dist/irdevices/fans.js +10 -10
- 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.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.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 +7 -5
- 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.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.map +1 -1
- package/dist/platform.d.ts +3 -2
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +136 -46
- package/dist/platform.js.map +1 -1
- package/dist/settings.d.ts +50 -8
- package/dist/settings.d.ts.map +1 -1
- package/package.json +65 -65
- package/src/devices/bots.ts +126 -176
- package/src/devices/{bulb.ts → colorbulb.ts} +43 -51
- package/src/devices/contact.ts +124 -68
- package/src/devices/curtains.ts +189 -300
- package/src/devices/humidifiers.ts +93 -14
- package/src/devices/indoorcam.ts +10 -20
- package/src/devices/meters.ts +53 -69
- package/src/devices/motion.ts +108 -62
- package/src/devices/plugs.ts +8 -19
- package/src/irdevices/airconditioners.ts +43 -20
- package/src/irdevices/airpurifiers.ts +15 -11
- package/src/irdevices/cameras.ts +5 -3
- package/src/irdevices/fans.ts +22 -16
- package/src/irdevices/lights.ts +5 -3
- package/src/irdevices/others.ts +5 -3
- package/src/irdevices/tvs.ts +13 -7
- package/src/irdevices/vacuumcleaners.ts +5 -3
- package/src/irdevices/waterheaters.ts +5 -3
- package/src/platform.ts +148 -49
- package/src/settings.ts +79 -8
- package/dist/devices/bulb.d.ts.map +0 -1
- package/dist/devices/bulb.js.map +0 -1
package/src/devices/bots.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Service, PlatformAccessory, CharacteristicValue
|
|
1
|
+
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, devicesConfig } from '../settings';
|
|
5
|
+
import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse } from '../settings';
|
|
6
|
+
import { AxiosResponse } from 'axios';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Platform Accessory
|
|
@@ -10,31 +11,29 @@ import { DeviceURL, device, devicesConfig } from '../settings';
|
|
|
10
11
|
* Each accessory may expose multiple services of different service types.
|
|
11
12
|
*/
|
|
12
13
|
export class Bot {
|
|
14
|
+
// Services
|
|
13
15
|
private service: Service;
|
|
16
|
+
private batteryService?: Service;
|
|
14
17
|
|
|
18
|
+
// Characteristic Values
|
|
15
19
|
SwitchOn!: CharacteristicValue;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
switchbot!: {
|
|
22
|
-
discover: (
|
|
23
|
-
arg0:
|
|
24
|
-
{
|
|
25
|
-
duration: any;
|
|
26
|
-
model: string;
|
|
27
|
-
quick: boolean;
|
|
28
|
-
id: MacAddress;
|
|
29
|
-
}
|
|
30
|
-
) => Promise<any>;
|
|
31
|
-
wait: (
|
|
32
|
-
arg0: number
|
|
33
|
-
) => any;
|
|
34
|
-
};
|
|
20
|
+
BatteryLevel!: CharacteristicValue;
|
|
21
|
+
StatusLowBattery!: CharacteristicValue;
|
|
22
|
+
|
|
23
|
+
// OpenAPI Others
|
|
24
|
+
deviceStatus!: deviceStatusResponse;
|
|
35
25
|
|
|
26
|
+
// BLE Others
|
|
27
|
+
switchbot!: switchbot;
|
|
28
|
+
TargetState!: boolean;
|
|
29
|
+
serviceData!: serviceData;
|
|
30
|
+
Mode!: serviceData['mode'];
|
|
31
|
+
state!: serviceData['state'];
|
|
32
|
+
battery!: serviceData['battery'];
|
|
33
|
+
|
|
34
|
+
// Updates
|
|
36
35
|
botUpdateInProgress!: boolean;
|
|
37
|
-
doBotUpdate
|
|
36
|
+
doBotUpdate!: Subject<void>;
|
|
38
37
|
|
|
39
38
|
constructor(
|
|
40
39
|
private readonly platform: SwitchBotPlatform,
|
|
@@ -43,27 +42,13 @@ export class Bot {
|
|
|
43
42
|
) {
|
|
44
43
|
// default placeholders
|
|
45
44
|
this.SwitchOn = false;
|
|
46
|
-
this.ScanDuration = this.platform.config.options!.refreshRate!;
|
|
47
|
-
if (device.bot?.mode !== 'switch') {
|
|
48
|
-
this.OutletInUse = true;
|
|
49
|
-
}
|
|
50
|
-
if (device.ble) {
|
|
51
|
-
this.SwitchOn = false;
|
|
52
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
53
|
-
const SwitchBot = require('node-switchbot');
|
|
54
|
-
this.switchbot = new SwitchBot();
|
|
55
|
-
const colon = device.deviceId!.match(/.{1,2}/g);
|
|
56
|
-
const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
|
|
57
|
-
this.device.bleMac = bleMac.toLowerCase();
|
|
58
|
-
this.platform.device(this.device.bleMac);
|
|
59
|
-
}
|
|
60
45
|
|
|
61
46
|
// this is subject we use to track when we need to POST changes to the SwitchBot API
|
|
62
47
|
this.doBotUpdate = new Subject();
|
|
63
48
|
this.botUpdateInProgress = false;
|
|
64
49
|
|
|
65
50
|
// Retrieve initial values and updateHomekit
|
|
66
|
-
this.
|
|
51
|
+
this.refreshStatus();
|
|
67
52
|
|
|
68
53
|
// set accessory information
|
|
69
54
|
accessory
|
|
@@ -84,6 +69,14 @@ export class Bot {
|
|
|
84
69
|
accessory.addService(this.platform.Service.Outlet)), `${accessory.displayName} Outlet`;
|
|
85
70
|
}
|
|
86
71
|
|
|
72
|
+
if (device.ble) {
|
|
73
|
+
(this.batteryService =
|
|
74
|
+
accessory.getService(this.platform.Service.Battery) ||
|
|
75
|
+
accessory.addService(this.platform.Service.Battery)), `${accessory.displayName} Battery`;
|
|
76
|
+
|
|
77
|
+
this.batteryService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Battery`);
|
|
78
|
+
}
|
|
79
|
+
|
|
87
80
|
// To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
|
|
88
81
|
// when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
|
|
89
82
|
// accessory.getService('NAME') ?? accessory.addService(this.platform.Service.Outlet, 'NAME', 'USER_DEFINED_SUBTYPE');
|
|
@@ -142,18 +135,24 @@ export class Bot {
|
|
|
142
135
|
}
|
|
143
136
|
|
|
144
137
|
private async BLEparseStatus() {
|
|
145
|
-
this.platform.debug('Bots BLE Device
|
|
138
|
+
this.platform.debug('Bots BLE Device parseStatus');
|
|
139
|
+
// BLEmode (true if Switch Mode) | (false if Press Mode)
|
|
140
|
+
if (this.Mode) {
|
|
141
|
+
this.platform.device(`Switch Mode, Mode: ${JSON.stringify(this.Mode)}`);
|
|
142
|
+
}
|
|
143
|
+
this.SwitchOn = Boolean(this.state);
|
|
144
|
+
this.BatteryLevel = Number(this.battery);
|
|
145
|
+
if (this.BatteryLevel < 10) {
|
|
146
|
+
this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
|
|
147
|
+
} else {
|
|
148
|
+
this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
|
|
149
|
+
}
|
|
150
|
+
this.platform.debug(`Bot ${this.accessory.displayName} On: ${this.SwitchOn}, BatteryLevel: ${this.BatteryLevel}`);
|
|
146
151
|
}
|
|
147
152
|
|
|
148
153
|
private async openAPIparseStatus() {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
this.SwitchOn = false;
|
|
152
|
-
this.platform.debug(`Bot ${this.accessory.displayName} OutletInUse: ${this.OutletInUse} On: ${this.SwitchOn}`);
|
|
153
|
-
} else {
|
|
154
|
-
this.SwitchOn = false;
|
|
155
|
-
this.platform.debug(`Bot ${this.accessory.displayName} On: ${this.SwitchOn}`);
|
|
156
|
-
}
|
|
154
|
+
this.SwitchOn = false;
|
|
155
|
+
this.platform.debug(`Bot ${this.accessory.displayName} On: ${this.SwitchOn}`);
|
|
157
156
|
}
|
|
158
157
|
|
|
159
158
|
/**
|
|
@@ -162,15 +161,14 @@ export class Bot {
|
|
|
162
161
|
async refreshStatus() {
|
|
163
162
|
if (this.device.ble) {
|
|
164
163
|
this.platform.device('BLE');
|
|
165
|
-
await this.
|
|
164
|
+
await this.BLERefreshStatus();
|
|
166
165
|
} else {
|
|
167
166
|
this.platform.device('OpenAPI');
|
|
168
167
|
await this.openAPIRefreshStatus();
|
|
169
168
|
}
|
|
170
169
|
}
|
|
171
170
|
|
|
172
|
-
private
|
|
173
|
-
this.platform.device('Bot BLE Device refreshStatus');
|
|
171
|
+
private connectBLE() {
|
|
174
172
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
175
173
|
const Switchbot = require('node-switchbot');
|
|
176
174
|
const switchbot = new Switchbot();
|
|
@@ -178,45 +176,40 @@ export class Bot {
|
|
|
178
176
|
const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
|
|
179
177
|
this.device.bleMac = bleMac.toLowerCase();
|
|
180
178
|
this.platform.device(this.device.bleMac!);
|
|
181
|
-
switchbot
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
this.
|
|
197
|
-
this.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
})
|
|
209
|
-
.then(() => {
|
|
210
|
-
switchbot.stopScan();
|
|
211
|
-
this.platform.log.info('Stop scan ' + this.device.deviceName + '(' + this.device.bleMac + ')');
|
|
212
|
-
})
|
|
213
|
-
.catch(async (error: any) => {
|
|
214
|
-
this.platform.log.error(error);
|
|
215
|
-
this.openAPIRefreshStatus();
|
|
216
|
-
});
|
|
179
|
+
return switchbot;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private async BLERefreshStatus() {
|
|
183
|
+
this.platform.device('Bot BLE Device refreshStatus');
|
|
184
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
185
|
+
const switchbot = this.connectBLE();
|
|
186
|
+
// Start to monitor advertisement packets
|
|
187
|
+
switchbot.startScan({
|
|
188
|
+
model: 'H',
|
|
189
|
+
id: this.device.bleMac,
|
|
190
|
+
}).then(() => {
|
|
191
|
+
// Set an event hander
|
|
192
|
+
switchbot.onadvertisement = (ad: ad) => {
|
|
193
|
+
this.serviceData = ad.serviceData;
|
|
194
|
+
this.Mode = ad.serviceData.mode;
|
|
195
|
+
this.state = ad.serviceData.state;
|
|
196
|
+
this.battery = ad.serviceData.battery;
|
|
197
|
+
this.platform.device(`${this.device.bleMac}: ${JSON.stringify(ad.serviceData)}`);
|
|
198
|
+
this.platform.device(`${this.accessory.displayName}, Model: ${ad.serviceData.model}, Model Name: ${ad.serviceData.modelName},`
|
|
199
|
+
+ ` Mode: ${ad.serviceData.mode}, State: ${ad.serviceData.state}, Battery: ${ad.serviceData.battery}`);
|
|
200
|
+
};
|
|
201
|
+
// Wait 10 seconds
|
|
202
|
+
return switchbot.wait(10000);
|
|
203
|
+
}).then(() => {
|
|
204
|
+
// Stop to monitor
|
|
205
|
+
switchbot.stopScan();
|
|
217
206
|
this.parseStatus();
|
|
218
207
|
this.updateHomeKitCharacteristics();
|
|
219
|
-
}
|
|
208
|
+
}).catch(async (e: any) => {
|
|
209
|
+
this.platform.log.error(`BLE Connection Failed: ${e.message}`);
|
|
210
|
+
this.platform.log.warn('Using OpenAPI Connection');
|
|
211
|
+
await this.openAPIRefreshStatus();
|
|
212
|
+
});
|
|
220
213
|
}
|
|
221
214
|
|
|
222
215
|
private async openAPIRefreshStatus() {
|
|
@@ -260,53 +253,38 @@ export class Bot {
|
|
|
260
253
|
|
|
261
254
|
private async BLEpushChanges() {
|
|
262
255
|
this.platform.device('Bot BLE Device pushChanges');
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
.discover({
|
|
267
|
-
|
|
268
|
-
this.
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
this.platform.log.info(targetDevice.modelName + ' (' + targetDevice.address + ') was found.');
|
|
284
|
-
// Set event handers
|
|
285
|
-
targetDevice.onconnect = () => {
|
|
286
|
-
// log.info('Connected.');
|
|
287
|
-
};
|
|
288
|
-
targetDevice.ondisconnect = () => {
|
|
289
|
-
// log.info('Disconnected.');
|
|
290
|
-
};
|
|
291
|
-
this.platform.log.info('Bot is running...');
|
|
292
|
-
return this.setTargetDeviceState(targetDevice, this.TargetState);
|
|
293
|
-
}
|
|
294
|
-
})
|
|
295
|
-
.then(() => {
|
|
296
|
-
this.platform.log.info('Done.');
|
|
297
|
-
this.SwitchOn = this.TargetState;
|
|
298
|
-
this.RunTimer = setTimeout(() => {
|
|
299
|
-
this.service?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.SwitchOn);
|
|
300
|
-
}, 500);
|
|
301
|
-
this.platform.log.info('Bot state has been set to: ' + (this.SwitchOn ? 'ON' : 'OFF'));
|
|
302
|
-
})
|
|
303
|
-
.catch((error: any) => {
|
|
304
|
-
this.platform.log.error(error);
|
|
305
|
-
this.RunTimer = setTimeout(() => {
|
|
306
|
-
this.service?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.SwitchOn);
|
|
307
|
-
}, 500);
|
|
308
|
-
this.platform.log.info('Bot state failed to be set to: ' + (this.TargetState ? 'ON' : 'OFF'));
|
|
256
|
+
const switchbot = this.connectBLE();
|
|
257
|
+
if (this.device.bot?.mode === 'press') {
|
|
258
|
+
this.platform.device(`Press Mode: ${this.device.bot?.mode}`);
|
|
259
|
+
switchbot.discover({ model: 'H', quick: true, id: this.device.bleMac }).then((device_list) => {
|
|
260
|
+
this.platform.log.info(`${this.accessory.displayName}, On: ${this.SwitchOn}`);
|
|
261
|
+
return device_list[0].press({ id: this.device.bleMac });
|
|
262
|
+
}).then(() => {
|
|
263
|
+
this.platform.device('Done.');
|
|
264
|
+
}).catch((e: any) => {
|
|
265
|
+
this.platform.log.error(`BLE pushChanges Error Message: ${e.message}`);
|
|
266
|
+
});
|
|
267
|
+
} else if (this.device.bot?.mode === 'switch') {
|
|
268
|
+
this.platform.device(`Press Mode: ${this.device.bot?.mode}`);
|
|
269
|
+
switchbot.discover({ model: 'H', quick: true, id: this.device.bleMac }).then((device_list) => {
|
|
270
|
+
this.platform.log.info(`${this.accessory.displayName}, On: ${this.SwitchOn}`);
|
|
271
|
+
return this.turnOnOff(device_list);
|
|
272
|
+
}).then(() => {
|
|
273
|
+
this.platform.device('Done.');
|
|
274
|
+
}).catch((e: any) => {
|
|
275
|
+
this.platform.log.error(`BLE pushChanges Error Message: ${e.message}`);
|
|
309
276
|
});
|
|
277
|
+
} else {
|
|
278
|
+
this.platform.log.error('Mode Not Set.');
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
private turnOnOff(device_list: any) {
|
|
283
|
+
if (this.SwitchOn) {
|
|
284
|
+
return device_list[0].turnOn({ id: this.device.bleMac });
|
|
285
|
+
} else {
|
|
286
|
+
return device_list[0].turnOff({ id: this.device.bleMac });
|
|
287
|
+
}
|
|
310
288
|
}
|
|
311
289
|
|
|
312
290
|
private async openAPIpushChanges() {
|
|
@@ -359,23 +337,27 @@ export class Bot {
|
|
|
359
337
|
this.service.updateCharacteristic(this.platform.Characteristic.On, this.SwitchOn);
|
|
360
338
|
this.platform.device(`Bot ${this.accessory.displayName} updateCharacteristic On: ${this.SwitchOn}`);
|
|
361
339
|
}
|
|
362
|
-
if (this.
|
|
363
|
-
this.platform.debug(`Bot ${this.accessory.displayName}
|
|
340
|
+
if (this.BatteryLevel === undefined) {
|
|
341
|
+
this.platform.debug(`Bot ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
|
|
364
342
|
} else {
|
|
365
|
-
this.
|
|
366
|
-
this.platform.device(`Bot ${this.accessory.displayName} updateCharacteristic
|
|
367
|
-
|
|
343
|
+
this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
|
|
344
|
+
this.platform.device(`Bot ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
|
|
345
|
+
}
|
|
346
|
+
if (this.StatusLowBattery === undefined) {
|
|
347
|
+
this.platform.debug(`Bot ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
|
|
348
|
+
} else {
|
|
349
|
+
this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
|
|
350
|
+
this.platform.device(`Bot ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
|
|
368
351
|
}
|
|
369
352
|
}
|
|
370
353
|
|
|
371
354
|
public apiError(e: any) {
|
|
372
355
|
this.service.updateCharacteristic(this.platform.Characteristic.On, e);
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
}
|
|
356
|
+
this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, e);
|
|
357
|
+
this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, e);
|
|
376
358
|
}
|
|
377
359
|
|
|
378
|
-
private statusCode(push: {
|
|
360
|
+
private statusCode(push: AxiosResponse<{ statusCode: number;}>) {
|
|
379
361
|
switch (push.data.statusCode) {
|
|
380
362
|
case 151:
|
|
381
363
|
this.platform.log.error('Command not supported by this device type.');
|
|
@@ -407,40 +389,8 @@ export class Bot {
|
|
|
407
389
|
* Handle requests to set the "On" characteristic
|
|
408
390
|
*/
|
|
409
391
|
private handleOnSet(value: CharacteristicValue) {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
if (this.TargetState === this.SwitchOn) {
|
|
414
|
-
this.platform.log.info('Target state of Bot has not changed: ' + (this.SwitchOn ? 'ON' : 'OFF'));
|
|
415
|
-
this.service?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.SwitchOn);
|
|
416
|
-
}
|
|
417
|
-
} else {
|
|
418
|
-
this.platform.debug(`Bot ${this.accessory.displayName} - Set On: ${value}`);
|
|
419
|
-
this.SwitchOn = value;
|
|
420
|
-
this.doBotUpdate.next();
|
|
421
|
-
}
|
|
392
|
+
this.platform.debug(`Bot ${this.accessory.displayName} - Set On: ${value}`);
|
|
393
|
+
this.SwitchOn === this.TargetState === value;
|
|
394
|
+
this.doBotUpdate.next();
|
|
422
395
|
}
|
|
423
|
-
|
|
424
|
-
async setTargetDeviceState(targetDevice: any, targetState: boolean): Promise<null> {
|
|
425
|
-
return await this.retry(5, () => {
|
|
426
|
-
if (targetState) {
|
|
427
|
-
return targetDevice.turnOn();
|
|
428
|
-
} else {
|
|
429
|
-
return targetDevice.turnOff();
|
|
430
|
-
}
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
async retry(max: number, fn: { (): any; (): Promise<any>; }): Promise<null> {
|
|
435
|
-
return fn().catch(async (err: any) => {
|
|
436
|
-
if (max === 0) {
|
|
437
|
-
throw err;
|
|
438
|
-
}
|
|
439
|
-
this.platform.log.info(err);
|
|
440
|
-
this.platform.log.info('Retrying');
|
|
441
|
-
await this.switchbot.wait(1000);
|
|
442
|
-
return this.retry(max - 1, fn);
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
|
|
446
396
|
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { Service, PlatformAccessory, CharacteristicValue
|
|
1
|
+
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, devicesConfig } from '../settings';
|
|
5
|
+
import { DeviceURL, device, devicesConfig, switchbot, deviceStatusResponse } from '../settings';
|
|
6
|
+
import { AxiosResponse } from 'axios';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Platform Accessory
|
|
9
10
|
* An instance of this class is created for each accessory your platform registers
|
|
10
11
|
* Each accessory may expose multiple services of different service types.
|
|
11
12
|
*/
|
|
12
|
-
export class
|
|
13
|
+
export class ColorBulb {
|
|
13
14
|
// Services
|
|
14
15
|
service!: Service;
|
|
15
16
|
|
|
@@ -18,27 +19,18 @@ export class Bulb {
|
|
|
18
19
|
Brightness!: CharacteristicValue;
|
|
19
20
|
ColorTemperature!: CharacteristicValue;
|
|
20
21
|
|
|
21
|
-
// Others
|
|
22
|
-
deviceStatus
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
quick: boolean;
|
|
30
|
-
id: MacAddress;
|
|
31
|
-
}
|
|
32
|
-
) => Promise<any>;
|
|
33
|
-
wait: (
|
|
34
|
-
arg0: number
|
|
35
|
-
) => any;
|
|
36
|
-
};
|
|
22
|
+
// OpenAPI Others
|
|
23
|
+
deviceStatus!: deviceStatusResponse;
|
|
24
|
+
|
|
25
|
+
// BLE Others
|
|
26
|
+
switchbot!: switchbot;
|
|
27
|
+
|
|
28
|
+
// Config
|
|
29
|
+
set_minStep?: number;
|
|
37
30
|
|
|
38
31
|
// Updates
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
set_minStep: any;
|
|
32
|
+
colorBulbUpdateInProgress!: boolean;
|
|
33
|
+
doColorBulbUpdate!: Subject<void>;
|
|
42
34
|
|
|
43
35
|
constructor(
|
|
44
36
|
private readonly platform: SwitchBotPlatform,
|
|
@@ -59,8 +51,8 @@ export class Bulb {
|
|
|
59
51
|
}
|
|
60
52
|
|
|
61
53
|
// this is subject we use to track when we need to POST changes to the SwitchBot API
|
|
62
|
-
this.
|
|
63
|
-
this.
|
|
54
|
+
this.doColorBulbUpdate = new Subject();
|
|
55
|
+
this.colorBulbUpdateInProgress = false;
|
|
64
56
|
|
|
65
57
|
|
|
66
58
|
// Retrieve initial values and updateHomekit
|
|
@@ -70,7 +62,7 @@ export class Bulb {
|
|
|
70
62
|
accessory
|
|
71
63
|
.getService(this.platform.Service.AccessoryInformation)!
|
|
72
64
|
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
|
|
73
|
-
.setCharacteristic(this.platform.Characteristic.Model, 'SWITCHBOT-
|
|
65
|
+
.setCharacteristic(this.platform.Characteristic.Model, 'SWITCHBOT-COLORBULB-W1401400')
|
|
74
66
|
.setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!);
|
|
75
67
|
|
|
76
68
|
// get the Television service if it exists, otherwise create a new Television service
|
|
@@ -122,17 +114,17 @@ export class Bulb {
|
|
|
122
114
|
|
|
123
115
|
// Start an update interval
|
|
124
116
|
interval(this.platform.config.options!.refreshRate! * 1000)
|
|
125
|
-
.pipe(skipWhile(() => this.
|
|
117
|
+
.pipe(skipWhile(() => this.colorBulbUpdateInProgress))
|
|
126
118
|
.subscribe(() => {
|
|
127
119
|
this.refreshStatus();
|
|
128
120
|
});
|
|
129
121
|
|
|
130
122
|
// Watch for Bulb change events
|
|
131
123
|
// We put in a debounce of 100ms so we don't make duplicate calls
|
|
132
|
-
this.
|
|
124
|
+
this.doColorBulbUpdate
|
|
133
125
|
.pipe(
|
|
134
126
|
tap(() => {
|
|
135
|
-
this.
|
|
127
|
+
this.colorBulbUpdateInProgress = true;
|
|
136
128
|
}),
|
|
137
129
|
debounceTime(this.platform.config.options!.pushRate! * 1000),
|
|
138
130
|
)
|
|
@@ -141,16 +133,16 @@ export class Bulb {
|
|
|
141
133
|
await this.pushChanges();
|
|
142
134
|
} catch (e: any) {
|
|
143
135
|
this.platform.log.error(JSON.stringify(e.message));
|
|
144
|
-
this.platform.debug(`Bulb ${accessory.displayName} - ${JSON.stringify(e)}`);
|
|
136
|
+
this.platform.debug(`Color Bulb ${accessory.displayName} - ${JSON.stringify(e)}`);
|
|
145
137
|
this.apiError(e);
|
|
146
138
|
}
|
|
147
|
-
this.
|
|
139
|
+
this.colorBulbUpdateInProgress = false;
|
|
148
140
|
});
|
|
149
141
|
}
|
|
150
142
|
|
|
151
143
|
private minStep(): number | undefined {
|
|
152
|
-
if (this.device.
|
|
153
|
-
this.set_minStep = this.device.
|
|
144
|
+
if (this.device.colorbulb?.set_minStep) {
|
|
145
|
+
this.set_minStep = this.device.colorbulb?.set_minStep;
|
|
154
146
|
} else {
|
|
155
147
|
this.set_minStep = 1;
|
|
156
148
|
}
|
|
@@ -165,21 +157,21 @@ export class Bulb {
|
|
|
165
157
|
default:
|
|
166
158
|
this.On = false;
|
|
167
159
|
}
|
|
168
|
-
this.platform.debug(`Bulb ${this.accessory.displayName} On: ${this.On}`);
|
|
160
|
+
this.platform.debug(`Color Bulb ${this.accessory.displayName} On: ${this.On}`);
|
|
169
161
|
this.deviceStatus.body.brightness = Number(this.Brightness);
|
|
170
162
|
this.deviceStatus.body.colorTemperature = Number(this.ColorTemperature);
|
|
171
163
|
}
|
|
172
164
|
|
|
173
165
|
async refreshStatus() {
|
|
174
166
|
try {
|
|
175
|
-
this.platform.debug('Bulb - Reading', `${DeviceURL}/${this.device.deviceId}/status`);
|
|
167
|
+
this.platform.debug('Color Bulb - Reading', `${DeviceURL}/${this.device.deviceId}/status`);
|
|
176
168
|
this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
|
|
177
|
-
this.platform.device(`Bulb ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
|
|
169
|
+
this.platform.device(`Color Bulb ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
|
|
178
170
|
this.parseStatus();
|
|
179
171
|
this.updateHomeKitCharacteristics();
|
|
180
172
|
} catch (e: any) {
|
|
181
|
-
this.platform.log.error(`Bulb ${this.accessory.displayName} failed to refresh status, Error Message ${JSON.stringify(e.message)}`);
|
|
182
|
-
this.platform.debug(`Bulb ${this.accessory.displayName}, Error: ${JSON.stringify(e)}`);
|
|
173
|
+
this.platform.log.error(`Color Bulb ${this.accessory.displayName} failed to refresh status, Error Message ${JSON.stringify(e.message)}`);
|
|
174
|
+
this.platform.debug(`Color Bulb ${this.accessory.displayName}, Error: ${JSON.stringify(e)}`);
|
|
183
175
|
this.apiError(e);
|
|
184
176
|
}
|
|
185
177
|
}
|
|
@@ -187,8 +179,8 @@ export class Bulb {
|
|
|
187
179
|
/**
|
|
188
180
|
* Pushes the requested changes to the SwitchBot API
|
|
189
181
|
* deviceType commandType Command command parameter Description
|
|
190
|
-
* Bulb - "command" "turnOff" "default" = set to OFF state
|
|
191
|
-
* Bulb - "command" "turnOn" "default" = set to ON state
|
|
182
|
+
* Color Bulb - "command" "turnOff" "default" = set to OFF state
|
|
183
|
+
* Color Bulb - "command" "turnOn" "default" = set to ON state
|
|
192
184
|
*/
|
|
193
185
|
async pushChanges() {
|
|
194
186
|
const payload = {
|
|
@@ -212,32 +204,32 @@ export class Bulb {
|
|
|
212
204
|
'commandType:',
|
|
213
205
|
payload.commandType,
|
|
214
206
|
);
|
|
215
|
-
this.platform.debug(`Bulb ${this.accessory.displayName} pushchanges: ${JSON.stringify(payload)}`);
|
|
207
|
+
this.platform.debug(`Color Bulb ${this.accessory.displayName} pushchanges: ${JSON.stringify(payload)}`);
|
|
216
208
|
|
|
217
209
|
// Make the API request
|
|
218
210
|
const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload));
|
|
219
|
-
this.platform.debug(`Bulb ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`);
|
|
211
|
+
this.platform.debug(`Color Bulb ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`);
|
|
220
212
|
this.statusCode(push);
|
|
221
213
|
}
|
|
222
214
|
|
|
223
215
|
updateHomeKitCharacteristics() {
|
|
224
216
|
if (this.On === undefined) {
|
|
225
|
-
this.platform.debug(`Bulb ${this.accessory.displayName} On: ${this.On}`);
|
|
217
|
+
this.platform.debug(`Color Bulb ${this.accessory.displayName} On: ${this.On}`);
|
|
226
218
|
} else {
|
|
227
219
|
this.service.updateCharacteristic(this.platform.Characteristic.On, this.On);
|
|
228
|
-
this.platform.device(`Bulb ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
|
|
220
|
+
this.platform.device(`Color Bulb ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
|
|
229
221
|
}
|
|
230
222
|
if (this.Brightness === undefined) {
|
|
231
|
-
this.platform.debug(`Bulb ${this.accessory.displayName} Brightness: ${this.Brightness}`);
|
|
223
|
+
this.platform.debug(`Color Bulb ${this.accessory.displayName} Brightness: ${this.Brightness}`);
|
|
232
224
|
} else {
|
|
233
225
|
this.service.updateCharacteristic(this.platform.Characteristic.Brightness, this.Brightness);
|
|
234
|
-
this.platform.device(`Bulb ${this.accessory.displayName} updateCharacteristic Brightness: ${this.Brightness}`);
|
|
226
|
+
this.platform.device(`Color Bulb ${this.accessory.displayName} updateCharacteristic Brightness: ${this.Brightness}`);
|
|
235
227
|
}
|
|
236
228
|
if (this.ColorTemperature === undefined) {
|
|
237
|
-
this.platform.debug(`Bulb ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`);
|
|
229
|
+
this.platform.debug(`Color Bulb ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`);
|
|
238
230
|
} else {
|
|
239
231
|
this.service.updateCharacteristic(this.platform.Characteristic.Brightness, this.ColorTemperature);
|
|
240
|
-
this.platform.debug(`Bulb ${this.accessory.displayName} updateCharacteristic ColorTemperature: ${this.ColorTemperature}`);
|
|
232
|
+
this.platform.debug(`Color Bulb ${this.accessory.displayName} updateCharacteristic ColorTemperature: ${this.ColorTemperature}`);
|
|
241
233
|
}
|
|
242
234
|
}
|
|
243
235
|
|
|
@@ -248,7 +240,7 @@ export class Bulb {
|
|
|
248
240
|
}
|
|
249
241
|
|
|
250
242
|
|
|
251
|
-
private statusCode(push: {
|
|
243
|
+
private statusCode(push: AxiosResponse<{ statusCode: number;}>) {
|
|
252
244
|
switch (push.data.statusCode) {
|
|
253
245
|
case 151:
|
|
254
246
|
this.platform.log.error('Command not supported by this device type.');
|
|
@@ -285,7 +277,7 @@ export class Bulb {
|
|
|
285
277
|
this.platform.debug(`${this.accessory.displayName} - Set On: ${value}`);
|
|
286
278
|
|
|
287
279
|
this.On = value;
|
|
288
|
-
this.
|
|
280
|
+
this.doColorBulbUpdate.next();
|
|
289
281
|
}
|
|
290
282
|
|
|
291
283
|
/**
|
|
@@ -295,7 +287,7 @@ export class Bulb {
|
|
|
295
287
|
this.platform.debug(`${this.accessory.displayName} - Set On: ${value}`);
|
|
296
288
|
|
|
297
289
|
this.Brightness = value;
|
|
298
|
-
this.
|
|
290
|
+
this.doColorBulbUpdate.next();
|
|
299
291
|
}
|
|
300
292
|
|
|
301
293
|
/**
|
|
@@ -305,6 +297,6 @@ export class Bulb {
|
|
|
305
297
|
this.platform.debug(`${this.accessory.displayName} - Set On: ${value}`);
|
|
306
298
|
|
|
307
299
|
this.ColorTemperature = value;
|
|
308
|
-
this.
|
|
300
|
+
this.doColorBulbUpdate.next();
|
|
309
301
|
}
|
|
310
302
|
}
|