Tuya / Smart Life (Cloud)
Cloud bridge for any device paired in the Tuya, Smart Life, Brilliant, Geeni, Treatlife, or other Tuya-white-label apps. One device row represents a Tuya IoT Platform project; each Tuya device exposed to that project becomes a GEM zone keyed by the Tuya device id. The same project can front a single home's couple of smart plugs or hundreds of devices across a property — the Tuya Cloud OpenAPI is one of the largest device platforms in the world by install base and is a common request for retrofit installs that already have Tuya hardware in place.
Prerequisites
- A Tuya IoT Platform account at https://iot.tuya.com.
- A Cloud Development project (Cloud → Development → Create Cloud Project) with the IoT Core service enabled (the free tier is enough for control + status).
- The Smart Life / Tuya end-user account that owns the physical devices linked into the project under Devices → Link Tuya App Account.
- The project Data Center matched to the linked account's region (Tuya supports US, EU, China, India). Mismatched regions silently return empty device lists.
Setup
- In the Tuya IoT Platform, open Cloud → Development → <your project> → Authorization Key. Copy the Access ID and Access Secret.
- In GEM, go to System → Devices → Add Device (Open Devices) and pick the Tuya / Smart Life (Cloud) driver. Paste the Access ID into
client_idand the Access Secret intopassword(stored encrypted). - Set
regionto the project Data Center:us,eu,cn, orin. Useapi_baseonly when Tuya has migrated your account to a per-customer endpoint. - (Optional) Set
uidto the linked Smart Life user id (Devices → Link Tuya App Account → UID column). Ifuidis blank, the driver falls back to the project-wide device list endpoint, which returns only the first 50 devices — setuidfor any site with more than ~50 Tuya devices. - Run
get_devicesto enumerate Tuya devices visible to the project. Each entry returnsid,name,category,product_id, plus the dp code map. - Create a zone per device.
zone.address= the 20-character Tuya device id (e.g.bf6c...e3a1).
GEM does not auto-create zones for Tuya devices — add one zone per device by hand. Put each zone in the subsystem that matches what the device physically is: Lights for bulbs, Power for plugs and wall switches, Shades for curtains/blinds, Climate for thermostats, Fans for fans. Use on/off/set_level/set_color/set_ct for lights and plugs and open/close/stop for shade/curtain devices.
Device attributes
| Attribute | Required | Description |
|---|---|---|
client_id | yes | Tuya project Access ID. |
password | yes | Tuya project Access Secret. Stored encrypted. |
region | no (default us) | Tuya regional data center: us, eu, cn, in. |
api_base | no | Override the regional endpoint URL. |
uid | no | Linked Smart Life / Tuya user id. Without it the driver uses the project-wide device list endpoint. |
status_interval | no (default 30000 ms) | Polling cadence for /devices/<id>/status. Free-tier projects rate-limit at roughly 60 requests/min — keep this at 30 s or above for accounts with more than a handful of devices. |
request_timeout | no (default 10000 ms) | Per-request HTTP timeout. |
Zone attributes
| Attribute | Description |
|---|---|
address | Tuya device id from get_devices. |
power_code | Override the on/off data point code. Common values: switch_led (bulbs), switch_1 (multi-gang switches), switch (single-relay plugs). Auto-detected on first command if blank. |
level_code | Override the brightness dp. Common: bright_value_v2 (newer), bright_value (older). |
level_range | Per-zone [min, max] integer range for brightness. Defaults to [10, 1000]. Get the right range from get_specifications. |
ct_code | Override CT dp. Common: temp_value_v2. |
ct_range | [min, max] for CT. Defaults to [0, 1000]. |
color_code | Override HSV color dp. Common: colour_data_v2. |
control_code | For shades / curtains: override the dp used for open/close/stop. Defaults to control. |
State the driver reports back
On every status poll (and after each command that succeeds) the driver writes live state onto the zone, so you can drive triggers, tiles, and history off it:
| Attribute | Description |
|---|---|
state | on / off resolved from the device's power dp. For zones in a subsystem with relay-state semantics (e.g. gates, doors, garages), the value is mapped to that subsystem's state words. |
level | Brightness scaled back to 0-100 from the device's level_range. Written only when the device exposes a brightness dp. |
battery_level | 0-100 from the device's battery_percentage / battery dp, for battery-powered Tuya devices (sensors, locks). Written only when the device reports a battery dp. |
These names are cataloged, so they autocomplete in the Attribute editor and can be referenced directly in attribute triggers.
Zone address format
A 20-character Tuya device id, lower-case hexadecimal. Obtain via the get_devices command — each entry returns id, name, category, product_id, plus the dp code map.
Supported commands
| Command | Mapping |
|---|---|
on / off | Auto-resolves the power dp from zone.power_code or the status response, then `POST /commands { commands: [{code, value: true |
toggle | Reads /status, flips the power dp. |
set_level | bright_value_v2 scaled from GEM 0-100 to the device's level_range (default [10, 1000]). |
set_ct | temp_value_v2 scaled from GEM 0-100 to the device's ct_range (default [0, 1000]). |
set_color | Converts RRGGBB hex to Tuya HSV {h: 0-360, s: 0-1000, v: 0-1000} and sends via colour_data_v2. |
open / close / stop | control dp with the verb name (Tuya convention for shades / curtains). |
get_devices | Lists devices visible to the project. With uid set it returns the full linked-account list via /v1.0/users/<uid>/devices; without uid it returns the project-wide list via /v1.3/iot-03/devices, capped at the first 50 devices (no paging). |
get_status | Reads /v1.0/iot-03/devices/<id>/status — the dp value array. |
get_specifications | Reads /v1.0/iot-03/devices/<id>/specifications — the dp catalog with codes, types, and value ranges. Use this to populate level_range / ct_range for a specific product. |
refresh_token | Force a token refresh. |
raw_command | Send any dp value: {code, value}. value accepts primitives or JSON for HSV / scene blobs. |
raw_request | Pass-through to any /v1.x Tuya endpoint with full signing. Useful for scenes, automations, schedule queries. |
What we don't yet support
- Tuya Cloud pulsar message stream (real-time push). The driver polls
/statusper zone; for sub-second updates you'd need a WebSocket subscription to Tuya's message service, which requires a paid tier and a separate transport. - Scene execution beyond
raw_request. Scenes are project-scoped under/v2.0/cloud/scene/rule— easily reachable viaraw_requestbut no first-class command surface. - Automatic dp range discovery.
set_leveldefaults to[10, 1000]; for devices that use a different range, setzone.level_rangefrom theget_specificationsoutput until per-zone auto-detection is wired. - IR-blaster sub-devices (Tuya treats them as a parent IR hub + virtual children). The driver currently surfaces only the parent.
Troubleshooting
sign invalid. Clock skew between the GEM host and Tuya's servers exceeds 60 seconds. Runtimedatectl statusand confirm NTP is healthy.get_devicesreturns empty. Most often the project Data Center doesn't match the Smart Life account's region —usprojects can't seeeuaccounts. Double-check Devices → Link Tuya App Account shows the account as linked.- HTTP 401 /
token invalid. The Access Secret was rotated in the Tuya IoT Platform, or the project's IoT Core service has expired (free tier auto-renews but lapsed projects do happen). The driver auto-retries once with a fresh token on a 401 or on Tuya result code1010(token expired/invalid); if it persists, runrefresh_tokenmanually. get_devicesshows fewer devices than the account has. Withoutuid, the project-wide list is capped at the first 50 devices. Setuidto the linked Smart Life account id (Devices → Link Tuya App Account → UID) to enumerate the full account.- Command returns
success: truebut the device doesn't change. The dp code is wrong for this product. Runget_status— the live dp codes will be in the response. Setzone.power_code/zone.level_codeto the right code and try again. - Rate limit errors (
code: 28841105or similar). You're polling too aggressively for a free-tier project. Bumpstatus_intervalto 60000 and shrink the zone count under a single Tuya project, or upgrade the project tier.
Related documentation
- Devices — add and configure the Tuya project device.
- Zones — create one zone per Tuya device, placed in the matching subsystem.
- Subsystems — the Lights / Power / Shades / Climate / Fans subsystems these zones belong to.
- Commands — the command grid and Test runner for
on/off,set_level,set_color,get_devices,get_specifications,raw_command, andraw_request.