homebridge-tuya-plus 3.1.2
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/bug_report.md +40 -0
- package/.github/ISSUE_TEMPLATE/new-device.md +24 -0
- package/.github/workflows/codeql-analysis.yml +67 -0
- package/.github/workflows/lint.yml +23 -0
- package/Changelog.md +63 -0
- package/LICENSE +21 -0
- package/Readme.MD +106 -0
- package/assets/Tuya-Plugin-Branding.png +0 -0
- package/bin/cli-decode.js +197 -0
- package/bin/cli-find.js +207 -0
- package/bin/cli.js +13 -0
- package/config-example.MD +43 -0
- package/config.schema.json +538 -0
- package/eslint.config.mjs +15 -0
- package/index.js +242 -0
- package/lib/AirConditionerAccessory.js +445 -0
- package/lib/AirPurifierAccessory.js +532 -0
- package/lib/BaseAccessory.js +290 -0
- package/lib/ConvectorAccessory.js +313 -0
- package/lib/CustomMultiOutletAccessory.js +111 -0
- package/lib/DehumidifierAccessory.js +301 -0
- package/lib/DoorbellAccessory.js +208 -0
- package/lib/EnergyCharacteristics.js +86 -0
- package/lib/GarageDoorAccessory.js +307 -0
- package/lib/MultiOutletAccessory.js +106 -0
- package/lib/OilDiffuserAccessory.js +480 -0
- package/lib/OutletAccessory.js +83 -0
- package/lib/RGBTWLightAccessory.js +234 -0
- package/lib/RGBTWOutletAccessory.js +296 -0
- package/lib/SimpleBlindsAccessory.js +299 -0
- package/lib/SimpleDimmer2Accessory.js +54 -0
- package/lib/SimpleDimmerAccessory.js +54 -0
- package/lib/SimpleFanAccessory.js +137 -0
- package/lib/SimpleFanLightAccessory.js +201 -0
- package/lib/SimpleHeaterAccessory.js +154 -0
- package/lib/SimpleLightAccessory.js +39 -0
- package/lib/SwitchAccessory.js +106 -0
- package/lib/TWLightAccessory.js +91 -0
- package/lib/TuyaAccessory.js +746 -0
- package/lib/TuyaDiscovery.js +165 -0
- package/lib/ValveAccessory.js +150 -0
- package/package.json +49 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
const BaseAccessory = require('./BaseAccessory');
|
|
2
|
+
|
|
3
|
+
// define constants for Kogan garage door.
|
|
4
|
+
// Action
|
|
5
|
+
const GARAGE_DOOR_OPEN = 'open';
|
|
6
|
+
const GARAGE_DOOR_CLOSE = 'close';
|
|
7
|
+
const GARAGE_DOOR_FOPEN = 'fopen';
|
|
8
|
+
const GARAGE_DOOR_FCLOSE = 'fclose';
|
|
9
|
+
|
|
10
|
+
// Status or state
|
|
11
|
+
// yes, 'openning' is not a mistake, that's the text from the Kogan opener
|
|
12
|
+
const GARAGE_DOOR_OPENED = 'opened';
|
|
13
|
+
const GARAGE_DOOR_CLOSED = 'closed';
|
|
14
|
+
const GARAGE_DOOR_OPENNING = 'openning';
|
|
15
|
+
const GARAGE_DOOR_OPENING =
|
|
16
|
+
'opening'; // 'opening' is not currently a valid value; added in case Kogan
|
|
17
|
+
// one day decides to correct the spelling
|
|
18
|
+
const GARAGE_DOOR_CLOSING = 'closing';
|
|
19
|
+
// Kogan garage door appears to have no stopped status
|
|
20
|
+
|
|
21
|
+
// Kogan manufacturer name
|
|
22
|
+
const GARAGE_DOOR_MANUFACTURER_KOGAN = 'Kogan';
|
|
23
|
+
|
|
24
|
+
// Wofea manufacturer name
|
|
25
|
+
const GARAGE_DOOR_MANUFACTURER_WOFEA = 'Wofea';
|
|
26
|
+
|
|
27
|
+
// main code
|
|
28
|
+
class GarageDoorAccessory extends BaseAccessory {
|
|
29
|
+
static getCategory(Categories) {
|
|
30
|
+
return Categories.GARAGE_DOOR_OPENER;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
constructor(...props) {
|
|
34
|
+
super(...props);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
_registerPlatformAccessory() {
|
|
38
|
+
const {Service} = this.hap;
|
|
39
|
+
|
|
40
|
+
this.accessory.addService(Service.GarageDoorOpener, this.device.context.name);
|
|
41
|
+
|
|
42
|
+
super._registerPlatformAccessory();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// function to return a ID string for log messages
|
|
46
|
+
_logPrefix() {
|
|
47
|
+
return (this.manufacturer ? this.manufacturer + ' ' : '') + 'GarageDoor';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// function to prefix a string ID and always log to console
|
|
51
|
+
_alwaysLog(...args) { this.log.info(this._logPrefix(), ...args); }
|
|
52
|
+
|
|
53
|
+
// function to log to console if debug is on
|
|
54
|
+
_debugLog(...args) {
|
|
55
|
+
this.log.debug(this._logPrefix(), ...args);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// function to return true if the garage door manufacturer is Kogan and false
|
|
59
|
+
// otherwise
|
|
60
|
+
_isKogan() {
|
|
61
|
+
if (this.manufacturer === GARAGE_DOOR_MANUFACTURER_KOGAN.trim()) {
|
|
62
|
+
return true;
|
|
63
|
+
} else {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// function to return true if the garage door manufacturer is Wofea
|
|
69
|
+
_isWofea() {
|
|
70
|
+
if (this.manufacturer === GARAGE_DOOR_MANUFACTURER_WOFEA.trim()) {
|
|
71
|
+
return true;
|
|
72
|
+
} else {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
_registerCharacteristics(dps) {
|
|
78
|
+
const {Service, Characteristic} = this.hap;
|
|
79
|
+
const service = this.accessory.getService(Service.GarageDoorOpener);
|
|
80
|
+
this._checkServiceName(service, this.device.context.name);
|
|
81
|
+
|
|
82
|
+
// Set the manufacturer string
|
|
83
|
+
// If the manufacturer string matches a known manufacturer, set to that string
|
|
84
|
+
// Otherwise set the manufacturer to the defined value
|
|
85
|
+
// Otherwise set the manufacturer to a blank string
|
|
86
|
+
if (this.device.context.manufacturer.trim().toLowerCase() ===
|
|
87
|
+
GARAGE_DOOR_MANUFACTURER_KOGAN.trim().toLowerCase()) {
|
|
88
|
+
this.manufacturer = GARAGE_DOOR_MANUFACTURER_KOGAN.trim();
|
|
89
|
+
} else if (this.device.context.manufacturer.trim().toLowerCase() ===
|
|
90
|
+
GARAGE_DOOR_MANUFACTURER_WOFEA.trim().toLowerCase()) {
|
|
91
|
+
this.manufacturer = this.device.context.manufacturer.trim();
|
|
92
|
+
} else if (this.device.context.manufacturer) {
|
|
93
|
+
this.manufacturer = this.device.context.manufacturer.trim();
|
|
94
|
+
} else {
|
|
95
|
+
this.manufacturer = '';
|
|
96
|
+
}
|
|
97
|
+
// set the dpAction and dpStatus values based on the manufacturer
|
|
98
|
+
if (this._isKogan()) {
|
|
99
|
+
// Kogan SmarterHome Wireless Garage Door Opener
|
|
100
|
+
this._debugLog(
|
|
101
|
+
'_registerCharacteristics setting dpAction and dpStatus for ' +
|
|
102
|
+
GARAGE_DOOR_MANUFACTURER_KOGAN + ' garage door');
|
|
103
|
+
this.dpAction = this._getCustomDP(this.device.context.dpAction) || '101';
|
|
104
|
+
this.dpStatus = this._getCustomDP(this.device.context.dpStatus) || '102';
|
|
105
|
+
} else if (this._isWofea()) {
|
|
106
|
+
// Wofea Wifi Switch Smart Garage Door Opener
|
|
107
|
+
this._debugLog(
|
|
108
|
+
'_registerCharacteristics setting dpAction and dpStatus for ' +
|
|
109
|
+
GARAGE_DOOR_MANUFACTURER_WOFEA + ' garage door');
|
|
110
|
+
this.dpAction = this._getCustomDP(this.device.context.dpAction) || '1';
|
|
111
|
+
this.dpStatus = this._getCustomDP(this.device.context.dpStatus) || '101';
|
|
112
|
+
} else {
|
|
113
|
+
// the original garage door opener
|
|
114
|
+
this._debugLog(
|
|
115
|
+
'_registerCharacteristics setting dpAction and dpStatus for generic door');
|
|
116
|
+
this.dpAction = this._getCustomDP(this.device.context.dpAction) || '1';
|
|
117
|
+
this.dpStatus = this._getCustomDP(this.device.context.dpStatus) || '2';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.currentOpen = Characteristic.CurrentDoorState.OPEN;
|
|
121
|
+
this.currentOpening = Characteristic.CurrentDoorState.OPENING;
|
|
122
|
+
this.currentClosing = Characteristic.CurrentDoorState.CLOSING;
|
|
123
|
+
this.currentClosed = Characteristic.CurrentDoorState.CLOSED;
|
|
124
|
+
this.currentStopped = Characteristic.CurrentDoorState.STOPPED;
|
|
125
|
+
this.targetOpen = Characteristic.TargetDoorState.OPEN;
|
|
126
|
+
this.targetClosed = Characteristic.TargetDoorState.CLOSED;
|
|
127
|
+
if (this.device.context.flipState) {
|
|
128
|
+
this.currentOpen = Characteristic.CurrentDoorState.CLOSED;
|
|
129
|
+
this.currentOpening = Characteristic.CurrentDoorState.CLOSING;
|
|
130
|
+
this.currentClosing = Characteristic.CurrentDoorState.OPENING;
|
|
131
|
+
this.currentClosed = Characteristic.CurrentDoorState.OPEN;
|
|
132
|
+
this.targetOpen = Characteristic.TargetDoorState.CLOSED;
|
|
133
|
+
this.targetClosed = Characteristic.TargetDoorState.OPEN;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const characteristicTargetDoorState = service.getCharacteristic(Characteristic.TargetDoorState)
|
|
137
|
+
.updateValue(this._getTargetDoorState(dps[this.dpStatus]))
|
|
138
|
+
.on('get', this.getTargetDoorState.bind(this))
|
|
139
|
+
.on('set', this.setTargetDoorState.bind(this));
|
|
140
|
+
|
|
141
|
+
const characteristicCurrentDoorState = service.getCharacteristic(Characteristic.CurrentDoorState)
|
|
142
|
+
.updateValue(this._getCurrentDoorState(dps[this.dpStatus]))
|
|
143
|
+
.on('get', this.getCurrentDoorState.bind(this));
|
|
144
|
+
|
|
145
|
+
this.device.on('change', changes => {
|
|
146
|
+
this._alwaysLog('changed:' + JSON.stringify(changes));
|
|
147
|
+
|
|
148
|
+
if (changes.hasOwnProperty(this.dpStatus)) {
|
|
149
|
+
const newCurrentDoorState =
|
|
150
|
+
this._getCurrentDoorState(changes[this.dpStatus]);
|
|
151
|
+
this._debugLog('on change new and old CurrentDoorState ' +
|
|
152
|
+
newCurrentDoorState + ' ' +
|
|
153
|
+
characteristicCurrentDoorState.value);
|
|
154
|
+
this._debugLog('on change old characteristicTargetDoorState ' +
|
|
155
|
+
characteristicTargetDoorState.value);
|
|
156
|
+
|
|
157
|
+
if (newCurrentDoorState == this.currentOpen &&
|
|
158
|
+
characteristicTargetDoorState.value !== this.targetOpen)
|
|
159
|
+
characteristicTargetDoorState.updateValue(this.targetOpen);
|
|
160
|
+
|
|
161
|
+
if (newCurrentDoorState == this.currentClosed &&
|
|
162
|
+
characteristicTargetDoorState.value !== this.targetClosed)
|
|
163
|
+
characteristicTargetDoorState.updateValue(this.targetClosed);
|
|
164
|
+
|
|
165
|
+
if (characteristicCurrentDoorState.value !== newCurrentDoorState) characteristicCurrentDoorState.updateValue(newCurrentDoorState);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
getTargetDoorState(callback) {
|
|
171
|
+
this.getState(this.dpStatus, (err, dp) => {
|
|
172
|
+
if (err) return callback(err);
|
|
173
|
+
|
|
174
|
+
this._debugLog('getTargetDoorState dp ' + JSON.stringify(dp));
|
|
175
|
+
|
|
176
|
+
callback(null, this._getTargetDoorState(dp));
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
_getTargetDoorState(dp) {
|
|
181
|
+
this._debugLog('_getTargetDoorState dp ' + JSON.stringify(dp));
|
|
182
|
+
|
|
183
|
+
if (this._isKogan()) {
|
|
184
|
+
// translate the Kogan strings to the enumerated status values
|
|
185
|
+
switch (dp) {
|
|
186
|
+
case GARAGE_DOOR_OPENED:
|
|
187
|
+
case GARAGE_DOOR_OPENNING:
|
|
188
|
+
case GARAGE_DOOR_OPENING:
|
|
189
|
+
return this.targetOpen;
|
|
190
|
+
|
|
191
|
+
case GARAGE_DOOR_CLOSED:
|
|
192
|
+
case GARAGE_DOOR_CLOSING:
|
|
193
|
+
return this.targetClosed;
|
|
194
|
+
|
|
195
|
+
default:
|
|
196
|
+
this._alwaysLog('_getTargetDoorState UNKNOWN STATE ' +
|
|
197
|
+
JSON.stringify(dp));
|
|
198
|
+
}
|
|
199
|
+
} else {
|
|
200
|
+
// Generic garage door uses true for the opened status and false for the
|
|
201
|
+
// closed status
|
|
202
|
+
if (dp === true) {
|
|
203
|
+
return this.targetOpen;
|
|
204
|
+
} else if (dp === false) {
|
|
205
|
+
return this.targetClosed;
|
|
206
|
+
} else {
|
|
207
|
+
this._alwaysLog('_getTargetDoorState UNKNOWN STATE ' +
|
|
208
|
+
JSON.stringify(dp));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
setTargetDoorState(value, callback) {
|
|
214
|
+
var newValue = GARAGE_DOOR_CLOSE;
|
|
215
|
+
this._debugLog('setTargetDoorState value ' + value + ' targetOpen ' +
|
|
216
|
+
this.targetOpen + ' targetClosed ' + this.targetClosed);
|
|
217
|
+
|
|
218
|
+
if (this._isKogan()) {
|
|
219
|
+
// translate the the enumerated status values to Kogan strings
|
|
220
|
+
switch (value) {
|
|
221
|
+
case this.targetOpen:
|
|
222
|
+
newValue = GARAGE_DOOR_OPEN;
|
|
223
|
+
break;
|
|
224
|
+
|
|
225
|
+
case this.targetClosed:
|
|
226
|
+
newValue = GARAGE_DOOR_CLOSE;
|
|
227
|
+
break;
|
|
228
|
+
|
|
229
|
+
default:
|
|
230
|
+
this._alwaysLog('setTargetDoorState UNKNOWN STATE ' +
|
|
231
|
+
JSON.stringify(value));
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
// Generic garage door uses true for the open action and false for the
|
|
235
|
+
// close action
|
|
236
|
+
switch (value) {
|
|
237
|
+
case this.targetOpen:
|
|
238
|
+
newValue = true;
|
|
239
|
+
break;
|
|
240
|
+
|
|
241
|
+
case this.targetClosed:
|
|
242
|
+
newValue = false;
|
|
243
|
+
break;
|
|
244
|
+
|
|
245
|
+
default:
|
|
246
|
+
this._alwaysLog('setTargetDoorState UNKNOWN STATE ' +
|
|
247
|
+
JSON.stringify(value));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
this.setState(this.dpAction, newValue, callback);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
getCurrentDoorState(callback) {
|
|
255
|
+
this.getState(this.dpStatus, (err, dpStatusValue) => {
|
|
256
|
+
if (err) return callback(err);
|
|
257
|
+
|
|
258
|
+
callback(null, this._getCurrentDoorState(dpStatusValue));
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
_getCurrentDoorState(dpStatusValue) {
|
|
263
|
+
this._debugLog('_getCurrentDoorState dpStatusValue ' +
|
|
264
|
+
JSON.stringify(dpStatusValue));
|
|
265
|
+
|
|
266
|
+
if (this._isKogan()) {
|
|
267
|
+
// translate the Kogan strings to the enumerated status values
|
|
268
|
+
switch (dpStatusValue) {
|
|
269
|
+
case GARAGE_DOOR_OPENED:
|
|
270
|
+
return this.currentOpen;
|
|
271
|
+
|
|
272
|
+
case GARAGE_DOOR_OPENNING:
|
|
273
|
+
case GARAGE_DOOR_OPENING:
|
|
274
|
+
return this.currentOpening;
|
|
275
|
+
|
|
276
|
+
case GARAGE_DOOR_CLOSING:
|
|
277
|
+
return this.currentClosing;
|
|
278
|
+
|
|
279
|
+
case GARAGE_DOOR_CLOSED:
|
|
280
|
+
return this.currentClosed;
|
|
281
|
+
|
|
282
|
+
default:
|
|
283
|
+
this._alwaysLog('_getCurrentDoorState UNKNOWN STATUS ' +
|
|
284
|
+
JSON.stringify(dpStatusValue));
|
|
285
|
+
}
|
|
286
|
+
} else {
|
|
287
|
+
// Generic garage door uses true for the open status and false for the
|
|
288
|
+
// close status. It doesn't seem to have other values for opening and
|
|
289
|
+
// closing. If the getState() function callback in BaseAccessory.js passed
|
|
290
|
+
// the dps object into this function, we may be able to infer opening and
|
|
291
|
+
// closing from the combined dpStatus and dpAction values. That would
|
|
292
|
+
// require mods to every accessory that used that callback. Not worth it.
|
|
293
|
+
if (dpStatusValue === true) {
|
|
294
|
+
// dpStatus true corresponds to an open door
|
|
295
|
+
return this.currentOpen;
|
|
296
|
+
} else if (dpStatusValue === false) {
|
|
297
|
+
// dpStatus false corresponds to a closed door, so assume "not open"
|
|
298
|
+
return this.currentClosed;
|
|
299
|
+
} else {
|
|
300
|
+
this._alwaysLog('_getCurrentDoorState UNKNOWN STATUS ' +
|
|
301
|
+
JSON.stringify(dpStatusValue));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
module.exports = GarageDoorAccessory;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const BaseAccessory = require('./BaseAccessory');
|
|
2
|
+
const async = require('async');
|
|
3
|
+
|
|
4
|
+
class MultiOutletAccessory extends BaseAccessory {
|
|
5
|
+
static getCategory(Categories) {
|
|
6
|
+
return Categories.OUTLET;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
constructor(...props) {
|
|
10
|
+
super(...props);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
_registerPlatformAccessory() {
|
|
14
|
+
this._verifyCachedPlatformAccessory();
|
|
15
|
+
this._justRegistered = true;
|
|
16
|
+
|
|
17
|
+
super._registerPlatformAccessory();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_verifyCachedPlatformAccessory() {
|
|
21
|
+
if (this._justRegistered) return;
|
|
22
|
+
|
|
23
|
+
const {Service} = this.hap;
|
|
24
|
+
|
|
25
|
+
const outletCount = parseInt(this.device.context.outletCount) || 1;
|
|
26
|
+
const _validServices = [];
|
|
27
|
+
for (let i = 0; i++ < outletCount;) {
|
|
28
|
+
let service = this.accessory.getServiceByUUIDAndSubType(Service.Outlet, 'outlet ' + i);
|
|
29
|
+
if (service) this._checkServiceName(service, this.device.context.name + ' ' + i);
|
|
30
|
+
else service = this.accessory.addService(Service.Outlet, this.device.context.name + ' ' + i, 'outlet ' + i);
|
|
31
|
+
|
|
32
|
+
_validServices.push(service);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
this.accessory.services
|
|
36
|
+
.filter(service => service.UUID === Service.Outlet.UUID && !_validServices.includes(service))
|
|
37
|
+
.forEach(service => {
|
|
38
|
+
this.log.info('Removing', service.displayName);
|
|
39
|
+
this.accessory.removeService(service);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_registerCharacteristics(dps) {
|
|
44
|
+
this._verifyCachedPlatformAccessory();
|
|
45
|
+
|
|
46
|
+
const {Service, Characteristic} = this.hap;
|
|
47
|
+
|
|
48
|
+
const characteristics = {};
|
|
49
|
+
this.accessory.services.forEach(service => {
|
|
50
|
+
if (service.UUID !== Service.Outlet.UUID || !service.subtype) return false;
|
|
51
|
+
|
|
52
|
+
let match;
|
|
53
|
+
if ((match = service.subtype.match(/^outlet (\d+)$/)) === null) return;
|
|
54
|
+
|
|
55
|
+
characteristics[match[1]] = service.getCharacteristic(Characteristic.On)
|
|
56
|
+
.updateValue(dps[match[1]])
|
|
57
|
+
.on('get', this.getPower.bind(this, match[1]))
|
|
58
|
+
.on('set', this.setPower.bind(this, match[1]));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
this.device.on('change', (changes, state) => {
|
|
62
|
+
Object.keys(changes).forEach(key => {
|
|
63
|
+
if (characteristics[key] && characteristics[key].value !== changes[key]) characteristics[key].updateValue(changes[key]);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getPower(dp, callback) {
|
|
69
|
+
callback(null, this.device.state[dp]);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
setPower(dp, value, callback) {
|
|
73
|
+
if (!this._pendingPower) {
|
|
74
|
+
this._pendingPower = {props: {}, callbacks: []};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (dp) {
|
|
78
|
+
if (this._pendingPower.timer) clearTimeout(this._pendingPower.timer);
|
|
79
|
+
|
|
80
|
+
this._pendingPower.props = {...this._pendingPower.props, ...{[dp]: value}};
|
|
81
|
+
this._pendingPower.callbacks.push(callback);
|
|
82
|
+
|
|
83
|
+
this._pendingPower.timer = setTimeout(() => {
|
|
84
|
+
this.setPower();
|
|
85
|
+
}, 500);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const callbacks = this._pendingPower.callbacks;
|
|
90
|
+
const callEachBack = err => {
|
|
91
|
+
async.eachSeries(callbacks, (callback, next) => {
|
|
92
|
+
try {
|
|
93
|
+
callback(err);
|
|
94
|
+
} catch (ex) {}
|
|
95
|
+
next();
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const newValue = this._pendingPower.props;
|
|
100
|
+
this._pendingPower = null;
|
|
101
|
+
|
|
102
|
+
this.setMultiState(newValue, callEachBack);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = MultiOutletAccessory;
|