homebridge-tuya-plus 3.14.0-dev.18 → 3.14.0-dev.19

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.
@@ -318,7 +318,7 @@
318
318
  "title": "Sync brightness to WLED (IP[:port])",
319
319
  "placeholder": "192.168.1.100",
320
320
  "condition": {
321
- "functionBody": "return model.devices && model.devices[arrayIndices] && ['WledDimmer', 'SimpleDimmer'].includes(model.devices[arrayIndices].type);"
321
+ "functionBody": "return model.devices && model.devices[arrayIndices] && ['WledDimmer'].includes(model.devices[arrayIndices].type);"
322
322
  }
323
323
  },
324
324
  "dpColorTemperature": {
package/index.js CHANGED
@@ -18,6 +18,7 @@ const ConvectorAccessory = require('./lib/ConvectorAccessory');
18
18
  const GarageDoorAccessory = require('./lib/GarageDoorAccessory');
19
19
  const SimpleGarageDoorAccessory = require('./lib/SimpleGarageDoorAccessory');
20
20
  const WledDimmerAccessory = require('./lib/WledDimmerAccessory');
21
+ const SimpleDimmerAccessory = require('./lib/SimpleDimmerAccessory');
21
22
  const SimpleDimmer2Accessory = require('./lib/SimpleDimmer2Accessory');
22
23
  const SimpleBlindsAccessory = require('./lib/SimpleBlindsAccessory');
23
24
  const SimpleHeaterAccessory = require('./lib/SimpleHeaterAccessory');
@@ -60,7 +61,7 @@ const CLASS_DEF = {
60
61
  convector: ConvectorAccessory,
61
62
  garagedoor: GarageDoorAccessory,
62
63
  simplegaragedoor: SimpleGarageDoorAccessory,
63
- simpledimmer: WledDimmerAccessory,
64
+ simpledimmer: SimpleDimmerAccessory,
64
65
  wleddimmer: WledDimmerAccessory,
65
66
  simpledimmer2: SimpleDimmer2Accessory,
66
67
  simpleblinds: SimpleBlindsAccessory,
@@ -25,20 +25,24 @@ class SimpleDimmerAccessory extends BaseAccessory {
25
25
  this.dpPower = this._getCustomDP(this.device.context.dpPower) || '1';
26
26
  this.dpBrightness = this._getCustomDP(this.device.context.dpBrightness) || this._getCustomDP(this.device.context.dp) || '2';
27
27
 
28
- const characteristicOn = service.getCharacteristic(Characteristic.On)
28
+ this._characteristicOn = service.getCharacteristic(Characteristic.On)
29
29
  .updateValue(dps[this.dpPower])
30
30
  .onGet(() => this.getStateAsync(this.dpPower))
31
31
  .onSet(value => this.setStateAsync(this.dpPower, value));
32
32
 
33
- const characteristicBrightness = service.getCharacteristic(Characteristic.Brightness)
33
+ this._characteristicBrightness = service.getCharacteristic(Characteristic.Brightness)
34
34
  .updateValue(this.convertBrightnessFromTuyaToHomeKit(dps[this.dpBrightness]))
35
35
  .onGet(() => this.getBrightness())
36
36
  .onSet(value => this.setBrightness(value));
37
37
 
38
+ this._registerChangeListener();
39
+ }
40
+
41
+ _registerChangeListener() {
38
42
  this.device.on('change', changes => {
39
- if (changes.hasOwnProperty(this.dpPower) && characteristicOn.value !== changes[this.dpPower]) characteristicOn.updateValue(changes[this.dpPower]);
40
- if (changes.hasOwnProperty(this.dpBrightness) && this.convertBrightnessFromHomeKitToTuya(characteristicBrightness.value) !== changes[this.dpBrightness])
41
- characteristicBrightness.updateValue(this.convertBrightnessFromTuyaToHomeKit(changes[this.dpBrightness]));
43
+ if (changes.hasOwnProperty(this.dpPower) && this._characteristicOn.value !== changes[this.dpPower]) this._characteristicOn.updateValue(changes[this.dpPower]);
44
+ if (changes.hasOwnProperty(this.dpBrightness) && this.convertBrightnessFromHomeKitToTuya(this._characteristicBrightness.value) !== changes[this.dpBrightness])
45
+ this._characteristicBrightness.updateValue(this.convertBrightnessFromTuyaToHomeKit(changes[this.dpBrightness]));
42
46
  });
43
47
  }
44
48
 
@@ -197,7 +197,7 @@ class SimpleGarageDoorAccessory extends BaseAccessory {
197
197
  if (this.pendingCloseTimer) return;
198
198
  const isOpen = this._mapDpState(changes[this.dpState]);
199
199
  if (isOpen === null) {
200
- this.log.info(`[SimpleGarageDoor] ${this.device.context.name}: ignoring unknown state DP value ${JSON.stringify(changes[this.dpState])}`);
200
+ this.log.debug(`[SimpleGarageDoor] ${this.device.context.name}: ignoring unknown state DP value ${JSON.stringify(changes[this.dpState])}`);
201
201
  return;
202
202
  }
203
203
  this._applyReportedState(isOpen);
@@ -266,7 +266,7 @@ class SimpleGarageDoorAccessory extends BaseAccessory {
266
266
  // own, even mid-close. Abandon any pending stop-before-close.
267
267
  _sendOpen() {
268
268
  this._cancelPendingClose();
269
- this.log.info(`[SimpleGarageDoor] ${this.device.context.name}: open (dp${this.dpOpen})`);
269
+ this.log.debug(`[SimpleGarageDoor] ${this.device.context.name}: open (dp${this.dpOpen})`);
270
270
  this.setMultiStateLegacyInBackground({[this.dpOpen]: true});
271
271
  }
272
272
 
@@ -278,19 +278,19 @@ class SimpleGarageDoorAccessory extends BaseAccessory {
278
278
  if (this.pendingCloseTimer) {
279
279
  // A stop-before-close is already running — don't restart it (and
280
280
  // push the close out) on a duplicate or retransmitted request.
281
- this.log.info(`[SimpleGarageDoor] ${name}: close ignored — a stop-before-close is already running`);
281
+ this.log.debug(`[SimpleGarageDoor] ${name}: close ignored — a stop-before-close is already running`);
282
282
  return;
283
283
  }
284
284
  if (this._isStopped()) {
285
- this.log.info(`[SimpleGarageDoor] ${name}: close (dp${this.dpClose}) — gate already stopped`);
285
+ this.log.debug(`[SimpleGarageDoor] ${name}: close (dp${this.dpClose}) — gate already stopped`);
286
286
  this.setMultiStateLegacyInBackground({[this.dpClose]: true});
287
287
  return;
288
288
  }
289
- this.log.info(`[SimpleGarageDoor] ${name}: stop-before-close — stop (dp${this.dpStop}) now, close (dp${this.dpClose}) in ${this.stopBeforeCloseMs}ms`);
289
+ this.log.debug(`[SimpleGarageDoor] ${name}: stop-before-close — stop (dp${this.dpStop}) now, close (dp${this.dpClose}) in ${this.stopBeforeCloseMs}ms`);
290
290
  this.setMultiStateLegacyInBackground({[this.dpStop]: true});
291
291
  this.pendingCloseTimer = setTimeout(() => {
292
292
  this.pendingCloseTimer = null;
293
- this.log.info(`[SimpleGarageDoor] ${name}: stop-before-close — firing close (dp${this.dpClose})`);
293
+ this.log.debug(`[SimpleGarageDoor] ${name}: stop-before-close — firing close (dp${this.dpClose})`);
294
294
  this.setMultiStateLegacyInBackground({[this.dpClose]: true});
295
295
  }, this.stopBeforeCloseMs);
296
296
  }
@@ -323,15 +323,15 @@ class SimpleGarageDoorAccessory extends BaseAccessory {
323
323
  // Re-entrant press while a partial is already armed (e.g. a
324
324
  // HomeKit/iOS WRITE retransmit) — ignore so the stop isn't pushed
325
325
  // out, which would let the gate run further than intended.
326
- this.log.info(`[SimpleGarageDoor] ${name}: partial press ignored — a partial open is already running`);
326
+ this.log.debug(`[SimpleGarageDoor] ${name}: partial press ignored — a partial open is already running`);
327
327
  return;
328
328
  }
329
329
 
330
- this.log.info(`[SimpleGarageDoor] ${name}: partial open — opening, will stop in ${this.partialOpenMs}ms`);
330
+ this.log.debug(`[SimpleGarageDoor] ${name}: partial open — opening, will stop in ${this.partialOpenMs}ms`);
331
331
  this._applyTarget(Characteristic.TargetDoorState.OPEN);
332
332
  this.partialStopTimer = setTimeout(() => {
333
333
  this.partialStopTimer = null;
334
- this.log.info(`[SimpleGarageDoor] ${name}: partial open — firing stop (dp${this.dpStop})`);
334
+ this.log.debug(`[SimpleGarageDoor] ${name}: partial open — firing stop (dp${this.dpStop})`);
335
335
  this.setMultiStateLegacyInBackground({[this.dpStop]: true});
336
336
  }, this.partialOpenMs);
337
337
  }
@@ -55,7 +55,7 @@ class ValveAccessory extends BaseAccessory {
55
55
  service.getCharacteristic(Characteristic.SetDuration)
56
56
  .onGet(() => { if (!this.device.connected) throw this._commError(); return this.setDuration; })
57
57
  .on('change', (data) => {
58
- this.log.info('Water Valve Time Duration Set to: ' + data.newValue / 60 + ' Minutes');
58
+ this.log.debug('Water Valve Time Duration Set to: ' + data.newValue / 60 + ' Minutes');
59
59
  this.setDuration = data.newValue;
60
60
 
61
61
  if (service.getCharacteristic(Characteristic.InUse).value) {
@@ -65,7 +65,7 @@ class ValveAccessory extends BaseAccessory {
65
65
 
66
66
  clearTimeout(this.timer);
67
67
  this.timer = setTimeout(() => {
68
- this.log.info('Water Valve Timer Expired. Shutting OFF Valve');
68
+ this.log.debug('Water Valve Timer Expired. Shutting OFF Valve');
69
69
  this.setStateInBackground(this.dpPower, 0);
70
70
  service.getCharacteristic(Characteristic.Active).updateValue(0);
71
71
  service.getCharacteristic(Characteristic.InUse).updateValue(0);
@@ -89,16 +89,16 @@ class ValveAccessory extends BaseAccessory {
89
89
  service.getCharacteristic(Characteristic.RemainingDuration).updateValue(0);
90
90
  service.getCharacteristic(Characteristic.Active).updateValue(0);
91
91
  clearTimeout(this.timer);
92
- this.log.info('Water Valve is OFF!');
92
+ this.log.debug('Water Valve is OFF!');
93
93
  break;
94
94
  case 1:
95
95
  this.lastActivationTime = (new Date()).getTime();
96
96
  service.getCharacteristic(Characteristic.RemainingDuration).updateValue(this.setDuration);
97
97
  service.getCharacteristic(Characteristic.Active).updateValue(1);
98
- this.log.info('Water Valve Turning ON with Timer Set to: ' + this.setDuration / 60 + ' Minutes');
98
+ this.log.debug('Water Valve Turning ON with Timer Set to: ' + this.setDuration / 60 + ' Minutes');
99
99
  clearTimeout(this.timer);
100
100
  this.timer = setTimeout(() => {
101
- this.log.info('Water Valve Timer Expired. Shutting OFF Valve');
101
+ this.log.debug('Water Valve Timer Expired. Shutting OFF Valve');
102
102
  this.setStateInBackground(this.dpPower, 0);
103
103
  service.getCharacteristic(Characteristic.Active).updateValue(0);
104
104
  service.getCharacteristic(Characteristic.InUse).updateValue(0);
@@ -113,10 +113,10 @@ class ValveAccessory extends BaseAccessory {
113
113
  service.getCharacteristic(Characteristic.RemainingDuration).updateValue(this.setDuration);
114
114
  service.getCharacteristic(Characteristic.Active).updateValue(1);
115
115
  service.getCharacteristic(Characteristic.InUse).updateValue(1);
116
- this.log.info('Water Valve is ON After Restart. Setting Timer to: ' + this.setDuration / 60 + ' Minutes');
116
+ this.log.debug('Water Valve is ON After Restart. Setting Timer to: ' + this.setDuration / 60 + ' Minutes');
117
117
  clearTimeout(this.timer);
118
118
  this.timer = setTimeout(() => {
119
- this.log.info('Water Valve Timer Expired. Shutting OFF Valve');
119
+ this.log.debug('Water Valve Timer Expired. Shutting OFF Valve');
120
120
  this.setStateInBackground(this.dpPower, 0);
121
121
  service.getCharacteristic(Characteristic.Active).updateValue(0);
122
122
  this.lastActivationTime = null;
@@ -1,4 +1,4 @@
1
- const BaseAccessory = require('./BaseAccessory');
1
+ const SimpleDimmerAccessory = require('./SimpleDimmerAccessory');
2
2
  const http = require('http');
3
3
  const maxWledBrightness = 255;
4
4
 
@@ -6,30 +6,14 @@ const maxWledBrightness = 255;
6
6
  const wledDebounceMs = 50;
7
7
  const wledWarmupMs = 8000;
8
8
 
9
- class WledDimmerAccessory extends BaseAccessory {
10
- static getCategory(Categories) {
11
- return Categories.LIGHTBULB;
12
- }
13
-
14
- constructor(...props) {
15
- super(...props);
16
- }
17
-
18
- _registerPlatformAccessory() {
19
- const {Service} = this.hap;
20
-
21
- this.accessory.addService(Service.Lightbulb, this.device.context.name);
22
-
23
- super._registerPlatformAccessory();
24
- }
25
-
9
+ class WledDimmerAccessory extends SimpleDimmerAccessory {
26
10
  _registerCharacteristics(dps) {
27
- const {Service, Characteristic} = this.hap;
28
- const service = this.accessory.getService(Service.Lightbulb);
29
- this._checkServiceName(service, this.device.context.name);
30
-
31
- this.dpPower = this._getCustomDP(this.device.context.dpPower) || '1';
32
- this.dpBrightness = this._getCustomDP(this.device.context.dpBrightness) || this._getCustomDP(this.device.context.dp) || '2';
11
+ // The Lightbulb service, On/Brightness characteristics, default DPs and
12
+ // the device 'change' listener are all set up by SimpleDimmerAccessory.
13
+ // WLED only layers optional brightness sync and preset-effect switches
14
+ // on top, so without syncBrightnessToWled configured this behaves
15
+ // exactly like a plain SimpleDimmer.
16
+ super._registerCharacteristics(dps);
33
17
 
34
18
  // State for debounced / delayed WLED updates
35
19
  this._wledPendingBri = null;
@@ -47,32 +31,20 @@ class WledDimmerAccessory extends BaseAccessory {
47
31
  null;
48
32
  this.syncBrightnessToWled = (syncCfg && ('' + syncCfg).trim()) || null;
49
33
 
50
- this.log.info(
34
+ this.log.debug(
51
35
  '[WLED Sync] %s: syncBrightnessToWled=%s',
52
36
  this.device.context.name,
53
37
  this.syncBrightnessToWled || 'disabled'
54
38
  );
55
39
 
56
- const characteristicOn = service.getCharacteristic(Characteristic.On)
57
- .updateValue(dps[this.dpPower])
58
- .onGet(() => this.getStateAsync(this.dpPower))
59
- .onSet(value => this.setStateAsync(this.dpPower, value));
60
-
61
- const characteristicBrightness = service.getCharacteristic(Characteristic.Brightness);
62
- // Keep a reference so we can update brightness from background WLED calls
63
- this._characteristicBrightness = characteristicBrightness;
64
-
65
40
  if (this.syncBrightnessToWled) {
66
41
  // Start with 100% locally while we fetch the actual WLED brightness
67
- characteristicBrightness
68
- .updateValue(100)
69
- .onGet(() => this.getBrightness())
70
- .onSet(value => this.setBrightness(value));
42
+ this._characteristicBrightness.updateValue(100);
71
43
 
72
44
  // Force Tuya dimmer brightness to 100% on startup
73
45
  const maxTuyaBrightness = this.convertBrightnessFromHomeKitToTuya(100);
74
46
  if (dps[this.dpBrightness] !== maxTuyaBrightness) {
75
- this.log.info(
47
+ this.log.debug(
76
48
  '[WLED Sync] %s: setting Tuya brightness DP%s to max (%s)',
77
49
  this.device.context.name,
78
50
  this.dpBrightness,
@@ -80,19 +52,6 @@ class WledDimmerAccessory extends BaseAccessory {
80
52
  );
81
53
  this.setState(this.dpBrightness, maxTuyaBrightness, () => {});
82
54
  }
83
- } else {
84
- const initial = this.convertBrightnessFromTuyaToHomeKit(dps[this.dpBrightness]);
85
- this.log.debug(
86
- '%s: initial Tuya brightness DP%s=%s -> HomeKit %s%%',
87
- this.device.context.name,
88
- this.dpBrightness,
89
- dps[this.dpBrightness],
90
- initial
91
- );
92
- characteristicBrightness
93
- .updateValue(initial)
94
- .onGet(() => this.getBrightness())
95
- .onSet(value => this.setBrightness(value));
96
55
  }
97
56
 
98
57
  // Optional: create one HomeKit Switch per configured WLED preset effect.
@@ -148,7 +107,7 @@ class WledDimmerAccessory extends BaseAccessory {
148
107
  return callback();
149
108
  }
150
109
 
151
- this.log.info(
110
+ this.log.debug(
152
111
  '[WLED Sync] %s: setting preset effect "%s" (id=%s, index=%s)',
153
112
  this.device.context.name,
154
113
  effectName,
@@ -212,15 +171,17 @@ class WledDimmerAccessory extends BaseAccessory {
212
171
  .filter(service => service.UUID === HapService.Switch.UUID && service.subtype && service.subtype.startsWith('wledEffect '))
213
172
  .forEach(service => {
214
173
  if (!validEffectServices.includes(service)) {
215
- this.log.info('Removing', service.displayName);
174
+ this.log.debug('Removing', service.displayName);
216
175
  this.accessory.removeService(service);
217
176
  }
218
177
  });
219
178
  }
179
+ }
220
180
 
181
+ _registerChangeListener() {
221
182
  this.device.on('change', changes => {
222
183
  // Log full DPS changes so we can see exactly what Tuya reported
223
- this.log.info(
184
+ this.log.debug(
224
185
  '%s DPS changes: %s %s was:',
225
186
  this.device.context.name,
226
187
  JSON.stringify(changes),
@@ -229,10 +190,10 @@ class WledDimmerAccessory extends BaseAccessory {
229
190
  );
230
191
 
231
192
  if (changes.hasOwnProperty(this.dpPower)) {
232
- const oldPower = !!characteristicOn.value;
193
+ const oldPower = !!this._characteristicOn.value;
233
194
  const newPower = !!changes[this.dpPower];
234
195
 
235
- this.log.info(
196
+ this.log.debug(
236
197
  '%s power change detected on DP%s: %s -> %s',
237
198
  this.device.context.name,
238
199
  this.dpPower,
@@ -241,13 +202,13 @@ class WledDimmerAccessory extends BaseAccessory {
241
202
  );
242
203
 
243
204
  if (oldPower !== newPower) {
244
- characteristicOn.updateValue(newPower);
205
+ this._characteristicOn.updateValue(newPower);
245
206
  }
246
207
 
247
208
  // Track warmup window for WLED after power ON
248
209
  if (this.syncBrightnessToWled && newPower) {
249
210
  this._wledReadyAt = Date.now() + wledWarmupMs;
250
- this.log.info(
211
+ this.log.debug(
251
212
  '[WLED Sync] %s: power ON -> delaying WLED API calls for %sms',
252
213
  this.device.context.name,
253
214
  wledWarmupMs
@@ -271,7 +232,7 @@ class WledDimmerAccessory extends BaseAccessory {
271
232
 
272
233
  // Ignore the "forced back to 100%" update to avoid feedback loops
273
234
  if (tuyaValue === maxTuyaBrightness) {
274
- this.log.info(
235
+ this.log.debug(
275
236
  '[WLED Sync] %s: Tuya DP%s reported max brightness (%s), ignoring',
276
237
  this.device.context.name,
277
238
  this.dpBrightness,
@@ -283,7 +244,7 @@ class WledDimmerAccessory extends BaseAccessory {
283
244
  const percent = this.convertBrightnessFromTuyaToHomeKit(tuyaValue);
284
245
  const bri = Math.max(0, Math.min(maxWledBrightness, Math.round((percent / 100) * maxWledBrightness)));
285
246
 
286
- this.log.info(
247
+ this.log.debug(
287
248
  '[WLED Sync] %s: Tuya DP%s changed to %s -> %s%% -> WLED bri=%s (debounced)',
288
249
  this.device.context.name,
289
250
  this.dpBrightness,
@@ -304,14 +265,14 @@ class WledDimmerAccessory extends BaseAccessory {
304
265
  }
305
266
 
306
267
  // Reflect that brightness back into HomeKit
307
- characteristicBrightness.updateValue(percent);
268
+ this._characteristicBrightness.updateValue(percent);
308
269
 
309
270
  // After a short delay, force Tuya brightness back to 100% so it stops dimming the strip
310
271
  if (this._wledForceMaxTimeout) {
311
272
  clearTimeout(this._wledForceMaxTimeout);
312
273
  }
313
274
  this._wledForceMaxTimeout = setTimeout(() => {
314
- this.log.info(
275
+ this.log.debug(
315
276
  '[WLED Sync] %s: forcing Tuya DP%s back to max (%s) after Tuya app change',
316
277
  this.device.context.name,
317
278
  this.dpBrightness,
@@ -320,8 +281,8 @@ class WledDimmerAccessory extends BaseAccessory {
320
281
  this.setState(this.dpBrightness, maxTuyaBrightness, () => {});
321
282
  }, 5000);
322
283
  });
323
- } else if (!this.syncBrightnessToWled && changes.hasOwnProperty(this.dpBrightness) && this.convertBrightnessFromHomeKitToTuya(characteristicBrightness.value) !== changes[this.dpBrightness]) {
324
- characteristicBrightness.updateValue(this.convertBrightnessFromTuyaToHomeKit(changes[this.dpBrightness]));
284
+ } else if (!this.syncBrightnessToWled && changes.hasOwnProperty(this.dpBrightness) && this.convertBrightnessFromHomeKitToTuya(this._characteristicBrightness.value) !== changes[this.dpBrightness]) {
285
+ this._characteristicBrightness.updateValue(this.convertBrightnessFromTuyaToHomeKit(changes[this.dpBrightness]));
325
286
  }
326
287
  });
327
288
  }
@@ -331,7 +292,7 @@ class WledDimmerAccessory extends BaseAccessory {
331
292
  if (this.syncBrightnessToWled) {
332
293
  // If we already have a cached WLED brightness, return it immediately.
333
294
  if (this._lastWledPercent != null) {
334
- this.log.info(
295
+ this.log.debug(
335
296
  '[WLED Sync] %s: getBrightness() -> using cached %s%%',
336
297
  this.device.context.name,
337
298
  this._lastWledPercent
@@ -340,13 +301,14 @@ class WledDimmerAccessory extends BaseAccessory {
340
301
  }
341
302
  return 50;
342
303
  } else {
343
- return this.convertBrightnessFromTuyaToHomeKit(this.device.state[this.dpBrightness]);
304
+ // No WLED sync: behave as a plain SimpleDimmer.
305
+ return super.getBrightness();
344
306
  }
345
307
  }
346
308
 
347
309
  setBrightness(value) {
348
310
  if (this.syncBrightnessToWled) {
349
- this.log.info(
311
+ this.log.debug(
350
312
  '[WLED Sync] %s: setBrightness(%s%%)',
351
313
  this.device.context.name,
352
314
  value
@@ -358,7 +320,7 @@ class WledDimmerAccessory extends BaseAccessory {
358
320
  // Ensure Tuya stays at 100% so it doesn't interfere with WLED.
359
321
  const maxTuyaBrightness = this.convertBrightnessFromHomeKitToTuya(100);
360
322
  if (this.device.state[this.dpBrightness] !== maxTuyaBrightness) {
361
- this.log.info(
323
+ this.log.debug(
362
324
  '[WLED Sync] %s: ensuring Tuya DP%s=%s (max)',
363
325
  this.device.context.name,
364
326
  this.dpBrightness,
@@ -370,7 +332,7 @@ class WledDimmerAccessory extends BaseAccessory {
370
332
 
371
333
  // Compute desired WLED brightness once.
372
334
  const bri = Math.max(0, Math.min(maxWledBrightness, Math.round((value / 100) * maxWledBrightness)));
373
- this.log.info(
335
+ this.log.debug(
374
336
  '[WLED Sync] %s: mapped HomeKit %s%% -> WLED bri=%s (debounced background)',
375
337
  this.device.context.name,
376
338
  value,
@@ -383,7 +345,8 @@ class WledDimmerAccessory extends BaseAccessory {
383
345
  // Return to HomeKit immediately; don't wait for network I/O.
384
346
  return null;
385
347
  } else {
386
- return this.setStateAsync(this.dpBrightness, this.convertBrightnessFromHomeKitToTuya(value));
348
+ // No WLED sync: behave as a plain SimpleDimmer.
349
+ return super.setBrightness(value);
387
350
  }
388
351
  }
389
352
 
@@ -392,7 +355,7 @@ class WledDimmerAccessory extends BaseAccessory {
392
355
  const parts = String(this.syncBrightnessToWled).split(':');
393
356
  const host = parts[0];
394
357
  const port = parts[1] ? parseInt(parts[1], 10) || 80 : 80;
395
- this.log.info(
358
+ this.log.debug(
396
359
  '[WLED Sync] %s: target host=%s port=%s',
397
360
  this.device.context.name,
398
361
  host,
@@ -426,7 +389,7 @@ class WledDimmerAccessory extends BaseAccessory {
426
389
  timeout: 1000
427
390
  };
428
391
 
429
- this.log.info(
392
+ this.log.debug(
430
393
  '[WLED Sync] %s: GET http://%s:%s/json/state',
431
394
  this.device.context.name,
432
395
  target.host,
@@ -449,7 +412,7 @@ class WledDimmerAccessory extends BaseAccessory {
449
412
  );
450
413
  return done(true);
451
414
  }
452
- this.log.info(
415
+ this.log.debug(
453
416
  '[WLED Sync] %s: /json/state -> bri=%s',
454
417
  this.device.context.name,
455
418
  bri
@@ -508,7 +471,7 @@ class WledDimmerAccessory extends BaseAccessory {
508
471
  timeout: 1000
509
472
  };
510
473
 
511
- this.log.info(
474
+ this.log.debug(
512
475
  '[WLED Sync] %s: POST http://%s:%s/json/state body=%s',
513
476
  this.device.context.name,
514
477
  target.host,
@@ -520,7 +483,7 @@ class WledDimmerAccessory extends BaseAccessory {
520
483
  // Consume response and ignore body
521
484
  res.on('data', () => {});
522
485
  res.on('end', () => {
523
- this.log.info(
486
+ this.log.debug(
524
487
  '[WLED Sync] %s: WLED brightness set, HTTP %s',
525
488
  this.device.context.name,
526
489
  res.statusCode
@@ -583,7 +546,7 @@ class WledDimmerAccessory extends BaseAccessory {
583
546
  timeout: 1000
584
547
  };
585
548
 
586
- this.log.info(
549
+ this.log.debug(
587
550
  '[WLED Sync] %s: POST http://%s:%s/json/state body=%s (preset effect)',
588
551
  this.device.context.name,
589
552
  target.host,
@@ -595,7 +558,7 @@ class WledDimmerAccessory extends BaseAccessory {
595
558
  // Consume response and ignore body
596
559
  res.on('data', () => {});
597
560
  res.on('end', () => {
598
- this.log.info(
561
+ this.log.debug(
599
562
  '[WLED Sync] %s: WLED effect set to %s, HTTP %s',
600
563
  this.device.context.name,
601
564
  effectId,
@@ -659,7 +622,7 @@ class WledDimmerAccessory extends BaseAccessory {
659
622
  const warmupDelay = this._wledReadyAt && now < this._wledReadyAt ? (this._wledReadyAt - now) : 0;
660
623
  const delay = warmupDelay + wledDebounceMs;
661
624
 
662
- this.log.info(
625
+ this.log.debug(
663
626
  '[WLED Sync] %s: scheduling WLED bri=%s in %sms (%s)',
664
627
  this.device.context.name,
665
628
  brightness,
@@ -672,7 +635,7 @@ class WledDimmerAccessory extends BaseAccessory {
672
635
 
673
636
  // If the light is now off, skip the call.
674
637
  if (!this.device.state[this.dpPower]) {
675
- this.log.info(
638
+ this.log.debug(
676
639
  '[WLED Sync] %s: skipping scheduled WLED bri=%s because power is OFF',
677
640
  this.device.context.name,
678
641
  this._wledPendingBri
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homebridge-tuya-plus",
3
- "version": "3.14.0-dev.18",
3
+ "version": "3.14.0-dev.19",
4
4
  "description": "A community-maintained Homebridge plugin for controlling Tuya devices locally over LAN. Includes new features, fixes, and updated device support.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -23,6 +23,7 @@ const CLASS_DEF = {
23
23
  garagedoor: require('../lib/GarageDoorAccessory'),
24
24
  simplegaragedoor: require('../lib/SimpleGarageDoorAccessory'),
25
25
  simpledimmer: require('../lib/SimpleDimmerAccessory'),
26
+ wleddimmer: require('../lib/WledDimmerAccessory'),
26
27
  simpledimmer2: require('../lib/SimpleDimmer2Accessory'),
27
28
  simpleblinds: require('../lib/SimpleBlindsAccessory'),
28
29
  simpleheater: require('../lib/SimpleHeaterAccessory'),
@@ -15,7 +15,8 @@ If you are looking for verified configurations for your specific device, please
15
15
  |Barely Smart Power Strip|`Outlet`|Smart power strips that don't allow individual control of the outlets|
16
16
  |Air Conditioner|`AirConditioner`<sup>[6](#air-conditioners)</sup>|Cooling and heating devices <small>([instructions](#air-conditioners))</small>|
17
17
  |Heat Convector|`Convector`<sup>[7](#heat-convectors)</sup>|Heating panels <small>([instructions](#heat-convectors))</small>|
18
- |WLED Dimmer|`WledDimmer` (or legacy `SimpleDimmer`)<sup>[8](#simple-dimmers)</sup>|Dimmer switches with power control, with optional WLED sync <small>([instructions](#simple-dimmers))</small>|
18
+ |Simple Dimmer|`SimpleDimmer`<sup>[8](#simple-dimmers)</sup>|Dimmer switches with power control <small>([instructions](#simple-dimmers))</small>|
19
+ |WLED Dimmer|`WledDimmer`<sup>[8](#simple-dimmers)</sup>|Dimmer switches with power control, plus optional WLED brightness sync and preset-effect switches <small>([instructions](#simple-dimmers))</small>|
19
20
  |Simple Heater|`SimpleHeater`<sup>[9](#simple-heaters)</sup>|Heating solutions with only temperature control <small>([instructions](#simple-heaters))</small>|
20
21
  |Garage Door|`GarageDoor`<sup>[10](#garage-doors)</sup>|Smart garage doors or garage door openers <small>([instructions](#garage-doors))</small>|
21
22
  |Simple Garage Door|`SimpleGarageDoor`<sup>[10](#simple-garage-doors)</sup>|Sliding gate openers and garage door controllers with open/stop/close action DPs and a simple three-value status DP <small>([instructions](#simple-garage-doors))</small>|
@@ -345,14 +346,15 @@ If your signature doesn't have a variation of _low_ or _high_, `SimpleHeater` wo
345
346
  ```
346
347
 
347
348
  ### Simple Dimmers / WLED Dimmers
348
- These are switches that allow turning on and off, and dimming. Use type `WledDimmer` (legacy `SimpleDimmer` alias is also supported for backward compatibility).
349
+ These are switches that allow turning on and off, and dimming. Two distinct types are available:
349
350
 
350
- When using with a WLED controller (e.g. a Tuya-based relay/dimmer feeding power to a WLED strip), you can use advanced sync options:
351
+ - `SimpleDimmer` a plain dimmer with power and brightness control.
352
+ - `WledDimmer` — a dimmer that can additionally drive a [WLED](https://kno.wled.ge/) controller (e.g. a Tuya-based relay/dimmer feeding power to a WLED strip). With none of the WLED options below configured, it behaves exactly like a `SimpleDimmer`.
351
353
 
352
- - `syncBrightnessToWled`: set to WLED device IP (e.g. "192.168.1.50" or "192.168.1.50:80") to sync HomeKit brightness changes directly to WLED over HTTP, keeping the Tuya dimmer at 100%.
353
- - `presetEffects`: array of effect configs to expose as switches in HomeKit (each turns on a WLED fx preset, optionally with staticColor).
354
+ The following options apply to `WledDimmer` only (they are ignored by `SimpleDimmer`):
354
355
 
355
- See the accessory source for full details on WLED integration.
356
+ - `syncBrightnessToWled`: set to the WLED device IP (e.g. "192.168.1.50" or "192.168.1.50:80") to sync HomeKit brightness changes directly to WLED over HTTP, keeping the Tuya dimmer at 100%.
357
+ - `presetEffects`: array of effect configs to expose as switches in HomeKit (each turns on a WLED fx preset, optionally with staticColor).
356
358
 
357
359
  ```json5
358
360
  {