homebridge-tuya-plus 3.14.0-dev.43 → 3.14.0-dev.45
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/lib/TuyaCloudDevice.js +26 -4
- package/package.json +1 -1
- package/test/TuyaCloudDevice.test.js +25 -0
package/lib/TuyaCloudDevice.js
CHANGED
|
@@ -77,6 +77,7 @@ class TuyaCloudDevice extends EventEmitter {
|
|
|
77
77
|
this._announcedProperties = false;
|
|
78
78
|
this._catchupTimers = null; // post-write state re-reads (see _scheduleStateCatchup)
|
|
79
79
|
this._writeChain = null; // serializes cloud writes to this device (see update)
|
|
80
|
+
this._reportsOverMqtt = false; // set once any realtime update arrives; disables the catch-up
|
|
80
81
|
|
|
81
82
|
// Bidirectional data-point maps, learned from the device's thing-shadow on
|
|
82
83
|
// connect. They let this cloud transport (and the LAN+cloud TuyaDevice that
|
|
@@ -215,7 +216,26 @@ class TuyaCloudDevice extends EventEmitter {
|
|
|
215
216
|
// first 'online' is caught. On any (re)connect we re-read the full state
|
|
216
217
|
// to recover anything missed while the stream was down.
|
|
217
218
|
this.messaging.on('online', () => this._refreshState());
|
|
218
|
-
this.messaging.subscribeDevice(this.context.id, status => this.
|
|
219
|
+
this.messaging.subscribeDevice(this.context.id, status => this._onRealtime(status));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// A realtime (MQTT) update arrived for this device, so it reports its changes
|
|
223
|
+
// over the stream and the post-write catch-up read is redundant — drop any
|
|
224
|
+
// pending one. A device whose reports never arrive over MQTT (some custom
|
|
225
|
+
// controllers — the stream carries nothing for them) gets none of these, so its
|
|
226
|
+
// catch-up stays armed and remains the only way its state reaches HomeKit. This
|
|
227
|
+
// makes the catch-up self-cancelling: it costs an HTTP read only on devices that
|
|
228
|
+
// actually need it.
|
|
229
|
+
_onRealtime(status) {
|
|
230
|
+
// This device reports over MQTT, so the catch-up is pure overhead for it —
|
|
231
|
+
// latch it off for good and drop any pending one. Only devices that never
|
|
232
|
+
// report here (the stream carries nothing for them) keep the catch-up.
|
|
233
|
+
this._reportsOverMqtt = true;
|
|
234
|
+
if (this._catchupTimers) {
|
|
235
|
+
this._catchupTimers.forEach(t => clearTimeout(t));
|
|
236
|
+
this._catchupTimers = null;
|
|
237
|
+
}
|
|
238
|
+
this._applyStatus(status);
|
|
219
239
|
}
|
|
220
240
|
|
|
221
241
|
async _refreshState() {
|
|
@@ -437,10 +457,12 @@ class TuyaCloudDevice extends EventEmitter {
|
|
|
437
457
|
// to "opening") shows up in the cloud within ~a second, but the realtime stream
|
|
438
458
|
// doesn't always carry it — and an accessory that mirrors a status DP (the
|
|
439
459
|
// garage door) would otherwise sit on the old value forever. So re-read the
|
|
440
|
-
// device a couple of times after a command to catch the transition.
|
|
441
|
-
//
|
|
442
|
-
//
|
|
460
|
+
// device a couple of times after a command to catch the transition. Strictly
|
|
461
|
+
// supplemental: a device that has ever reported over MQTT skips this entirely
|
|
462
|
+
// (realtime is its path), each new command re-arms it, and it only runs on the
|
|
463
|
+
// cloud path — so a LAN-reachable or MQTT-covered device pays nothing.
|
|
443
464
|
_scheduleStateCatchup() {
|
|
465
|
+
if (this._reportsOverMqtt) return;
|
|
444
466
|
if (this._catchupTimers) this._catchupTimers.forEach(t => clearTimeout(t));
|
|
445
467
|
this._catchupTimers = [2000, 8000].map(delay => {
|
|
446
468
|
const t = setTimeout(() => this._catchupOnce(), delay);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-tuya-plus",
|
|
3
|
-
"version": "3.14.0-dev.
|
|
3
|
+
"version": "3.14.0-dev.45",
|
|
4
4
|
"description": "A community-maintained Homebridge plugin for controlling Tuya devices in HomeKit. LAN-first, with an optional Tuya Cloud fallback for any device the LAN can't reach.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -342,6 +342,31 @@ describe('TuyaCloudDevice', () => {
|
|
|
342
342
|
dev.stop();
|
|
343
343
|
});
|
|
344
344
|
|
|
345
|
+
test('a realtime update cancels the pending catch-up (no redundant read on MQTT-covered devices)', async () => {
|
|
346
|
+
const api = makeApi();
|
|
347
|
+
api.getShadowProperties = jest.fn().mockResolvedValue([{code: 'switch_1', dp_id: 1, value: false}]);
|
|
348
|
+
const dev = makeDevice(api, null, {key: 'abc'});
|
|
349
|
+
await dev._connect();
|
|
350
|
+
await dev.update({'1': true});
|
|
351
|
+
expect(Array.isArray(dev._catchupTimers)).toBe(true);
|
|
352
|
+
|
|
353
|
+
dev._onRealtime([{code: 'switch_1', value: true}]); // MQTT delivers → catch-up not needed
|
|
354
|
+
expect(dev._catchupTimers).toBeNull();
|
|
355
|
+
dev.stop();
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
test('once a device has reported over MQTT, later writes never arm a catch-up', async () => {
|
|
359
|
+
const api = makeApi();
|
|
360
|
+
api.getShadowProperties = jest.fn().mockResolvedValue([{code: 'switch_1', dp_id: 1, value: false}]);
|
|
361
|
+
const dev = makeDevice(api, null, {key: 'abc'});
|
|
362
|
+
await dev._connect();
|
|
363
|
+
dev._onRealtime([{code: 'switch_1', value: true}]); // device reports over MQTT → latch
|
|
364
|
+
|
|
365
|
+
await dev.update({'1': false});
|
|
366
|
+
expect(dev._catchupTimers).toBeNull(); // strictly supplemental: no read for MQTT-covered devices
|
|
367
|
+
dev.stop();
|
|
368
|
+
});
|
|
369
|
+
|
|
345
370
|
test('a refresh reads via the shadow, so a thing-model device whose /status is empty still updates', async () => {
|
|
346
371
|
const api = makeApi();
|
|
347
372
|
let rs = 13;
|