homebridge-tuya-plus 3.13.0 → 3.13.1-dev.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.
@@ -27,10 +27,19 @@ const HAP = {
27
27
  LockCurrentState: { UNSECURED: 0, SECURED: 1, JAMMED: 2, UNKNOWN: 3 },
28
28
  RotationDirection: { CLOCKWISE: 0, COUNTER_CLOCKWISE: 1 },
29
29
  RotationSpeed: { UUID: '29' },
30
- ValveType: { UUID: 'D5' },
31
- InUse: { UUID: 'D2' },
30
+ ValveType: { UUID: 'D5', GENERIC_VALVE: 0, IRRIGATION: 1, SHOWER_HEAD: 2, WATER_FAUCET: 3 },
31
+ InUse: { UUID: 'D2', NOT_IN_USE: 0, IN_USE: 1 },
32
32
  SetDuration: { UUID: 'D3' },
33
33
  RemainingDuration: { UUID: 'D4' },
34
+ ProgramMode: { UUID: 'D1', NO_PROGRAM_SCHEDULED: 0, PROGRAM_SCHEDULED: 1, PROGRAM_SCHEDULED_MANUAL_MODE: 2 },
35
+ IsConfigured: { UUID: 'D6', NOT_CONFIGURED: 0, CONFIGURED: 1 },
36
+ ServiceLabelIndex: { UUID: 'CB' },
37
+ ConfiguredName: { UUID: 'E3' },
38
+ BatteryLevel: { UUID: '68' },
39
+ StatusLowBattery: { UUID: '79', BATTERY_LEVEL_NORMAL: 0, BATTERY_LEVEL_LOW: 1 },
40
+ ChargingState: { UUID: '8F', NOT_CHARGING: 0, CHARGING: 1, NOT_CHARGEABLE: 2 },
41
+ ContactSensorState: { UUID: '6A', CONTACT_DETECTED: 0, CONTACT_NOT_DETECTED: 1 },
42
+ LeakDetected: { UUID: '70', LEAK_NOT_DETECTED: 0, LEAK_DETECTED: 1 },
34
43
  HeatingThresholdTemperature: { UUID: '12' },
35
44
  CoolingThresholdTemperature: { UUID: '0D' },
36
45
  CurrentTemperature: { UUID: '11' },
@@ -55,11 +64,30 @@ const HAP = {
55
64
  TemperatureSensor: { UUID: '8A' },
56
65
  LockMechanism: { UUID: '45' },
57
66
  Valve: { UUID: 'D0' },
67
+ IrrigationSystem: { UUID: 'CF' },
68
+ Battery: { UUID: '96' },
69
+ ContactSensor: { UUID: '80' },
70
+ LeakSensor: { UUID: '83' },
58
71
  Doorbell: { UUID: '41' },
59
72
  AccessoryInformation: { UUID: '3E' },
60
73
  },
61
74
  Formats: { FLOAT: 'float', UINT32: 'uint32', UINT16: 'uint16' },
62
75
  Perms: { READ: 'pr', NOTIFY: 'ev', WRITE: 'pw' },
76
+ // Mirrors hap-nodejs Categories. Intentionally kept faithful to the real
77
+ // enum (no FANLIGHT, no bare DEHUMIDIFIER) so getCategory() regressions are
78
+ // caught here instead of in production (see issue #45).
79
+ Categories: {
80
+ OTHER: 1, BRIDGE: 2, FAN: 3, GARAGE_DOOR_OPENER: 4, LIGHTBULB: 5,
81
+ DOOR_LOCK: 6, OUTLET: 7, SWITCH: 8, THERMOSTAT: 9, SENSOR: 10,
82
+ ALARM_SYSTEM: 11, SECURITY_SYSTEM: 11, DOOR: 12, WINDOW: 13,
83
+ WINDOW_COVERING: 14, PROGRAMMABLE_SWITCH: 15, RANGE_EXTENDER: 16,
84
+ CAMERA: 17, IP_CAMERA: 17, VIDEO_DOORBELL: 18, AIR_PURIFIER: 19,
85
+ AIR_HEATER: 20, AIR_CONDITIONER: 21, AIR_HUMIDIFIER: 22,
86
+ AIR_DEHUMIDIFIER: 23, APPLE_TV: 24, HOMEPOD: 25, SPEAKER: 26,
87
+ AIRPORT: 27, SPRINKLER: 28, FAUCET: 29, SHOWER_HEAD: 30,
88
+ TELEVISION: 31, TARGET_CONTROLLER: 32, ROUTER: 33, AUDIO_RECEIVER: 34,
89
+ TV_SET_TOP_BOX: 35, TV_STREAMING_STICK: 36,
90
+ },
63
91
  };
