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.
- Create a GEM device with driver
tuya_cloud. Paste the Access ID into client_id and the Access Secret into password (stored encrypted).
- Set
region to the project Data Center: us, eu, cn, or in. Use api_base only when Tuya has migrated your account to a per-customer endpoint.
- (Optional) Set
uid to the linked Smart Life user id (Devices → Link Tuya App Account → UID column). If uid is blank, the driver falls back to the project-wide device list endpoint.
- Run
get_devices to enumerate Tuya devices visible to the project.
- Create a zone per device.
zone.address = the 20-character Tuya device id (e.g. bf6c...e3a1).
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. |
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. Uses /users/<uid>/devices when uid is set, otherwise /v1.3/iot-03/devices. |
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
/status per 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 via raw_request but no first-class command surface.
- Automatic dp range discovery.
set_level defaults to [10, 1000]; for devices that use a different range, set zone.level_range from the get_specifications output 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. Run timedatectl status and confirm NTP is healthy.
get_devices returns empty. Most often the project Data Center doesn't match the Smart Life account's region — us projects can't see eu accounts. 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 on 401 with a fresh token; if it persists, run refresh_token manually.
- Command returns
success: true but the device doesn't change. The dp code is wrong for this product. Run get_status — the live dp codes will be in the response. Set zone.power_code / zone.level_code to the right code and try again.
- Rate limit errors (
code: 28841105 or similar). You're polling too aggressively for a free-tier project. Bump status_interval to 60000 and shrink the zone count under a single Tuya project, or upgrade the project tier.