Tuya Integration
Tuya Integration connects ThingsBoard to the Tuya IoT Platform (Tuya Cloud). It subscribes to the Tuya Message Service and receives status reports from devices added in the Smart Life or Tuya Smart app. An uplink data converter turns each report into telemetry and attributes. ThingsBoard can also send downlink commands to the devices through the Tuya OpenAPI.
Use Tuya Integration when:
- Your devices are Tuya-based and already connected to Tuya Cloud through the Smart Life or Tuya Smart app.
- You want to monitor them in ThingsBoard dashboards and process their data in the Rule Engine.
- You need to control them from ThingsBoard, for example switch a smart plug on or off.
This guide uses a Tuya Smart Plug. A virtual Tuya device works for testing too.
Architecture
Section titled “Architecture”- Receive data. The device reports its status to Tuya Cloud over Wi-Fi. The integration subscribes to the Message Service (an Apache Pulsar queue) with your cloud project credentials and receives each status report.
- Decode the payload. The integration decrypts the report and passes it to the uplink converter, which returns the device name, telemetry, and attributes.
- Process the message. ThingsBoard processes the converted data through the Rule Engine. If Allow create devices or assets is enabled, ThingsBoard creates the device on its first report.
- Send downlink data (optional). The Rule Engine passes an RPC message to the integration. The downlink converter encodes it as a Tuya command, and the integration sends it to the Tuya OpenAPI, which delivers it to the device.
Prerequisites
Section titled “Prerequisites”- Access to ThingsBoard Cloud with integrations enabled for your tenant.
- Permission to create integrations and data converters, and to edit rule chains for downlink.
- A Tuya IoT Platform account. To create one, see Create a Tuya IoT Platform Account.
- A Tuya device (physical or virtual) added in the Smart Life or Tuya Smart app. To add a smart plug, see Register the Smart Plug in the Tuya App.
Set Up Tuya
Section titled “Set Up Tuya”Add the device to the Tuya app, create a cloud project, enable the Message Service, and link the app account to the project.
Register the Smart Plug in the Tuya App
Section titled “Register the Smart Plug in the Tuya App”- Open the Smart Life (or Tuya Smart) app and tap Add Device.
- Open the Electrical category and tap Socket (Wi-Fi).
- Power the plug off for 10 seconds, then power it back on.
- Press and hold the RESET button for 5 seconds.
- Check that the indicator blinks rapidly, then tap Next.
- Wait until the app adds the device, then tap Done.
- Open the device card and tap the power button to check that the plug responds.
Create a Tuya IoT Platform Account
Section titled “Create a Tuya IoT Platform Account”- Open the Tuya IoT Platform login page and click Sign Up.
- Enter your Email, the Verification Code sent to it, a Password, the Organization Name, and the Country/Region. Accept the terms and click Next.
- Log in with the new account.
Create a Cloud Project
Section titled “Create a Cloud Project”- In the sidebar, go to Cloud ⇾ Cloud Project ⇾ Project Management.
- Click Create Cloud Project. Enter a Project Name (for example,
ThingsBoard), choose an Industry and a Development Method (for example, Custom), and select the Data Center. Click Create. - On the Authorize API Services screen, keep the recommended services (IoT Core is required) and click Authorize.
- On the project Overview page, copy the Access ID/Client ID and Access Secret/Client Secret under Authorization Key. You need them, together with the data center, to create the integration.
Enable the Message Service
Section titled “Enable the Message Service”The Message Service pushes device status reports to subscribers such as the integration. Without it, no uplinks arrive.
- In the cloud project, open the Message Service tab and turn on Message Service.
- In the Configure Message Service dialog, select the Message Service Type and the Message encryption algorithm, then click OK. The integration decrypts both AES-ECB and AES-GCM messages.
- Check that the tab shows the service as Enabled.
Configure Messaging Rules
Section titled “Configure Messaging Rules”A messaging rule defines which messages Tuya pushes. Tuya keeps separate rules and subscriptions for two environments:
| Tuya environment | Devices | Integration Environment |
|---|---|---|
| Production | Real (physical) devices. | PROD |
| Test | Only devices you add as test devices, for example virtual devices. | TEST |
The rule must be enabled in the environment where your device reports, and the integration must use the same environment. Otherwise the integration connects, but no uplinks arrive.
- In the cloud project, open Message Service ⇾ Messaging Rules.
- Select Production Environment for a real device, or Test Environment for a test device.
- Turn on the rule. While it is off, the tab shows “The rules for the production environment are disabled, so all messages will not be pushed”.
- Click Modify Messaging Rules, make sure BizCode includes
statusReport(Status Report), and save. Optionally addonlineandoffline. - Open Subscription Management, select the same environment, and check that the subscription
<Access ID>-subhas Status = Good.
Link Your Tuya App Account
Section titled “Link Your Tuya App Account”Linking the app account adds its devices to the cloud project.
- Go to Devices ⇾ Link App Account, click Add App Account, and select Tuya App Account Authorization.
- Scan the QR code in the Smart Life (or Tuya Smart) app: Me ⇾ Scan.
- In the Link App Account dialog, select Automatic Link and click OK.
- Confirm the linking. Click Manage Devices on the app account row and check that the smart plug appears as Online.
Set Up Tuya Integration
Section titled “Set Up Tuya Integration”Create the uplink converter, assign it to a new Tuya integration, and trigger a status report from the device.
Create the Uplink Data Converter
Section titled “Create the Uplink Data Converter”The uplink converter receives each status report as JSON, resolves the device by its Tuya ID, and returns its telemetry. Tuya Integration uses the Generic converter: you write the decoder in TBEL or JavaScript. For converter types and the full decoder reference, see Uplink data converter.
-
Go to Integrations ⇾ Data converters and click + Add data converter ⇾ Create new converter.
-
Converter type — keep Uplink.
-
Integration type — select Tuya.
-
Name — enter a name, for example
Tuya Uplink Converter. -
Main decoding configuration — the editor opens with the generic default decoder. Replace it with the decoder below. It reads the
code–valuepairs from thestatusarray and scales the smart plug readings:cur_voltageandcur_power÷ 10,cur_current÷ 100. The editor opens in TBEL; use the TBEL / JS toggle to switch to JavaScript./** Decoder **/// Parse the incoming payload (byte array) into a JSON object.// decodeToJson is a built-in TBEL helper — no need to declare it.var data = decodeToJson(payload);// Also accept the integration event format { metadata, data },// for example when you test with a payload copied from the Events tab.if (data.?data != null) {data = data.data;}// Resolve the device name.// Try bizData.devId first (present in some Tuya message types);// fall back to devId otherwise. The .? operator safely returns null// if an intermediate field is missing (won't throw an error).var deviceName = data.?bizData.?devId != null ? data.?bizData.?devId : data.?devId;// Fixed device type — used to create the Device Profile in ThingsBoard.var deviceType = 'Tuya device';// Array that will hold the telemetry (key-value pairs).var telemetry = [];// Tuya's main data arrives in the status array. Make sure it exists.if (data.status != null) {// Iterate over each element of the status array.for (var i = 0; i < data.status.length; i++) {var res = {}; // temp object for a single data pointvar code = data.status[i].code; // parameter name, e.g. "cur_voltage"var value = data.status[i].value; // raw value from the device// Tuya sends some values as integers without a decimal point.// Divide voltage and power by 10 to get real units (V, W).if (code == "cur_voltage" || code == "cur_power") {value = data.status[i].value / 10;// Divide current by 100 (to get A).} else if (code == "cur_current") {value = data.status[i].value / 100;}res[code] = value; // build a pair, e.g. { "cur_voltage": 231 }telemetry.push(res); // add it to the telemetry array}} else {// If there is no status array, return the payload as-is (fallback).telemetry = data;}// Build the result in the format ThingsBoard expects.var result = {deviceName: deviceName, // device is created/matched by this namedeviceType: deviceType, // device profileattributes: {}, // attributes (empty here)telemetry: telemetry // time-series data};/** Helper functions 'decodeToString' and 'decodeToJson' are already built-in **/return result;/** Decoder **/// In JS the helpers are NOT built-in, so we declare them ourselves.// Convert a byte array into a string.function decodeToString(payload) {return String.fromCharCode.apply(String, payload);}// Convert a byte array into JSON: first to a string, then parse it.function decodeToJson(payload) {var str = decodeToString(payload);var data = JSON.parse(str);return data;}// Parse the incoming payload into a JSON object.var data = decodeToJson(payload);// Also accept the integration event format { metadata, data },// for example when you test with a payload copied from the Events tab.if (data.data) {data = data.data;}// Device name: use bizData.devId if present, otherwise devId.// JS has no .? operator, so we check existence with && .var deviceName = (data.bizData && data.bizData.devId) ? data.bizData.devId : data.devId;// Fixed device type (profile).var deviceType = 'Tuya device';// Array for telemetry.var telemetry = [];// Tuya data lives in the status array.if (data.status != null) {for (var i = 0; i < data.status.length; i++) {var res = {}; // a single data pointvar code = data.status[i].code; // parameter namevar value = data.status[i].value; // raw value// Unit scaling for the Smart Plug.if (code == "cur_voltage" || code == "cur_power") {value = data.status[i].value / 10; // voltage/power ÷10} else if (code == "cur_current") {value = data.status[i].value / 100; // current ÷100}res[code] = value; // e.g. { "cur_power": 15 }telemetry.push(res); // add to the array}} else {telemetry = data; // fallback if status is missing}// Result for ThingsBoard.var result = {deviceName: deviceName,deviceType: deviceType,attributes: {},telemetry: telemetry};return result;To adapt:
- Other data points — add or change the
codebranches. Codes without a branch are stored as received. - Other scaling — change the divisors. For example, some models report
add_ele(energy, kWh) that needs ÷ 100 or ÷ 1000. - Friendly key names — map codes explicitly, for example
cur_voltagetovoltage. - Device timestamp — each
statusentry carriest(ms). Return{ ts: data.status[i].t, values: res }instead ofresto store it instead of the server time. - Device properties — add them to
attributes, for exampleproductKey: data.productKey.
- Other data points — add or change the
-
Click Test decoder function and check the result (see Test the Decoder).
-
Click Add.
Decoder Inputs
The decoder signature is function decoder(payload, metadata): object | object[].
| Variable | Type | Content |
|---|---|---|
payload |
byte array | The decrypted status report (JSON). Parse it with decodeToJson(payload). |
metadata |
{[key: string]: string} |
Message Service envelope: topic, t (send time, ms), pv (protocol version), protocol, and sign. Also integrationName and the key–value pairs from the integration’s Metadata field. |
Status report fields:
| Field | Content |
|---|---|
devId |
Tuya device ID, for example bfac3c40adca459c9chaar. |
productKey |
Product key of the device model, for example iq9hwd2bjsdgqiby. |
dataId |
Unique ID of the report. |
status |
Data points. Each entry has code, value, and t (ms). An entry can also carry a numeric alias key, for example "1": true; the decoder ignores it. |
The integration’s Events tab shows each message as { "metadata": {...}, "data": {...} }. The decoder receives only data as payload.
Test the Decoder
Click Test decoder function below the editor. Keep Payload content type set to JSON, paste this status report as the payload, and click Test:
{ "dataId": "000657E477596B12C80347616A03002A", "devId": "bfac3c40adca459c9chaar", "productKey": "iq9hwd2bjsdgqiby", "status": [ { "code": "cur_voltage", "t": 1785490000000, "value": 2086 }, { "code": "cur_power", "t": 1785490000000, "value": 142 }, { "code": "cur_current", "t": 1785490000000, "value": 63 } ]}The Output panel shows:
{ "deviceName": "bfac3c40adca459c9chaar", "deviceType": "Tuya device", "attributes": {}, "telemetry": [ { "cur_voltage": 208.6 }, { "cur_power": 14.2 }, { "cur_current": 0.63 } ]}Check that deviceName equals the devId and that telemetry holds the scaled values. If the output is empty or shows an error, fix the decoder and test again.
Create the Integration
Section titled “Create the Integration”- Go to Integrations ⇾ Integrations and click + Add integration.
- Basic settings:
- Integration type — select Tuya.
- Name — enter a name, for example
Tuya integration. - Enable integration and Allow create devices or assets — keep both on, so the device is created on its first report.
- Click Next.
- Uplink data converter:
- Click Select existing and choose
Tuya Uplink Converter. - Alternatively, click Create new to write a decoder here.
- Click Next.
- Click Select existing and choose
- Downlink data converter:
- Click Skip. Add a downlink converter later if you need to send commands to devices.
- Connection:
- Region — select the data center of your cloud project.
- Environment — select PROD for a real device or TEST for a test device.
- Access Id and Access Key — paste the Access ID/Client ID and Access Secret/Client Secret from the cloud project.
- See Connection Settings for details.
- Click Add.
Connection Settings
Section titled “Connection Settings”| Parameter | Description |
|---|---|
| Region | Data center of the cloud project: China (default), United States (Western America), Europe (Central Europe), or India. Selects the Message Service and OpenAPI endpoints, for example mqe.tuyaeu.com and openapi.tuyaeu.com for Europe. |
| Environment | PROD (default) or TEST. PROD reads the production topic <Access Id>/out/event; TEST reads <Access Id>/out/event-test. Must match the environment of the messaging rule. |
| Access Id | Access ID/Client ID of the cloud project. The integration subscribes as <Access Id>-sub. |
| Access Key | Access Secret/Client Secret of the cloud project. Signs OpenAPI requests and decrypts messages. Keep it secret. |
| Execute remotely | Runs the integration as a separate process outside the ThingsBoard cluster. ThingsBoard generates an Integration key and Integration secret for it. See Remote Integration. |
| Description | In Advanced settings. Free-text description. |
| Metadata | In Advanced settings. Key–value pairs added to the metadata of every message from this integration. |
If the connection fails, the integration retries every 60 seconds and logs a CONNECT event with the error on its Events tab.
Test Uplink
Section titled “Test Uplink”Trigger a status report, then confirm that ThingsBoard decoded it and created the device.
Send a Test Uplink
Section titled “Send a Test Uplink”Unplug the smart plug and plug it back in, or switch it in the Smart Life app. The device sends a statusReport to Tuya Cloud, which pushes it to the integration.
Verify the Result
Section titled “Verify the Result”- Integration events — go to Integrations ⇾ Integrations, open the integration, and click the Events tab. The uplink appears with status OK. Click
⋅⋅⋅in the Message column to see the metadata and the report. - Converter events — open
Tuya Uplink Converterin Integrations ⇾ Data converters and click the Events tab. In shows the report, Out shows the decoded result, and Metadata shows the Message Service envelope. - Device — go to Devices & assets ⇾ Devices. The integration has created a device named after the Tuya
devId. Open it and check the Latest telemetry tab, for examplecur_power,cur_voltage,cur_current, andswitch_1.
Configure Downlink
Section titled “Configure Downlink”ThingsBoard sends a command to the device when the Rule Engine routes an RPC request to the integration — for example, when a dashboard switch is toggled. The integration calls the Tuya OpenAPI of the selected region:
- It takes the Tuya device ID from
deviceIdin the encoder’s outputmetadata, or fromdeviceNameifdeviceIdis not set. - It sends
datatoPOST /v1.0/iot-03/devices/{deviceId}/commandsas{ "commands": [...] }.
The encoder’s data must be one of:
data |
Sent to Tuya |
|---|---|
An object with code and value, for example {"code": "switch_1", "value": true} |
{"commands": [{"code": "switch_1", "value": true}]} |
| An array of such objects | {"commands": [...]} with every command |
An object with method set to a query name: getStatus, getFunctions, getSpecification, getLogs, getReportLogs, or getCategory |
A GET request to the matching OpenAPI endpoint. The response goes to the uplink converter as payload, with msgType = DownlinkResponse and deviceId in metadata. |
Any other data, including {"commands": [...]}, fails with “Downlink message format is not correct, downlink message should contain one of the following: code field, service RPC method, commands array!”.
The downlink converter turns a Rule Engine message into a Tuya command. For the full encoder reference, see Downlink data converter.
Create and Assign the Downlink Converter
Section titled “Create and Assign the Downlink Converter”The encoder below uses the RPC method as the Tuya command code and the RPC params as its value. It converts "true" and "false" to booleans, because RPC params arrive as strings. It sets deviceId to the device name, which is the Tuya devId.
- Open the integration in Integrations ⇾ Integrations and click Toggle edit mode (pencil icon).
- In the Downlink data converter field, click Create new. The Downlink converter type and Tuya integration type are preselected.
- Enter a name, for example
Tuya Downlink Converter. - In Main encoding configuration, replace the default encoder with the encoder below.
/** Encoder **/// The command object we will send to the Tuya device.var command = {};// msg.method — the RPC method name from ThingsBoard. Becomes the Tuya// command code, e.g. "switch_1", "bright_value", etc.command["code"] = msg.method;// msg.params — the command value.// If it is the string "true"/"false", convert it to a real boolean// (needed for switches, e.g. turning the plug on/off).if (msg.params == "false" || msg.params == "true") {command["value"] = Boolean.valueOf(msg.params);} else {// Otherwise pass it as-is (number, string, e.g. brightness level).command["value"] = msg.params;}// Build the result for the integration.var result = {contentType: "JSON", // command body is JSONdata: JSON.stringify(command), // serialize { code, value } to a stringmetadata: {deviceId: metadata.deviceName // which device to send the command to}};return result;/** Encoder **/// Build the command object.const command = {// Tuya command code = RPC method name from ThingsBoard.code: msg.method,// Command value:// if msg.params is "true"/"false", convert it to a boolean (true/false),// otherwise keep the value as-is.value:msg.params === "false" || msg.params === "true"? msg.params === "true": msg.params};// Result to send through the integration.const result = {contentType: "JSON", // body is JSONdata: JSON.stringify(command), // serialize { code, value }metadata: {deviceId: metadata.deviceName // target device}};return result;
- To record downlink events, click the debug icon next to the name, select All messages, and click Apply.
- Click Add, then Apply changes.
Configure the Root Rule Chain
Section titled “Configure the Root Rule Chain”RPC messages already carry deviceName in their metadata, so a single integration downlink node is enough.
- Go to Data processing ⇾ Rule chains and open the Root Rule Chain.
- From the Action section, drag an integration downlink node onto the canvas.
- Enter a Name (for example,
Downlink to Tuya), select your Tuya integration, and click Add. - Connect the message type switch node to the integration downlink node with the RPC Request to Device relation.
- Click Apply changes.
Test Downlink
Section titled “Test Downlink”Import the Tuya Smart Plug dashboard. It shows the uplink data and sends commands to the plug:
- Time series chart and Simple neon gauge — voltage, power, and current.
- Led indicator — the on/off state from the
switch_1telemetry. - Round switch — sends an RPC with method
switch_1and paramstrueorfalse.
- Download tuya_smart_plug_dashboard.json.
- Go to Dashboards, click + Add dashboard ⇾ Import dashboard, upload the file, and click Import.
- Open the Tuya smart plug dashboard and enter edit mode.
- Click the Entity aliases icon, click the pencil icon next to the
smartPlugalias, select your device, and click Save. Then click Save in the lower-right corner. - Toggle the Round switch.
If a widget shows no data, edit it and change the data keys in Datasource to the codes your device reports.
Verify the Downlink
Section titled “Verify the Downlink”- Device — the plug switches, and the Led indicator follows after the next status report.
- Converter events — open the downlink converter’s Events tab. In shows the RPC message, Out shows the encoded command, and Metadata shows the message metadata.
- Integration events — the Downlink event has status SUCCESS. On FAILURE, the event shows the error returned by Tuya Cloud.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
| CONNECT event with status FAILURE | Wrong Access Id, Access Key, or Region. | Copy the credentials from the project Overview page again. Select the Region that matches the project data center. |
| Integration connects, but no uplinks arrive | The messaging rule is off, or enabled in the other environment. | Enable the rule with statusReport in the environment that matches Environment. See Configure Messaging Rules. |
| Same, with the rule enabled | The device is not linked to the cloud project. | Link the app account. See Link Your Tuya App Account. |
| Subscription Status is not Good | The messaging rule was disabled or changed. | Enable the rule for the matching environment and check the subscription again. |
| Device not created | Allow create devices or assets is off, or deviceName is empty. |
Turn the option on. Check Out in the converter events for deviceName. |
Only switch_1 arrives |
The plug has no load. | Connect a load and wait for the next report. |
| Downlink fails with “Downlink message format is not correct…” | data has neither code, a query method, nor an array of commands. |
Check Out in the downlink converter events. Make sure the RPC method reaches msg.method. |
| Downlink fails with “Device identifier not found in metadata” | The encoder’s output metadata has neither deviceId nor deviceName. |
Set deviceId in the encoder’s metadata. |
Downlink fails with Illegal character in path |
The device name is not the raw Tuya devId. |
Rename the device to its devId. Use Label for a readable name. |
| Downlink fails with “Tuya integration response error code…” | Tuya Cloud rejected the command, for example the code is not writable. |
Check the writable codes in Device Debugging in the Tuya IoT Platform. |
| No downlink event | The rule chain does not reach the integration downlink node. | Connect the node with the RPC Request to Device relation. |
To read debug events, open the integration or converter, click Events, and select an event: In is the received data, Out is the converter result, and Error holds the error text.
See Also
Section titled “See Also”- Integrations overview — how integrations, converters, uplinks, and downlinks fit together.
- Uplink data converter — decoder inputs, output format, and scripting patterns.
- Downlink data converter — encoder reference.
- Remote Integration — run the integration outside the ThingsBoard cluster.
- TBEL — built-in functions and syntax for converter scripts.
- Rule Engine — how messages are processed after decoding.
Was this helpful?