homebridge-tuya-community 3.3.0 → 3.3.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/npm-publish.yml +54 -0
- package/Readme.MD +3 -3
- package/bin/cli-decode.js +1 -1
- package/bin/cli-find.js +10 -10
- package/index.js +3 -3
- package/lib/AirPurifierAccessory.js +140 -139
- package/lib/CustomMultiOutletAccessory.js +3 -3
- package/lib/DehumidifierAccessory.js +24 -27
- package/lib/GarageDoorAccessory.js +11 -11
- package/lib/MultiOutletAccessory.js +2 -2
- package/lib/OilDiffuserAccessory.js +5 -6
- package/lib/RGBTWLightAccessory.js +11 -11
- package/lib/RGBTWOutletAccessory.js +1 -1
- package/lib/SimpleFanAccessory.js +35 -40
- package/lib/SimpleFanLightAccessory.js +35 -46
- package/lib/SwitchAccessory.js +2 -2
- package/lib/TWLightAccessory.js +3 -3
- package/lib/TuyaAccessory.js +34 -34
- package/package.json +2 -2
|
@@ -10,14 +10,14 @@ class SimpleFanLightAccessory extends BaseAccessory {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
_registerPlatformAccessory() {
|
|
13
|
-
const {Service} = this.hap;
|
|
13
|
+
const { Service } = this.hap;
|
|
14
14
|
this.accessory.addService(Service.Fan, this.device.context.name);
|
|
15
15
|
this.accessory.addService(Service.Lightbulb, this.device.context.name + ' Light');
|
|
16
16
|
super._registerPlatformAccessory();
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
_registerCharacteristics(dps) {
|
|
20
|
-
const {Service, Characteristic} = this.hap;
|
|
20
|
+
const { Service, Characteristic } = this.hap;
|
|
21
21
|
const serviceFan = this.accessory.getService(Service.Fan);
|
|
22
22
|
const serviceLightbulb = this.accessory.getService(Service.Lightbulb);
|
|
23
23
|
this._checkServiceName(serviceFan, this.device.context.name);
|
|
@@ -100,40 +100,37 @@ class SimpleFanLightAccessory extends BaseAccessory {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
_getFanOn(dp) {
|
|
103
|
-
const {Characteristic} = this.hap;
|
|
104
103
|
return dp;
|
|
105
104
|
}
|
|
106
105
|
|
|
107
106
|
setFanOn(value, callback) {
|
|
108
|
-
const {Characteristic} = this.hap;
|
|
109
107
|
// This uses the multistatelegacy set command to send the fan on and speed request in one call.
|
|
110
|
-
if (value == false
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
108
|
+
if (value == false) {
|
|
109
|
+
this.fanCurrentSpeed = 0;
|
|
110
|
+
// This will turn off the fan speed if it is set to be 0.
|
|
111
|
+
return this.setState(this.dpFanOn, false, callback);
|
|
114
112
|
} else {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
} else {
|
|
123
|
-
// The current fanCurrentSpeed Variable is there to ensure the fan speed doesn't change if the fan is already on.
|
|
124
|
-
if (this.useStrings) {
|
|
125
|
-
return this.setMultiStateLegacy({[this.dpFanOn]: value, [this.dpRotationSpeed]: this.fanCurrentSpeed.toString()}, callback);
|
|
113
|
+
if (this.fanCurrentSpeed === 0) {
|
|
114
|
+
// The current fanDefaultSpeed Variable is there to have the fan set to a sensible default if turned on.
|
|
115
|
+
if (this.useStrings) {
|
|
116
|
+
return this.setMultiStateLegacy({ [this.dpFanOn]: value, [this.dpRotationSpeed]: this.fanDefaultSpeed.toString() }, callback);
|
|
117
|
+
} else {
|
|
118
|
+
return this.setMultiStateLegacy({ [this.dpFanOn]: value, [this.dpRotationSpeed]: this.fanDefaultSpeed }, callback);
|
|
119
|
+
}
|
|
126
120
|
} else {
|
|
127
|
-
|
|
121
|
+
// The current fanCurrentSpeed Variable is there to ensure the fan speed doesn't change if the fan is already on.
|
|
122
|
+
if (this.useStrings) {
|
|
123
|
+
return this.setMultiStateLegacy({ [this.dpFanOn]: value, [this.dpRotationSpeed]: this.fanCurrentSpeed.toString() }, callback);
|
|
124
|
+
} else {
|
|
125
|
+
return this.setMultiStateLegacy({ [this.dpFanOn]: value, [this.dpRotationSpeed]: this.fanCurrentSpeed }, callback);
|
|
126
|
+
}
|
|
128
127
|
}
|
|
129
|
-
}
|
|
130
128
|
}
|
|
131
|
-
callback();
|
|
132
129
|
}
|
|
133
130
|
|
|
134
131
|
// Get the Current Fan Speed
|
|
135
132
|
getSpeed(callback) {
|
|
136
|
-
this.getState(this.dpRotationSpeed, (err,
|
|
133
|
+
this.getState(this.dpRotationSpeed, (err, _dp) => {
|
|
137
134
|
if (err) return callback(err);
|
|
138
135
|
callback(null, this.convertRotationSpeedFromTuyaToHomeKit(this.device.state[this.dpRotationSpeed]));
|
|
139
136
|
});
|
|
@@ -141,25 +138,23 @@ class SimpleFanLightAccessory extends BaseAccessory {
|
|
|
141
138
|
|
|
142
139
|
// Set the new fan speed
|
|
143
140
|
setSpeed(value, callback) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
} else {
|
|
153
|
-
// This is to set the fan speed variable to match the current speed.
|
|
154
|
-
this.fanCurrentSpeed = this.convertRotationSpeedFromHomeKitToTuya(value);
|
|
155
|
-
// This uses the multistatelegacy set command to send the fan on and speed request in one call.
|
|
156
|
-
if (this.useStrings) {
|
|
157
|
-
return this.setMultiStateLegacy({[this.dpFanOn]: true, [this.dpRotationSpeed]: this.convertRotationSpeedFromHomeKitToTuya(value).toString()}, callback);
|
|
141
|
+
if (value === 0) {
|
|
142
|
+
// This is to set the fan speed variable to be 1 when the fan is off.
|
|
143
|
+
if (this.useStrings) {
|
|
144
|
+
return this.setMultiStateLegacy({ [this.dpFanOn]: false, [this.dpRotationSpeed]: this.fanDefaultSpeed.toString() }, callback);
|
|
145
|
+
} else {
|
|
146
|
+
return this.setMultiStateLegacy({ [this.dpFanOn]: false, [this.dpRotationSpeed]: this.fanDefaultSpeed }, callback);
|
|
147
|
+
}
|
|
158
148
|
} else {
|
|
159
|
-
|
|
149
|
+
// This is to set the fan speed variable to match the current speed.
|
|
150
|
+
this.fanCurrentSpeed = this.convertRotationSpeedFromHomeKitToTuya(value);
|
|
151
|
+
// This uses the multistatelegacy set command to send the fan on and speed request in one call.
|
|
152
|
+
if (this.useStrings) {
|
|
153
|
+
return this.setMultiStateLegacy({ [this.dpFanOn]: true, [this.dpRotationSpeed]: this.convertRotationSpeedFromHomeKitToTuya(value).toString() }, callback);
|
|
154
|
+
} else {
|
|
155
|
+
return this.setMultiStateLegacy({ [this.dpFanOn]: true, [this.dpRotationSpeed]: this.convertRotationSpeedFromHomeKitToTuya(value) }, callback);
|
|
156
|
+
}
|
|
160
157
|
}
|
|
161
|
-
}
|
|
162
|
-
callback();
|
|
163
158
|
}
|
|
164
159
|
|
|
165
160
|
/*************************** LIGHT ***************************/
|
|
@@ -172,14 +167,11 @@ class SimpleFanLightAccessory extends BaseAccessory {
|
|
|
172
167
|
}
|
|
173
168
|
|
|
174
169
|
_getLightOn(dp) {
|
|
175
|
-
const {Characteristic} = this.hap;
|
|
176
170
|
return dp;
|
|
177
171
|
}
|
|
178
172
|
|
|
179
173
|
setLightOn(value, callback) {
|
|
180
|
-
const {Characteristic} = this.hap;
|
|
181
174
|
return this.setState(this.dpLightOn, value, callback);
|
|
182
|
-
callback();
|
|
183
175
|
}
|
|
184
176
|
|
|
185
177
|
//Lightbulb Brightness
|
|
@@ -191,14 +183,11 @@ class SimpleFanLightAccessory extends BaseAccessory {
|
|
|
191
183
|
}
|
|
192
184
|
|
|
193
185
|
_getBrightness(dp) {
|
|
194
|
-
const {Characteristic} = this.hap;
|
|
195
186
|
return dp;
|
|
196
187
|
}
|
|
197
188
|
|
|
198
189
|
setBrightness(value, callback) {
|
|
199
|
-
const {Characteristic} = this.hap;
|
|
200
190
|
return this.setState(this.dpBrightness, value, callback);
|
|
201
|
-
callback();
|
|
202
191
|
}
|
|
203
192
|
}
|
|
204
193
|
|
package/lib/SwitchAccessory.js
CHANGED
|
@@ -58,7 +58,7 @@ class SwitchAccessory extends BaseAccessory {
|
|
|
58
58
|
.on('set', this.setPower.bind(this, match[1]));
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
this.device.on('change', (changes,
|
|
61
|
+
this.device.on('change', (changes, _state) => {
|
|
62
62
|
Object.keys(changes).forEach(key => {
|
|
63
63
|
if (characteristics[key] && characteristics[key].value !== changes[key]) characteristics[key].updateValue(changes[key]);
|
|
64
64
|
});
|
|
@@ -91,7 +91,7 @@ class SwitchAccessory extends BaseAccessory {
|
|
|
91
91
|
async.eachSeries(callbacks, (callback, next) => {
|
|
92
92
|
try {
|
|
93
93
|
callback(err);
|
|
94
|
-
} catch (ex) { }
|
|
94
|
+
} catch (ex) { /* Intentionally empty */ }
|
|
95
95
|
next();
|
|
96
96
|
});
|
|
97
97
|
};
|
package/lib/TWLightAccessory.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const BaseAccessory = require('./BaseAccessory');
|
|
2
|
-
const
|
|
2
|
+
const _async = require('async');
|
|
3
3
|
|
|
4
4
|
class TWLightAccessory extends BaseAccessory {
|
|
5
5
|
static getCategory(Categories) {
|
|
@@ -11,7 +11,7 @@ class TWLightAccessory extends BaseAccessory {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
_registerPlatformAccessory() {
|
|
14
|
-
const {Service} = this.hap;
|
|
14
|
+
const { Service } = this.hap;
|
|
15
15
|
|
|
16
16
|
this.accessory.addService(Service.Lightbulb, this.device.context.name);
|
|
17
17
|
|
|
@@ -19,7 +19,7 @@ class TWLightAccessory extends BaseAccessory {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
_registerCharacteristics(dps) {
|
|
22
|
-
const {Service, Characteristic, AdaptiveLightingController} = this.hap;
|
|
22
|
+
const { Service, Characteristic, AdaptiveLightingController } = this.hap;
|
|
23
23
|
const service = this.accessory.getService(Service.Lightbulb);
|
|
24
24
|
this._checkServiceName(service, this.device.context.name);
|
|
25
25
|
|
package/lib/TuyaAccessory.js
CHANGED
|
@@ -17,7 +17,7 @@ class TuyaAccessory extends EventEmitter {
|
|
|
17
17
|
|
|
18
18
|
this.log = props.log;
|
|
19
19
|
|
|
20
|
-
this.context = {version: '3.1', port: 6668, ...props};
|
|
20
|
+
this.context = { version: '3.1', port: 6668, ...props };
|
|
21
21
|
|
|
22
22
|
this.state = {};
|
|
23
23
|
this._cachedBuffer = Buffer.allocUnsafe(0);
|
|
@@ -151,7 +151,7 @@ class TuyaAccessory extends EventEmitter {
|
|
|
151
151
|
|
|
152
152
|
endingIndex += 4;
|
|
153
153
|
|
|
154
|
-
this._msgQueue.push({msg: this._cachedBuffer.slice(0, endingIndex)});
|
|
154
|
+
this._msgQueue.push({ msg: this._cachedBuffer.slice(0, endingIndex) });
|
|
155
155
|
|
|
156
156
|
this._cachedBuffer = this._cachedBuffer.slice(endingIndex);
|
|
157
157
|
} while (this._cachedBuffer.length);
|
|
@@ -189,7 +189,7 @@ class TuyaAccessory extends EventEmitter {
|
|
|
189
189
|
}
|
|
190
190
|
});
|
|
191
191
|
|
|
192
|
-
this._socket.on('close',
|
|
192
|
+
this._socket.on('close', _err => {
|
|
193
193
|
this.connected = false;
|
|
194
194
|
this.session_key = null;
|
|
195
195
|
//this.log.info('Closed connection with', this.context.name);
|
|
@@ -246,7 +246,7 @@ class TuyaAccessory extends EventEmitter {
|
|
|
246
246
|
const decipher = crypto.createDecipheriv('aes-128-ecb', this.context.key, '');
|
|
247
247
|
decryptedMsg = decipher.update(data.substr(19), 'base64', 'utf8');
|
|
248
248
|
decryptedMsg += decipher.final('utf8');
|
|
249
|
-
} catch(ex) {
|
|
249
|
+
} catch (ex) {
|
|
250
250
|
decryptedMsg = data.substr(19).toString('utf8');
|
|
251
251
|
}
|
|
252
252
|
|
|
@@ -339,7 +339,7 @@ class TuyaAccessory extends EventEmitter {
|
|
|
339
339
|
let data;
|
|
340
340
|
try {
|
|
341
341
|
data = JSON.parse(decryptedMsg);
|
|
342
|
-
} catch(ex) {
|
|
342
|
+
} catch (ex) {
|
|
343
343
|
this.log.info(`Odd message from ${this.context.name} with command ${cmd}:`, decryptedMsg);
|
|
344
344
|
this.log.info(`Raw message from ${this.context.name} (${this.context.version}) with command ${cmd}:`, task.msg.toString('hex'));
|
|
345
345
|
return callback();
|
|
@@ -372,8 +372,8 @@ class TuyaAccessory extends EventEmitter {
|
|
|
372
372
|
|
|
373
373
|
const len = task.msg.length;
|
|
374
374
|
if (len < 16 ||
|
|
375
|
-
|
|
376
|
-
|
|
375
|
+
task.msg.readUInt32BE(0) !== 0x000055aa ||
|
|
376
|
+
task.msg.readUInt32BE(len - 4) !== 0x0000aa55
|
|
377
377
|
) return callback();
|
|
378
378
|
|
|
379
379
|
const size = task.msg.readUInt32BE(12);
|
|
@@ -407,15 +407,15 @@ class TuyaAccessory extends EventEmitter {
|
|
|
407
407
|
decryptedMsg = decipher.update(cleanMsg);
|
|
408
408
|
decipher.final();
|
|
409
409
|
//remove padding
|
|
410
|
-
decryptedMsg = decryptedMsg.slice(0, (decryptedMsg.length - decryptedMsg[decryptedMsg.length-1])
|
|
410
|
+
decryptedMsg = decryptedMsg.slice(0, (decryptedMsg.length - decryptedMsg[decryptedMsg.length - 1]));
|
|
411
411
|
|
|
412
412
|
let parsedPayload;
|
|
413
413
|
try {
|
|
414
414
|
if (decryptedMsg.indexOf(this.context.version) === 0) {
|
|
415
415
|
decryptedMsg = decryptedMsg.slice(15);
|
|
416
416
|
}
|
|
417
|
-
let res =
|
|
418
|
-
if('data' in res) {
|
|
417
|
+
let res = JSON.parse(decryptedMsg);
|
|
418
|
+
if ('data' in res) {
|
|
419
419
|
let resdata = res.data;
|
|
420
420
|
resdata.t = res.t;
|
|
421
421
|
parsedPayload = resdata;//res.data //for compatibility with tuya-mqtt
|
|
@@ -428,7 +428,7 @@ class TuyaAccessory extends EventEmitter {
|
|
|
428
428
|
|
|
429
429
|
if (cmd === 4) { // CommandType.RENAME_GW
|
|
430
430
|
this._tmpRemoteKey = parsedPayload.subarray(0, 16);
|
|
431
|
-
const calcLocalHmac =
|
|
431
|
+
const calcLocalHmac = hmac(this._tmpLocalKey, this.session_key ?? this.context.key).toString('hex');
|
|
432
432
|
const expLocalHmac = parsedPayload.slice(16, 16 + 32).toString('hex');
|
|
433
433
|
if (expLocalHmac !== calcLocalHmac) {
|
|
434
434
|
throw new Error(`HMAC mismatch(keys): expected ${expLocalHmac}, was ${calcLocalHmac}. ${parsedPayload.toString('hex')}`);
|
|
@@ -442,7 +442,7 @@ class TuyaAccessory extends EventEmitter {
|
|
|
442
442
|
clearTimeout(this._socket._connTimeout);
|
|
443
443
|
|
|
444
444
|
this.session_key = Buffer.from(this._tmpLocalKey);
|
|
445
|
-
for(
|
|
445
|
+
for (let i = 0; i < this._tmpLocalKey.length; i++) {
|
|
446
446
|
this.session_key[i] = this._tmpLocalKey[i] ^ this._tmpRemoteKey[i];
|
|
447
447
|
}
|
|
448
448
|
|
|
@@ -514,16 +514,16 @@ class TuyaAccessory extends EventEmitter {
|
|
|
514
514
|
dps
|
|
515
515
|
};
|
|
516
516
|
const data = this.context.version === '3.4'
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
517
|
+
? {
|
|
518
|
+
data: {
|
|
519
|
+
...payload,
|
|
520
|
+
ctype: 0,
|
|
521
|
+
t: undefined
|
|
522
|
+
},
|
|
523
|
+
protocol: 5,
|
|
524
|
+
t
|
|
525
|
+
}
|
|
526
|
+
: payload;
|
|
527
527
|
result = this._send({
|
|
528
528
|
data,
|
|
529
529
|
cmd: this.context.version === '3.4' ? 13 : 7
|
|
@@ -531,7 +531,7 @@ class TuyaAccessory extends EventEmitter {
|
|
|
531
531
|
if (result !== true) this.log.info(' Result', result);
|
|
532
532
|
if (this.context.sendEmptyUpdate) {
|
|
533
533
|
//this.log.info(" Sending", this.context.name, 'empty signature');
|
|
534
|
-
this._send({cmd: this.context.version === '3.4' ? 13 : 7});
|
|
534
|
+
this._send({ cmd: this.context.version === '3.4' ? 13 : 7 });
|
|
535
535
|
}
|
|
536
536
|
} else {
|
|
537
537
|
//this.log.info(`Sending first query to ${this.context.name} (${this.context.version})`);
|
|
@@ -558,7 +558,7 @@ class TuyaAccessory extends EventEmitter {
|
|
|
558
558
|
});
|
|
559
559
|
|
|
560
560
|
if (isNonEmptyPlainObject(changes)) {
|
|
561
|
-
this.state = {...this.state, ...data};
|
|
561
|
+
this.state = { ...this.state, ...data };
|
|
562
562
|
this.emit('change', changes, this.state);
|
|
563
563
|
}
|
|
564
564
|
}
|
|
@@ -573,7 +573,7 @@ class TuyaAccessory extends EventEmitter {
|
|
|
573
573
|
}
|
|
574
574
|
|
|
575
575
|
_send_3_1(o) {
|
|
576
|
-
const {cmd, data} = {...o};
|
|
576
|
+
const { cmd, data } = { ...o };
|
|
577
577
|
|
|
578
578
|
let msg = '';
|
|
579
579
|
|
|
@@ -608,7 +608,7 @@ class TuyaAccessory extends EventEmitter {
|
|
|
608
608
|
}
|
|
609
609
|
|
|
610
610
|
_send_3_3(o) {
|
|
611
|
-
const {cmd, data} = {...o};
|
|
611
|
+
const { cmd, data } = { ...o };
|
|
612
612
|
|
|
613
613
|
// If sending empty dp-update command, we should not increment the sequence
|
|
614
614
|
if (cmd !== 7 || data) this._sendCounter++;
|
|
@@ -654,7 +654,7 @@ class TuyaAccessory extends EventEmitter {
|
|
|
654
654
|
}
|
|
655
655
|
|
|
656
656
|
_send_3_4(o) {
|
|
657
|
-
let {cmd, data} = {...o};
|
|
657
|
+
let { cmd, data } = { ...o };
|
|
658
658
|
|
|
659
659
|
//data
|
|
660
660
|
if (!data) {
|
|
@@ -669,11 +669,11 @@ class TuyaAccessory extends EventEmitter {
|
|
|
669
669
|
}
|
|
670
670
|
|
|
671
671
|
if (cmd !== 10 &&
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
672
|
+
cmd !== 9 &&
|
|
673
|
+
cmd !== 16 &&
|
|
674
|
+
cmd !== 3 &&
|
|
675
|
+
cmd !== 5 &&
|
|
676
|
+
cmd !== 18) {
|
|
677
677
|
// Add 3.4 header
|
|
678
678
|
// check this: mqc_very_pcmcd_mcd(int a1, unsigned int a2)
|
|
679
679
|
const buffer = Buffer.alloc(data.length + 15);
|
|
@@ -682,7 +682,7 @@ class TuyaAccessory extends EventEmitter {
|
|
|
682
682
|
data = buffer;
|
|
683
683
|
}
|
|
684
684
|
|
|
685
|
-
const padding=0x10 - (data.length & 0xf);
|
|
685
|
+
const padding = 0x10 - (data.length & 0xf);
|
|
686
686
|
let buf34 = Buffer.alloc((data.length + padding), padding);
|
|
687
687
|
data.copy(buf34);
|
|
688
688
|
data = buf34;
|
|
@@ -722,7 +722,7 @@ const encrypt34 = (data, encryptKey) => {
|
|
|
722
722
|
};
|
|
723
723
|
|
|
724
724
|
const hmac = (data, hmacKey) => {
|
|
725
|
-
return crypto.createHmac('sha256',hmacKey).update(data, 'utf8').digest();
|
|
725
|
+
return crypto.createHmac('sha256', hmacKey).update(data, 'utf8').digest();
|
|
726
726
|
};
|
|
727
727
|
|
|
728
728
|
const crc32LookupTable = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-tuya-community",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.1",
|
|
4
4
|
"description": "A Homebridge plugin to control Tuya devices locally. Community maintained version of homebridge-tuya that supports new device types.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -46,4 +46,4 @@
|
|
|
46
46
|
"homebridge": "^1.6.0 || ^2.0.0-beta.0",
|
|
47
47
|
"node": "^18.20.4 || ^20.15.1 || ^22"
|
|
48
48
|
}
|
|
49
|
-
}
|
|
49
|
+
}
|