64
92
 
65
93
  function makeMockCharacteristic(initialValue = null) {
@@ -15,10 +15,10 @@ 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
- |Simple Dimmer|`SimpleDimmer`<sup>[8](#simple-dimmers)</sup>|Dimmer switches with power control <small>([instructions](#simple-dimmers))</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>|
19
19
  |Simple Heater|`SimpleHeater`<sup>[9](#simple-heaters)</sup>|Heating solutions with only temperature control <small>([instructions](#simple-heaters))</small>|
20
20
  |Garage Door|`GarageDoor`<sup>[10](#garage-doors)</sup>|Smart garage doors or garage door openers <small>([instructions](#garage-doors))</small>|
21
- |Simple Garage Door|`SimpleGarageDoor`<sup>[10](#simple-garage-doors)</sup>|Garage doors and sliding gate openers that expose only three momentary action DPs: open, stop, close <small>([instructions](#simple-garage-doors))</small>|
21
+ |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>|
22
22
  |Simple Blinds|`SimpleBlinds`<sup>[11](#simple-blinds)</sup>|Smart blinds and smart switches that control blinds <small>([instructions](#simple-blinds))</small>|
23
23
  |Simple Blinds2|`SimpleBlinds2`<sup>[11](#simple-blinds)</sup>|Smart blinds and smart switches that control blinds(Use if simple Blinds (1) doesn't work for you. <small>([instructions](#simple-blinds))</small>|
24
24
  |Vertical Blinds with Tilt|`VerticalBlindsWithTilt`<sup>[11](#vertical-blinds-with-tilt)</sup>|Smart vertical blinds with open/close and panel rotation <small>([instructions](#vertical-blinds-with-tilt))</small>|
@@ -316,13 +316,20 @@ If your signature doesn't have a variation of _low_ or _high_, `SimpleHeater` wo
316
316
  }
317
317
  ```
318
318
 
319
- ### Simple Dimmers
320
- These are switches allow turning on and off, and dimming.
319
+ ### Simple Dimmers / WLED Dimmers
320
+ These are switches that allow turning on and off, and dimming. Use type `WledDimmer` (legacy `SimpleDimmer` alias is also supported for backward compatibility).
321
+
322
+ 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:
323
+
324
+ - `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%.
325
+ - `presetEffects`: array of effect configs to expose as switches in HomeKit (each turns on a WLED fx preset, optionally with staticColor).
326
+
327
+ See the accessory source for full details on WLED integration.
321
328
 
322
329
  ```json5
323
330
  {
324
- "name": "My Simple Dimmer",
325
- "type": "SimpleDimmer",
331
+ "name": "My WLED Dimmer",
332
+ "type": "WledDimmer",
326
333
  "manufacturer": "TESSAN",
327
334
  "model": "Smart Dimmer Switch",
328
335
  "id": "032000123456789abcde",
@@ -334,11 +341,19 @@ These are switches allow turning on and off, and dimming.
334
341
  "dpPower": 1,
335
342
 
336
343
  /* Override the default datapoint identifier for brightness */
337
- "dpBrightness": 2
344
+ "dpBrightness": 2,
338
345
 
339
346
  /* Override the default datapoint identifier for scaleBrightness. Common values are 255 or 1000 */
340
- "scaleBrightness": 1000
347
+ "scaleBrightness": 1000,
341
348
 
349
+ /* WLED sync: forward brightness to a WLED instance instead of using the Tuya dimmer's level */
350
+ "syncBrightnessToWled": "192.168.1.123",
351
+
352
+ /* Optional preset effect switches (for WLED) */
353
+ "presetEffects": [
354
+ { "name": "Solid", "fx": 0, "staticColor": "#FFFFFF" },
355
+ { "name": "Rainbow", "fx": 2 }
356
+ ]
342
357
  }
343
358
  ```
344
359
 
@@ -404,7 +419,21 @@ While still in early testing, you can use this to open and close the garage door
404
419
  ```
405
420
 
406
421
  ### Simple Garage Doors
407
- For very basic garage door openers and sliding gate controllers that expose only three momentary action DPs — one to open, one to stop, one to close — with no position or status feedback. The plugin tracks the target state locally and persists it across restarts, so HomeKit always reflects whatever was last requested. Triggering a change sends the stop command first (so reversing direction mid-motion works; it is a no-op when the gate is idle) and then the open or close command. There is no obstruction detection.
422
+ For sliding gate openers and garage door controllers that expose momentary
423
+ open/stop/close action DPs plus a single status DP that reports the gate's
424
+ movement. The controller only distinguishes three states — `11` (stopped),
425
+ `12` (opening or open) and `13` (closing or closed) — so the plugin collapses
426
+ them for HomeKit: `11`/`12` are treated as **OPEN** and `13` as **CLOSED**.
427
+ Both the current and target door state are mirrored straight from this DP, so
428
+ HomeKit stays in sync however the gate was operated (Home app, a physical
429
+ remote, the Tuya app, ...).
430
+
431
+ Opening is direct: the controller reverses on its own, so an open command is
432
+ fired straight away even while the gate is closing. Closing is asymmetric — the
433
+ controller ignores a close command while the gate is actively moving. So unless
434
+ the status DP already reads `11` (stopped, e.g. after a partial-open or an
435
+ external stop, where close is accepted immediately), a close is sent as
436
+ **stop → wait `stopBeforeCloseMs` → close**. There is no obstruction detection.
408
437
 
409
438
  ```json5
410
439
  {
@@ -418,28 +447,39 @@ For very basic garage door openers and sliding gate controllers that expose only
418
447
  /* Additional parameters to override defaults only if needed */
419
448
 
420
449
  /* Override the default datapoint identifier for the open action */
421
- "dpOpen": 1,
422
-
423
- /* Override the default datapoint identifier for the stop action */
424
- "dpStop": 2,
450
+ "dpOpen": 101,
425
451
 
426
452
  /* Override the default datapoint identifier for the close action */
427
- "dpClose": 3,
453
+ "dpClose": 102,
454
+
455
+ /* Override the default datapoint identifier for the stop action
456
+ (used by the partial-open feature and the stop-before-close) */
457
+ "dpStop": 103,
458
+
459
+ /* Override the default datapoint identifier for the reported state.
460
+ 11 (stopped) and 12 (opening/open) are treated as OPEN; 13
461
+ (closing/closed) is treated as CLOSED. */
462
+ "dpState": 105,
463
+
464
+ /* Optional. The controller ignores a close while the gate is moving,
465
+ so unless the state DP already reads 11 (stopped) the plugin sends
466
+ stop, waits this many milliseconds, then sends close. Tune to about
467
+ how long the gate takes to halt after a stop. Default 1500. */
468
+ "stopBeforeCloseMs": 1500,
428
469
 
429
470
  /* Optional. If set, exposes an extra stateful switch that mirrors
430
471
  whether the gate is currently open in HomeKit's view. Tapping it
431
472
  ON triggers a partial-open: the gate opens and then stops itself
432
- this many milliseconds after the device acknowledges the open,
433
- leaving the gate partially open. Tapping it OFF triggers a
434
- standard full close. Useful for letting someone pass through
435
- briefly. Leave unset to skip the switch. */
473
+ this many milliseconds later, leaving the gate partially open.
474
+ Tapping it OFF triggers a standard full close. Useful for letting
475
+ someone pass through briefly. Leave unset to skip the switch. */
436
476
  "partialOpenMs": 2000,
437
477
 
438
478
  /* Optional. Exposes extra Force Open and Force Close momentary
439
- switches alongside the main GarageDoorOpener. They drive the gate
440
- through the same queue as the main toggle, but being plain
441
- switches they can be used in HomeKit automations (which won't
442
- accept GarageDoorOpener targets directly). Default false. */
479
+ switches alongside the main GarageDoorOpener. They fire the same
480
+ open/close actions as the main toggle, but being plain switches
481
+ they can be used in HomeKit automations (which won't accept
482
+ GarageDoorOpener targets directly). Default false. */
443
483
  "forceSwitches": true
444
484
  }
445
485
  ```
@@ -663,3 +703,108 @@ These are accessories that combine fan and lighting control in one device. Suppo
663
703
  "dpRotationDirection": 63
664
704
  }
665
705
  ```
706
+
707
+ If the light, brightness and turning the fan **off** all work, but turning the fan **on** or changing its speed is silently ignored, your fan's firmware most likely rejects LAN packets that carry more than one data point at once (some `fsd` ceiling fans behave this way). Set `"singleDpWrites": true` to send the fan power and speed as separate packets — matching how the Tuya cloud issues these commands:
708
+
709
+ ```json5
710
+ {
711
+ "type": "FanLight",
712
+ "name": "My Fan with Light",
713
+ "id": "032000123456789abcde",
714
+ "key": "0123456789abcdef",
715
+
716
+ "dpActive": 60,
717
+ "dpRotationSpeed": 62,
718
+
719
+ /* Send each data point in its own packet instead of combining them. */
720
+ "singleDpWrites": true
721
+ }
722
+ ```
723
+
724
+ ### Irrigation Systems / Sprinklers
725
+
726
+ Multi-valve Tuya irrigation/sprinkler controllers (the battery-powered Wi-Fi "faucet timers" that expose several `switch_*` valves, a `battery_percentage` and a `rain_sensor_state`) are exposed as a single, fully-fledged HomeKit **Irrigation System** accessory:
727
+
728
+ * one **Irrigation System** tile that contains every zone,
729
+ * one **Valve** per zone (`ValveType = Irrigation`) — each with its own on/off, its own **Duration** picker and a live countdown,
730
+ * an optional **Battery** service (level, low-battery warning, and — for solar/USB-C rechargeable units that report it — live charging status),
731
+ * an optional **rain sensor** (a Contact sensor by default).
732
+
733
+ Because these devices are slow to respond, all zone changes that happen close together — turning the whole system on/off, or running a scene that toggles several zones — are merged into a **single** Tuya command instead of a burst of them.
734
+
735
+ #### Minimal Configuration
736
+
737
+ The defaults match the common 4-zone layout (valves A–D on data-points `1`–`4`, battery on `46`, rain on `49`):
738
+
739
+ ```json5
740
+ {
741
+ "name": "Garden Irrigation",
742
+ "type": "IrrigationSystem",
743
+ "manufacturer": "Generic",
744
+ "model": "4-Zone Water Timer",
745
+ "id": "032000123456789abcde",
746
+ "key": "0123456789abcdef"
747
+ }
748
+ ```
749
+
750
+ #### Per-zone timers and "indefinite" mode
751
+
752
+ Each zone has its own **Duration**. When a zone is switched on it runs for that duration and the plugin closes it automatically (HomeKit's countdown is display-only — the plugin always enforces the shut-off, even after a Homebridge restart).
753
+
754
+ * Set a zone's duration to **`0`** to make it run **indefinitely** (until it is switched off again) — handy for long, manual watering tasks.
755
+ * Apple's Home app only offers duration presets up to **1 hour**. For longer runs (up to `maxDuration`) or to preset "indefinite" zones, set `defaultDuration` / the per-valve `defaultDuration` in the config, or use the Eve app.
756
+
757
+ #### Master ("toggle all") switch
758
+
759
+ Switching the whole Irrigation System tile **off** closes every open zone, and switching it **on** opens every zone — each as one combined command (mirroring the physical "all" button many of these controllers have). Either direction can be disabled with `masterTurnsOffAllZones` / `masterTurnsOnAllZones`.
760
+
761
+ #### Rain sensor: Contact vs Leak
762
+
763
+ By default the rain sensor is a **Contact sensor** (`raining → Open`). You can switch it to a **Leak sensor** (`raining → Leak Detected`) with `"rainSensorType": "leak"` — leak sensors read more naturally for rain and make "when it starts raining" automations obvious, **but HomeKit treats them as safety accessories and raises critical alerts that bypass Do-Not-Disturb on every rainfall**, which many people find too noisy. Use `rainInverted` if the wet/dry states appear reversed.
764
+
765
+ #### Full Configuration
766
+
767
+ ```json5
768
+ {
769
+ "name": "Garden Irrigation",
770
+ "type": "IrrigationSystem",
771
+ "manufacturer": "Generic",
772
+ "model": "4-Zone Water Timer",
773
+ "id": "032000123456789abcde",
774
+ "key": "0123456789abcdef",
775
+
776
+ /* --- Zones --- */
777
+ /* Simple: number of valves on sequential data-points 1, 2, 3, … */
778
+ "valveCount": 4,
779
+ /* …or, for custom names / non-sequential data-points, define them explicitly
780
+ (this overrides valveCount). defaultDuration is in seconds; 0 = indefinite. */
781
+ "valves": [
782
+ { "name": "Front Lawn", "dp": 1, "defaultDuration": 900 },
783
+ { "name": "Back Lawn", "dp": 2, "defaultDuration": 900 },
784
+ { "name": "Flower Beds","dp": 3, "defaultDuration": 600 },
785
+ { "name": "Drip Line", "dp": 4, "defaultDuration": 0 }
786
+ ],
787
+
788
+ /* --- Timers --- */
789
+ "defaultDuration": 600, /* default per-zone run time, seconds (0 = indefinite) */
790
+ "maxDuration": 7200, /* upper bound advertised to HomeKit, seconds */
791
+
792
+ /* --- Master switch behaviour --- */
793
+ "masterTurnsOnAllZones": true,
794
+ "masterTurnsOffAllZones": true,
795
+ "commandDebounce": 500, /* ms window for merging zone changes into one command */
796
+
797
+ /* --- Battery (omit / set noBattery:true if mains-powered) --- */
798
+ "dpBattery": 46,
799
+ "lowBatteryThreshold": 20,
800
+ "dpCharging": 101, /* boolean charging-status DP (solar / USB-C); omit if not reported */
801
+ /* "noBattery": true, */
802
+
803
+ /* --- Rain sensor (omit / set noRainSensor:true if not present) --- */
804
+ "dpRain": 49,
805
+ "rainSensorType": "contact", /* or "leak" */
806
+ "rainOnValue": "rain", /* enum value reported while raining */
807
+ "rainInverted": false
808
+ /* "noRainSensor": true, */
809
+ }
810
+ ```
@@ -17,6 +17,63 @@ Below is a list of configurations for devices by users of the plugin. Please fol
17
17
 
18
18
  # Dimmer
19
19
 
20
+ ## Wled Dimmer
21
+
22
+ **Treatlife Dimmable Smart Plug for Lamps**
23
+
24
+
25
+
26
+ Tested plugin version: ```2.0.1```
27
+
28
+ Tested by: [@benwtf](https://www.github.com/benwtf)
29
+
30
+ Link to buy: [Amazon](https://www.amazon.com/Treatlife-Dimmer-Halogen-Incandescent-Outlets/dp/B08LPYXK6L/ref=sr_1_2_sspa)
31
+
32
+ Capabilities:
33
+ - [X] On/Off
34
+ - [X] Dimming
35
+ - [ ] Timer (Use Tuya app)
36
+ - [ ] Dimming Config (Use Tuya app)
37
+
38
+ Config:
39
+ ```json
40
+ {
41
+ "type": "WledDimmer",
42
+ "name": "Lamp",
43
+ "id": "xxx",
44
+ "key": "xxx",
45
+ "manufacturer": "Treatlife",
46
+ "model": "WiFi Smart Dimmer Plug",
47
+ "scaleBrightness": 1000,
48
+ "minBrightness": 10,
49
+ "syncBrightnessToWled": "192.168.1.239",
50
+ "presetEffects": [
51
+ {
52
+ "name": "White Static",
53
+ "id": 0,
54
+ "staticColor": "#ffffff"
55
+ },
56
+ {
57
+ "name": "Cyan Static",
58
+ "id": 0,
59
+ "staticColor": "#5cecd9"
60
+ },
61
+ {
62
+ "name": "Juggle",
63
+ "id": 130
64
+ },
65
+ {
66
+ "name": "Fire",
67
+ "id": 80
68
+ }
69
+ ]
70
+ }
71
+ ```
72
+
73
+ Schema/DP's:
74
+ ```
75
+ ```
76
+
20
77
  ## Simple Dimmer
21
78
 
22
79
  **Treatlife Dimmable Smart Plug for Lamps**