homebridge-tuya-plus 3.14.0-dev.44 → 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.
@@ -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
@@ -226,6 +227,10 @@ class TuyaCloudDevice extends EventEmitter {
226
227
  // makes the catch-up self-cancelling: it costs an HTTP read only on devices that
227
228
  // actually need it.
228
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;
229
234
  if (this._catchupTimers) {
230
235
  this._catchupTimers.forEach(t => clearTimeout(t));
231
236
  this._catchupTimers = null;
@@ -452,10 +457,12 @@ class TuyaCloudDevice extends EventEmitter {
452
457
  // to "opening") shows up in the cloud within ~a second, but the realtime stream
453
458
  // doesn't always carry it — and an accessory that mirrors a status DP (the
454
459
  // garage door) would otherwise sit on the old value forever. So re-read the
455
- // device a couple of times after a command to catch the transition. Each new
456
- // command re-arms it, so a burst of writes only triggers one catch-up; it only
457
- // runs on the cloud path, so a LAN-reachable device pays nothing.
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.
458
464
  _scheduleStateCatchup() {
465
+ if (this._reportsOverMqtt) return;
459
466
  if (this._catchupTimers) this._catchupTimers.forEach(t => clearTimeout(t));
460
467
  this._catchupTimers = [2000, 8000].map(delay => {
461
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.44",
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": {
@@ -355,6 +355,18 @@ describe('TuyaCloudDevice', () => {
355
355
  dev.stop();
356
356
  });
357
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
+
358
370
  test('a refresh reads via the shadow, so a thing-model device whose /status is empty still updates', async () => {
359
371
  const api = makeApi();
360
372
  let rs = 13;