homebridge-tuya-plus 3.14.0-dev.9 → 3.14.0-pr.94.55
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/AGENTS.md +42 -0
- package/Changelog.md +16 -3
- package/Readme.MD +7 -45
- package/config.schema.json +93 -74
- package/index.js +599 -358
- package/lib/AirConditionerAccessory.js +31 -8
- package/lib/AirPurifierAccessory.js +1 -1
- package/lib/BaseAccessory.js +54 -17
- package/lib/ConvectorAccessory.js +1 -1
- package/lib/CustomMultiOutletAccessory.js +2 -1
- package/lib/DoorbellAccessory.js +9 -16
- package/lib/GarageDoorAccessory.js +1 -1
- package/lib/IrrigationSystemAccessory.js +287 -114
- package/lib/MultiOutletAccessory.js +3 -2
- package/lib/OilDiffuserAccessory.js +10 -8
- package/lib/RGBTWLightAccessory.js +13 -11
- package/lib/RGBTWOutletAccessory.js +11 -9
- package/lib/SimpleBlindsAccessory.js +5 -5
- package/lib/SimpleDimmer2Accessory.js +1 -1
- package/lib/SimpleDimmerAccessory.js +10 -6
- package/lib/SimpleGarageDoorAccessory.js +122 -26
- package/lib/SimpleHeaterAccessory.js +4 -4
- package/lib/SimpleLightAccessory.js +1 -1
- package/lib/SwitchAccessory.js +3 -2
- package/lib/TWLightAccessory.js +2 -2
- package/lib/TuyaAccessory.js +75 -42
- package/lib/TuyaCloudApi.js +121 -8
- package/lib/TuyaCloudDevice.js +434 -31
- package/lib/TuyaCloudMessaging.js +34 -41
- package/lib/TuyaDevice.js +269 -0
- package/lib/TuyaDiscovery.js +25 -9
- package/lib/ValveAccessory.js +16 -12
- package/lib/VerticalBlindsWithTilt.js +27 -25
- package/lib/WledDimmerAccessory.js +46 -81
- package/package.json +3 -5
- package/test/BaseAccessory.test.js +94 -15
- package/test/IrrigationSystemAccessory.test.js +355 -59
- package/test/MultiOutletAccessory.test.js +18 -3
- package/test/RGBTWLightAccessory.test.js +3 -3
- package/test/SimpleFanLightAccessory.test.js +3 -3
- package/test/SimpleGarageDoorAccessory.test.js +193 -19
- package/test/TuyaAccessory.protocol.test.js +76 -3
- package/test/TuyaCloudApi.test.js +110 -1
- package/test/TuyaCloudDevice.test.js +564 -2
- package/test/TuyaCloudMessaging.test.js +24 -5
- package/test/TuyaDevice.test.js +266 -0
- package/test/getCategory.test.js +1 -0
- package/test/index.test.js +271 -0
- package/test/support/mocks.js +13 -0
- package/wiki/Supported-Device-Types.md +89 -31
- package/wiki/Tuya-Cloud-Setup.md +31 -108
package/AGENTS.md
CHANGED
|
@@ -91,6 +91,29 @@ When adding or changing a device type, follow the existing pattern:
|
|
|
91
91
|
`no-prototype-builtins`) as tech debt; don't rely on or expand that. Keep new
|
|
92
92
|
code clean.
|
|
93
93
|
|
|
94
|
+
## Logging
|
|
95
|
+
|
|
96
|
+
Users run this in Homebridge and read these logs; a chatty plugin is a real
|
|
97
|
+
complaint. The bar for a non-debug line is high.
|
|
98
|
+
|
|
99
|
+
- **`debug` is the default.** Anything routine, per-message, per-state-change,
|
|
100
|
+
per-connect, or protocol-level (odd/raw/malformed frames, reconnects, socket
|
|
101
|
+
recycling, DP dumps, "X changed: …") goes to `this.log.debug`. If it can fire
|
|
102
|
+
more than a handful of times in normal operation, it's `debug`.
|
|
103
|
+
- **`info`/`warn`/`error` are for what the user must see or act on**, and the
|
|
104
|
+
level must match real severity — a harmless condition (e.g. a cloud-fallback
|
|
105
|
+
failure while the device is reachable over the LAN) is not a `warn`/`error`.
|
|
106
|
+
Prefer one actionable line over a vague one; name the device.
|
|
107
|
+
- **Never spam.** A condition that recurs on a timer or hot path must be
|
|
108
|
+
deduplicated (surface once, then drop repeats to `debug` until it changes) and
|
|
109
|
+
its retry backed off — see `TuyaCloudDevice._onConnectFailure` and the
|
|
110
|
+
discovery port-in-use guard for the pattern.
|
|
111
|
+
- **Use the Homebridge logger** (`this.log` / `this.log.debug|info|warn|error`),
|
|
112
|
+
never `console.*`, in plugin/runtime code. (The standalone `bin/` CLIs are the
|
|
113
|
+
exception: their `console.*` is intentional user-facing output.)
|
|
114
|
+
- **Logging must never throw and must never leak secrets** — don't dump a whole
|
|
115
|
+
config/props object (it carries the device's local `key`); log the id/name.
|
|
116
|
+
|
|
94
117
|
## Backwards compatibility (read before changing behavior)
|
|
95
118
|
|
|
96
119
|
This is the most important rule in this repo. Users have working setups and
|
|
@@ -108,6 +131,25 @@ devices already paired in HomeKit; a careless change can break them silently.
|
|
|
108
131
|
routing.
|
|
109
132
|
- When a change must alter behavior, make it opt-in.
|
|
110
133
|
|
|
134
|
+
## Debug config block
|
|
135
|
+
|
|
136
|
+
The platform reads an **undocumented** top-level `debug` object from the config
|
|
137
|
+
(see `_debugConfig()` in `index.js`). It holds dev/test-only switches and is
|
|
138
|
+
deliberately **kept out of `config.schema.json`** so it never appears in the
|
|
139
|
+
Homebridge UI. Treat it as the one place such switches belong; add new ones as
|
|
140
|
+
optional, off-by-default flags read through `_debugConfig()` and coerced with
|
|
141
|
+
`coerceBoolean`.
|
|
142
|
+
|
|
143
|
+
- `debug.forceCloudFallback` — pretend LAN discovery fails for every device, so
|
|
144
|
+
the whole platform runs over the Tuya Cloud fallback. Lets the cloud path be
|
|
145
|
+
exercised end-to-end without taking devices off the LAN (needs a configured
|
|
146
|
+
`cloud` block to be useful).
|
|
147
|
+
- `debug.logCloudHttp` — trace every Tuya Cloud HTTP request/response (method,
|
|
148
|
+
path, headers, body, status, response) at `debug`, with all credentials
|
|
149
|
+
(token, signature, password, access key/id, uid) redacted. Verbose and
|
|
150
|
+
per-request — keep it off in normal operation, and remember Homebridge
|
|
151
|
+
debug logging must be on to see it.
|
|
152
|
+
|
|
111
153
|
## Git & PR workflow
|
|
112
154
|
|
|
113
155
|
- Develop on the branch you've been assigned; never push to `main` directly.
|
package/Changelog.md
CHANGED
|
@@ -4,11 +4,24 @@ All notable changes to this project will be documented in this file. This projec
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
-
* [+] **Tuya Cloud
|
|
7
|
+
* [+] **Tuya Cloud is now a transparent, global fallback for every device.** What started as an experiment to reach battery-powered "sleepy" irrigation timers (which never appear on the LAN) is now a general, optional fallback: add a top-level `cloud` credentials block once and the plugin keeps a single Tuya Cloud/MQTT session alive in the background, falling back to it whenever a device can't be reached on the LAN — a flaky moment, or hardware that's never local. The plugin stays **LAN-first** (local is always tried first and preferred) and cloud remains **strictly opt-in** (nothing runs unless credentials are present).
|
|
8
|
+
* **No per-device configuration.** There is one global session and no per-device cloud settings. Every device automatically uses the LAN first and the cloud as a fallback; a device with no local `key` is reached over the cloud only. Existing LAN configs (numeric data-points) keep working over the cloud — the data-point id↔code map is learned from Tuya's device shadow (`/v2.0/cloud/thing/{id}/shadow/properties`) — and if both transports fail, HomeKit shows "No Response" as before. This mirrors the official Tuya/Smart Life app (LAN when possible, cloud as a backup).
|
|
8
9
|
* Realtime updates arrive over Tuya's **MQTT** message service (via the optional `mqtt` dependency, installed automatically); initial state and control use the Tuya OpenAPI. There is no polling.
|
|
9
10
|
* Works with both **Custom** and **Smart Home** Cloud projects (the latter via app-account login).
|
|
10
|
-
* The
|
|
11
|
-
* See the wiki: **[Tuya Cloud Setup](https://github.com/adrianjagielak/homebridge-tuya-plus/blob/main/wiki/Tuya-Cloud-Setup.md)**.
|
|
11
|
+
* The **IrrigationSystem** accessory no longer has any cloud-specific handling — like every other accessory it's transport-agnostic, with the LAN+cloud fallback handled underneath.
|
|
12
|
+
* If your devices sometimes drop off the LAN and show "No Response", adding cloud credentials is an easy way to smooth that over. See the wiki: **[Tuya Cloud Setup](https://github.com/adrianjagielak/homebridge-tuya-plus/blob/main/wiki/Tuya-Cloud-Setup.md)**.
|
|
13
|
+
* [*] **Fix realtime (MQTT) cloud updates being silently dropped** — external changes (physical buttons, the Tuya app, the device's own timers) now show up in HomeKit within a second or two. The decryptor was verifying the AES-GCM auth tag (`decipher.final()`), but Tuya's real status frames don't carry a tag that verifies against the documented AAD, so every realtime message was being thrown away. Now decrypts with `update()` only, matching the official `tuya/tuya-homebridge` and `0x5e/homebridge-tuya-platform` implementations.
|
|
14
|
+
* [*] **Fix cloud irrigation valves that could be turned on but not off** — the per-zone write coalescer was dropping any command that matched the last-known `device.state`. Cloud devices never optimistically advance `state` (it only moves once the realtime stream confirms the device), so an "off" issued before the "on" was echoed matched the stale "off" and was discarded — HomeKit showed the zone closed while it kept running. Queued commands are now sent as-is (callers already queue only genuine changes).
|
|
15
|
+
* [*] **IrrigationSystem: remove the rain sensor.** It never reported reliably on these devices, and bundling a sensor (a different HomeKit category) in the same accessory forced the Home app to fragment the sprinkler into "sub-accessories" — blocking control from the main tile and hiding the system master on/off. The accessory is now a single, clean sprinkler tile (IrrigationSystem + valves + optional battery); any leftover Contact/Leak sensor service from a previous build is removed automatically on restart. The `noRainSensor`, `rainSensorType`, `rainInverted`, `dpRain` and `rainOnValue` options are gone.
|
|
16
|
+
* [*] **IrrigationSystem: add the HAP Service Label service for multi-valve controllers** — an accessory that exposes a collection of same-type services (more than one `Valve`) must include a `ServiceLabel` service to anchor each valve's `ServiceLabelIndex`. It was missing, so stricter Home app clients (notably iOS) scattered the zones as separate tiles instead of nesting them under the single irrigation tile. The service is added automatically (with the Arabic-numerals namespace) whenever there is more than one valve; user-set zone names still take precedence.
|
|
17
|
+
* [*] **IrrigationSystem: stop the valve toggle flickering after a press** — tapping a zone briefly snapped back to the old state before settling on the new one. The `Active` getters returned the raw `device.state`, which (for cloud devices) only advances once the realtime stream echoes the write back, so a read in that window reported the pre-press value. The getters now report the value HomeKit already shows (optimistic on press, then confirmed/corrected by device-side change events); they still surface "No Response" while disconnected.
|
|
18
|
+
* [*] **Tuya Cloud: report real device online/offline status.** Cloud devices previously always showed as reachable. They now mirror Tuya's `online` flag (read from the device record on connect and re-checked when the realtime stream reconnects), so HomeKit shows **"No Response"** when the device is genuinely offline. If the lookup isn't permitted (the project lacks the device-management API), the device is assumed reachable so control is never blocked.
|
|
19
|
+
* [*] **Signal unreachable devices with `HapStatusError`.** The shared getters (`getStateAsync` / `getDividedStateAsync`, used by nearly every accessory's read handlers, plus the irrigation `Active` getter) threw a plain `Error` to make HomeKit show "No Response". Newer Homebridge logs a characteristic warning for that before falling back to a generic status; they now throw `HapStatusError(SERVICE_COMMUNICATION_FAILURE)` directly — same "No Response", no warning spam. No behaviour change for users.
|
|
20
|
+
* [*] **Surface write and connection failures to HomeKit, universally across device types.** Previously a command sent to an unreachable device was silently dropped while HomeKit still reported the tap as successful, and several accessories kept showing their last-known state instead of going "No Response" — the clearest example was a LAN gate that logged `skipping write, device not connected` yet still appeared online and "accepted" open/close taps. Now:
|
|
21
|
+
* **Writes** to a disconnected device (or a write the device/cloud rejects) fail the HomeKit operation instead of pretending success. The shared write helpers (`setState` / `setMultiState` / `setMultiStateLegacy` and their `*Async` variants) now signal a communication failure, so accessories whose set handlers swallowed it (garage door, switches/outlets, RGB hue + saturation, blinds, vertical-tilt blinds) report it on a tap.
|
|
22
|
+
* **Reads** that returned a cached/optimistic or constant value (brightness, colour, hue/saturation, blind position/state, garage-door state, valve in-use/duration, heater state, …) now report **"No Response"** when the device is unreachable — matching the read handlers that already did.
|
|
23
|
+
* **Cloud** commands that fail over HTTP (a network error, or a command Tuya rejects) are surfaced too: `TuyaCloudDevice.update()` now resolves to the real command result so the write helper can await it, rather than firing and forgetting.
|
|
24
|
+
* Internal/background writes (auto-shutoff timers, multi-step open/close sequences, debounced batches) stay non-fatal via dedicated non-throwing helpers (`setStateInBackground` / `setMultiStateInBackground` / `setMultiStateLegacyInBackground`), so an unreachable device can never crash Homebridge with an unhandled error.
|
|
12
25
|
|
|
13
26
|
## 2.0.1 (2021-03-25)
|
|
14
27
|
This update includes the following changes:
|
package/Readme.MD
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
A community-maintained Homebridge plugin for controlling Tuya devices locally over LAN. Control your supported Tuya accessories locally in HomeKit.
|
|
17
|
+
A community-maintained Homebridge plugin for controlling Tuya devices locally over LAN. Control your supported Tuya accessories locally in HomeKit, with an optional Tuya Cloud fallback for devices the LAN can't reach.
|
|
18
18
|
|
|
19
19
|
* [Supported Device Types](#supported-device-types)
|
|
20
20
|
* [Supported Tuya Protocol Versions](#supported-tuya-protocol-versions)
|
|
@@ -38,7 +38,7 @@ A community-maintained Homebridge plugin for controlling Tuya devices locally ov
|
|
|
38
38
|
* Fan v2<sup>[7](https://github.com/adrianjagielak/homebridge-tuya-plus/blob/main/wiki/Supported-Device-Types.md)</sup>
|
|
39
39
|
* Garages<sup>[8](https://github.com/adrianjagielak/homebridge-tuya-plus/blob/main/wiki/Supported-Device-Types.md#garage-doors)</sup>
|
|
40
40
|
* Heaters<sup>[9](https://github.com/adrianjagielak/homebridge-tuya-plus/blob/main/wiki/Supported-Device-Types.md)</sup>
|
|
41
|
-
* Irrigation Systems / Sprinklers<sup>[17](https://github.com/adrianjagielak/homebridge-tuya-plus/blob/main/wiki/Supported-Device-Types.md#irrigation-systems--sprinklers)</sup> (multi-valve, per-zone timers,
|
|
41
|
+
* Irrigation Systems / Sprinklers<sup>[17](https://github.com/adrianjagielak/homebridge-tuya-plus/blob/main/wiki/Supported-Device-Types.md#irrigation-systems--sprinklers)</sup> (multi-valve, per-zone timers, battery)
|
|
42
42
|
* Lights
|
|
43
43
|
* On/Off<sup>[10](https://github.com/adrianjagielak/homebridge-tuya-plus/blob/main/wiki/Supported-Device-Types.md)</sup>
|
|
44
44
|
* Brightness<sup>[11](https://github.com/adrianjagielak/homebridge-tuya-plus/blob/main/wiki/Supported-Device-Types.md#tunable-white-light-bulbs)</sup>
|
|
@@ -48,34 +48,11 @@ A community-maintained Homebridge plugin for controlling Tuya devices locally ov
|
|
|
48
48
|
* Switches<sup>[15](https://github.com/adrianjagielak/homebridge-tuya-plus/blob/main/wiki/Supported-Device-Types.md#switch)</sup>
|
|
49
49
|
* Vertical Blinds<sup>[16](https://github.com/adrianjagielak/homebridge-tuya-plus/blob/main/wiki/Supported-Device-Types.md#vertical-blinds-with-tilt)</sup>
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
## Cloud fallback
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
The plugin is LAN-first — everything is controlled locally by default. If you add Tuya Cloud credentials in a top-level `cloud` block, it keeps a cloud session in the background and uses it as a fallback: each device is tried on the LAN first, and only falls back to the cloud when it can't be reached locally — a brief LAN hiccup, or hardware that never appears on the LAN at all (e.g. battery-powered "sleepy" devices). It's opt-in and local always comes first, and existing configs need no changes — so it's also a handy fix if some devices occasionally show as "No Response".
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
| Tuya LAN protocol | Framing | Encryption | Status |
|
|
58
|
-
|---|---|---|---|
|
|
59
|
-
| 3.1 | `0x55AA` | AES-128-ECB (base64, control only) | ✅ Supported |
|
|
60
|
-
| 3.2 | `0x55AA` | AES-128-ECB | ✅ Supported |
|
|
61
|
-
| 3.3 | `0x55AA` | AES-128-ECB | ✅ Supported |
|
|
62
|
-
| 3.4 | `0x55AA` | AES-128-ECB, session key, HMAC-SHA256 | ✅ Supported |
|
|
63
|
-
| 3.5 | `0x6699` | AES-128-GCM, session key | ✅ Supported |
|
|
64
|
-
| 3.6 and newer | — | — | ✅ Forward-compatible (see below) |
|
|
65
|
-
|
|
66
|
-
The protocol version is auto-detected from the device's discovery broadcast, so you normally don't need to configure anything.
|
|
67
|
-
|
|
68
|
-
Protocol **3.5** is currently the newest Tuya LAN protocol in existence: it is the latest version implemented by Tuya's own open-source device SDK ([TuyaOpen](https://github.com/tuya/TuyaOpen)) and by the reference reverse-engineered implementations ([tinytuya](https://github.com/jasonacox/tinytuya/blob/master/PROTOCOL.md)). No device firmware speaking a "3.6" protocol has been observed in the wild so far.
|
|
69
|
-
|
|
70
|
-
This plugin is nevertheless **3.6-ready**: if a device reports a newer protocol version (e.g. `3.6`) in its broadcast, the plugin automatically talks to it using the newest (3.5/GCM) protocol stack while tagging payloads with the device's reported version — the same forward-compatibility strategy used by tinytuya. Should such a device misbehave, you can pin it to a specific protocol with the `forceVersion` device option (e.g. `"forceVersion": "3.5"`), or use `version` to set a protocol for devices that can't be discovered (e.g. on another subnet).
|
|
71
|
-
|
|
72
|
-
## Cloud devices (for hardware that can't be controlled locally)
|
|
73
|
-
|
|
74
|
-
This is a **LAN-first** plugin — virtually every Tuya device is controlled locally, which is faster, more private, and keeps working without internet. A few devices, though, simply **can't** be reached over the LAN: battery-powered "sleepy" devices — for example the **irrigation / faucet timers** — sleep almost all the time and only ever connect out to Tuya's cloud, so they never answer on the local network.
|
|
75
|
-
|
|
76
|
-
For exactly these cases the plugin can talk to a device through the **Tuya Cloud** instead — strictly **opt-in, per device**. You add your Tuya Cloud project credentials once and set `"cloud": true` on the device; everything else works the same.
|
|
77
|
-
|
|
78
|
-
👉 **[Tuya Cloud Setup guide](https://github.com/adrianjagielak/homebridge-tuya-plus/blob/main/wiki/Tuya-Cloud-Setup.md)** — how to get credentials and configure a cloud device.
|
|
55
|
+
See the **[Tuya Cloud Setup guide](https://github.com/adrianjagielak/homebridge-tuya-plus/blob/main/wiki/Tuya-Cloud-Setup.md)** for credentials and setup.
|
|
79
56
|
|
|
80
57
|
## Installation Instructions
|
|
81
58
|
|
|
@@ -92,8 +69,7 @@ sudo npm install -g homebridge-tuya-plus
|
|
|
92
69
|
#### Bleeding-edge (`dev`) builds:
|
|
93
70
|
|
|
94
71
|
Every commit to `main` is automatically published to npm under the `dev` tag — a
|
|
95
|
-
separate, unstable channel.
|
|
96
|
-
won't affect normal installs. To try the latest in-development build:
|
|
72
|
+
separate, unstable channel. To try the latest in-development build:
|
|
97
73
|
|
|
98
74
|
```
|
|
99
75
|
sudo npm install -g homebridge-tuya-plus@dev
|
|
@@ -102,20 +78,6 @@ sudo npm install -g homebridge-tuya-plus@dev
|
|
|
102
78
|
These are versioned like `3.14.0-dev.<n>` and may be unstable; use the default
|
|
103
79
|
install above for production.
|
|
104
80
|
|
|
105
|
-
#### Testing a specific pull request:
|
|
106
|
-
|
|
107
|
-
Maintainers can publish an unmerged PR as a throwaway test build by commenting
|
|
108
|
-
`/publish` on it. The build goes to its own `pr-<number>` tag — never `latest`
|
|
109
|
-
or `dev` — so it never reaches normal installs. The bot replies on the PR with
|
|
110
|
-
the exact install command, e.g.:
|
|
111
|
-
|
|
112
|
-
```
|
|
113
|
-
sudo npm install -g homebridge-tuya-plus@pr-57
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
Each build also gets a unique, immutable version like `3.14.0-pr.57.<n>` that you
|
|
117
|
-
can pin or paste into the Homebridge UI's "Install Alternate Version" field.
|
|
118
|
-
|
|
119
81
|
## Configuration
|
|
120
82
|
> UI
|
|
121
83
|
|
|
@@ -148,7 +110,7 @@ If you have new accessory logic for a new device, please add a function defined
|
|
|
148
110
|
|
|
149
111
|
#
|
|
150
112
|
|
|
151
|
-
|
|
113
|
+
Check out my other Homebridge plugins:
|
|
152
114
|
|
|
153
115
|
* [homebridge-futurehome](https://github.com/adrianjagielak/homebridge-futurehome) ([npm](https://npmjs.com/package/homebridge-futurehome))
|
|
154
116
|
* [homebridge-tuya-plus](https://github.com/adrianjagielak/homebridge-tuya-plus) ([npm](https://npmjs.com/package/homebridge-tuya-plus))
|
package/config.schema.json
CHANGED
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
},
|
|
22
22
|
"cloud": {
|
|
23
23
|
"type": "object",
|
|
24
|
-
"title": "Tuya Cloud (optional
|
|
25
|
-
"description": "
|
|
24
|
+
"title": "Tuya Cloud fallback (optional)",
|
|
25
|
+
"description": "<a href='https://github.com/adrianjagielak/homebridge-tuya-plus/blob/main/wiki/Tuya-Cloud-Setup.md'>Tuya Cloud API Setup</a>.",
|
|
26
26
|
"properties": {
|
|
27
27
|
"accessId": {
|
|
28
28
|
"type": "string",
|
|
@@ -69,12 +69,6 @@
|
|
|
69
69
|
{ "title": "Tuya Smart", "enum": ["tuyaSmart"] },
|
|
70
70
|
{ "title": "Smart Life", "enum": ["smartlife"] }
|
|
71
71
|
]
|
|
72
|
-
},
|
|
73
|
-
"realtime": {
|
|
74
|
-
"type": "boolean",
|
|
75
|
-
"title": "Realtime updates (MQTT)",
|
|
76
|
-
"default": true,
|
|
77
|
-
"description": "Receive instant updates over Tuya's MQTT message service (uses the 'mqtt' package, installed automatically). If disabled, devices stay controllable and show their state at startup, but external changes (physical buttons, the device's own timers) won't be reflected until restart."
|
|
78
72
|
}
|
|
79
73
|
}
|
|
80
74
|
},
|
|
@@ -96,7 +90,7 @@
|
|
|
96
90
|
{
|
|
97
91
|
"title": "Switch Gang / Basic Switch",
|
|
98
92
|
"enum": ["Switch"]
|
|
99
|
-
},
|
|
93
|
+
},
|
|
100
94
|
{
|
|
101
95
|
"title": "Smart Plug / Barely Smart Power Strip",
|
|
102
96
|
"enum": ["Outlet"]
|
|
@@ -197,16 +191,7 @@
|
|
|
197
191
|
"key": {
|
|
198
192
|
"title": "Tuya Key (local key — required for LAN devices)",
|
|
199
193
|
"type": "string",
|
|
200
|
-
"description": "The device's local key. Required for normal (LAN) devices.
|
|
201
|
-
"condition": {
|
|
202
|
-
"functionBody": "return model.devices && model.devices[arrayIndices].type !== 'null';"
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
|
-
"cloud": {
|
|
206
|
-
"type": ["boolean", "object"],
|
|
207
|
-
"title": "Control via Tuya Cloud (instead of the LAN)",
|
|
208
|
-
"default": false,
|
|
209
|
-
"description": "Enable for devices that can't be reached locally (e.g. battery-powered irrigation timers). Uses the platform-level 'cloud' credentials above. Advanced: instead of a checkbox you can set this to an object with per-device { accessId, accessKey, region, … } to use different credentials for this one device.",
|
|
194
|
+
"description": "The device's local key. Required for normal (LAN) devices. A device with no key is treated as cloud-only and is reached through the Tuya Cloud session configured above (e.g. a battery-powered 'sleepy' irrigation timer).",
|
|
210
195
|
"condition": {
|
|
211
196
|
"functionBody": "return model.devices && model.devices[arrayIndices].type !== 'null';"
|
|
212
197
|
}
|
|
@@ -318,9 +303,9 @@
|
|
|
318
303
|
"title": "Sync brightness to WLED (IP[:port])",
|
|
319
304
|
"placeholder": "192.168.1.100",
|
|
320
305
|
"condition": {
|
|
321
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['WledDimmer'
|
|
306
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['WledDimmer'].includes(model.devices[arrayIndices].type);"
|
|
322
307
|
}
|
|
323
|
-
},
|
|
308
|
+
},
|
|
324
309
|
"dpColorTemperature": {
|
|
325
310
|
"type": "integer",
|
|
326
311
|
"placeholder": "3",
|
|
@@ -421,6 +406,12 @@
|
|
|
421
406
|
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
422
407
|
}
|
|
423
408
|
},
|
|
409
|
+
"noAuto": {
|
|
410
|
+
"type": "boolean",
|
|
411
|
+
"condition": {
|
|
412
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
413
|
+
}
|
|
414
|
+
},
|
|
424
415
|
"cmdCool": {
|
|
425
416
|
"type": "string",
|
|
426
417
|
"placeholder": "COOL",
|
|
@@ -435,6 +426,42 @@
|
|
|
435
426
|
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
436
427
|
}
|
|
437
428
|
},
|
|
429
|
+
"cmdAuto": {
|
|
430
|
+
"type": "string",
|
|
431
|
+
"placeholder": "AUTO",
|
|
432
|
+
"condition": {
|
|
433
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
"noRotationSpeed": {
|
|
437
|
+
"type": "boolean",
|
|
438
|
+
"condition": {
|
|
439
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
"fanSpeedSteps": {
|
|
443
|
+
"type": "integer",
|
|
444
|
+
"placeholder": "3",
|
|
445
|
+
"condition": {
|
|
446
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
447
|
+
}
|
|
448
|
+
},
|
|
449
|
+
"fanSpeedValues": {
|
|
450
|
+
"type": "array",
|
|
451
|
+
"description": "Ordered string values used by the device for each fan speed (low → high), e.g. ['low', 'middle', 'high']. Overrides fanSpeedSteps.",
|
|
452
|
+
"items": {
|
|
453
|
+
"type": "string"
|
|
454
|
+
},
|
|
455
|
+
"condition": {
|
|
456
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
457
|
+
}
|
|
458
|
+
},
|
|
459
|
+
"noChildLock": {
|
|
460
|
+
"type": "boolean",
|
|
461
|
+
"condition": {
|
|
462
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner', 'Convector'].includes(model.devices[arrayIndices].type);"
|
|
463
|
+
}
|
|
464
|
+
},
|
|
438
465
|
"noSwing": {
|
|
439
466
|
"type": "boolean",
|
|
440
467
|
"condition": {
|
|
@@ -569,30 +596,31 @@
|
|
|
569
596
|
"functionBody": "return model.devices && model.devices[arrayIndices] && ['Convector'].includes(model.devices[arrayIndices].type);"
|
|
570
597
|
}
|
|
571
598
|
},
|
|
572
|
-
"
|
|
599
|
+
"noTemperatureUnit": {
|
|
573
600
|
"type": "boolean",
|
|
574
601
|
"condition": {
|
|
575
602
|
"functionBody": "return model.devices && model.devices[arrayIndices] && ['Convector'].includes(model.devices[arrayIndices].type);"
|
|
576
603
|
}
|
|
577
604
|
},
|
|
578
|
-
"
|
|
579
|
-
"type": "
|
|
605
|
+
"temperatureDivisor": {
|
|
606
|
+
"type": "integer",
|
|
607
|
+
"placeholder": "1",
|
|
580
608
|
"condition": {
|
|
581
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['
|
|
609
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['SimpleHeater', 'AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
582
610
|
}
|
|
583
611
|
},
|
|
584
|
-
"
|
|
612
|
+
"currentTemperatureDivisor": {
|
|
585
613
|
"type": "integer",
|
|
586
614
|
"placeholder": "1",
|
|
587
615
|
"condition": {
|
|
588
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['
|
|
616
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
589
617
|
}
|
|
590
618
|
},
|
|
591
619
|
"thresholdTemperatureDivisor": {
|
|
592
620
|
"type": "integer",
|
|
593
621
|
"placeholder": "1",
|
|
594
622
|
"condition": {
|
|
595
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['SimpleHeater'].includes(model.devices[arrayIndices].type);"
|
|
623
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['SimpleHeater', 'AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
596
624
|
}
|
|
597
625
|
},
|
|
598
626
|
"temperatureOffset": {
|
|
@@ -717,7 +745,7 @@
|
|
|
717
745
|
"type": "integer",
|
|
718
746
|
"title": "Number of Valves / Zones",
|
|
719
747
|
"placeholder": 4,
|
|
720
|
-
"description": "How many valves/zones the controller has. They are assumed to be on data-points 1, 2, 3, … (
|
|
748
|
+
"description": "How many valves/zones the controller has. They are assumed to be on data-points 1, 2, 3, … (over the cloud these map to the codes switch_1, switch_2, … automatically). For non-sequential data-points use the 'valves' list instead.",
|
|
721
749
|
"condition": {
|
|
722
750
|
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type);"
|
|
723
751
|
}
|
|
@@ -730,9 +758,27 @@
|
|
|
730
758
|
"items": {
|
|
731
759
|
"type": "object",
|
|
732
760
|
"properties": {
|
|
733
|
-
"name": {
|
|
734
|
-
|
|
735
|
-
|
|
761
|
+
"name": {
|
|
762
|
+
"type": "string",
|
|
763
|
+
"title": "Zone name",
|
|
764
|
+
"placeholder": "Lawn"
|
|
765
|
+
},
|
|
766
|
+
"dp": {
|
|
767
|
+
"type": ["integer", "string"],
|
|
768
|
+
"title": "Data-point (numeric id, or cloud code)",
|
|
769
|
+
"placeholder": "1 or switch_1"
|
|
770
|
+
},
|
|
771
|
+
"dpCountdown": {
|
|
772
|
+
"type": ["integer", "string"],
|
|
773
|
+
"title": "Countdown data-point (numeric id, or cloud code)",
|
|
774
|
+
"description": "Optional. The device's built-in auto-off timer for this zone. Defaults to the switch data-point + 16 (so switch 1 → countdown 17). Set this only for code-addressed or non-standard zones.",
|
|
775
|
+
"placeholder": "17 or countdown_1"
|
|
776
|
+
},
|
|
777
|
+
"defaultDuration": {
|
|
778
|
+
"type": "integer",
|
|
779
|
+
"title": "Default run time (seconds, 0 = run indefinitely)",
|
|
780
|
+
"placeholder": 600
|
|
781
|
+
}
|
|
736
782
|
}
|
|
737
783
|
},
|
|
738
784
|
"condition": {
|
|
@@ -752,7 +798,16 @@
|
|
|
752
798
|
"type": "integer",
|
|
753
799
|
"title": "Maximum Run Time (seconds)",
|
|
754
800
|
"placeholder": 7200,
|
|
755
|
-
"description": "Upper bound advertised to HomeKit for the duration picker (HAP default is 3600 / 1h). Apple's Home app only offers presets up to 1h regardless; longer values can be set from the config or apps like Eve.",
|
|
801
|
+
"description": "Upper bound advertised to HomeKit for the duration picker (HAP default is 3600 / 1h). Apple's Home app only offers presets up to 1h regardless; longer values can be set from the config or apps like Eve. Also bounds the hardware countdown: a device countdown longer than this (or the 120-min hardware cap) is corrected on connect.",
|
|
802
|
+
"condition": {
|
|
803
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type);"
|
|
804
|
+
}
|
|
805
|
+
},
|
|
806
|
+
"nativeCountdown": {
|
|
807
|
+
"type": "boolean",
|
|
808
|
+
"title": "Use the device's built-in countdown timer",
|
|
809
|
+
"placeholder": true,
|
|
810
|
+
"description": "When the controller exposes per-zone countdown data-points (countdown_1.., DP 17.. by default), mirror each zone's duration to the hardware timer so a zone still auto-closes on schedule even if Homebridge or the network drops out mid-run. Disable to fall back to the software-timer-only behaviour. Has no effect on devices that don't report a countdown data-point.",
|
|
756
811
|
"condition": {
|
|
757
812
|
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type);"
|
|
758
813
|
}
|
|
@@ -818,49 +873,13 @@
|
|
|
818
873
|
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type) && !model.devices[arrayIndices].noBattery;"
|
|
819
874
|
}
|
|
820
875
|
},
|
|
821
|
-
"
|
|
822
|
-
"type": "boolean",
|
|
823
|
-
"title": "No rain sensor",
|
|
824
|
-
"description": "Disable the rain sensor (use if your controller doesn't report 'rain_sensor_state').",
|
|
825
|
-
"condition": {
|
|
826
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type);"
|
|
827
|
-
}
|
|
828
|
-
},
|
|
829
|
-
"rainSensorType": {
|
|
830
|
-
"type": "string",
|
|
831
|
-
"title": "Rain sensor type",
|
|
832
|
-
"default": "contact",
|
|
833
|
-
"oneOf": [
|
|
834
|
-
{ "title": "Contact sensor (Open / Closed)", "enum": ["contact"] },
|
|
835
|
-
{ "title": "Leak sensor (Leak Detected / Dry — note: fires critical alerts)", "enum": ["leak"] }
|
|
836
|
-
],
|
|
837
|
-
"condition": {
|
|
838
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type) && !model.devices[arrayIndices].noRainSensor;"
|
|
839
|
-
}
|
|
840
|
-
},
|
|
841
|
-
"rainInverted": {
|
|
842
|
-
"type": "boolean",
|
|
843
|
-
"title": "Invert rain sensor",
|
|
844
|
-
"description": "Flip the reported state if 'raining' and 'dry' appear reversed in HomeKit.",
|
|
845
|
-
"condition": {
|
|
846
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type) && !model.devices[arrayIndices].noRainSensor;"
|
|
847
|
-
}
|
|
848
|
-
},
|
|
849
|
-
"dpRain": {
|
|
876
|
+
"dpRainState": {
|
|
850
877
|
"type": ["integer", "string"],
|
|
851
878
|
"placeholder": "49 or rain_sensor_state",
|
|
852
|
-
"title": "Rain
|
|
879
|
+
"title": "Rain-sensor data-point (numeric id, or cloud code)",
|
|
880
|
+
"description": "Enum data-point reporting rainfall ('rain' / 'no_rain'), surfaced as a Leak Detected characteristic on the irrigation system so HomeKit can notify and automate on rain. Defaults to DP 49; if your controller doesn't report it, Leak Detected simply stays clear.",
|
|
853
881
|
"condition": {
|
|
854
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type)
|
|
855
|
-
}
|
|
856
|
-
},
|
|
857
|
-
"rainOnValue": {
|
|
858
|
-
"type": "string",
|
|
859
|
-
"placeholder": "rain",
|
|
860
|
-
"title": "Rain sensor 'raining' value",
|
|
861
|
-
"description": "The enum value the device reports when it is raining (default 'rain').",
|
|
862
|
-
"condition": {
|
|
863
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type) && !model.devices[arrayIndices].noRainSensor;"
|
|
882
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type);"
|
|
864
883
|
}
|
|
865
884
|
}
|
|
866
885
|
}
|
|
@@ -868,4 +887,4 @@
|
|
|
868
887
|
}
|
|
869
888
|
}
|
|
870
889
|
}
|
|
871
|
-
}
|
|
890
|
+
}
|