Following on from my original post on how to use Tuya devices and nodered, here is a Tuya and virtual switch example.
This is to control a smart switch controlling a pool pump.
The intention was to ensure that you could still use the native Tuya app and not end up with conflicts with the GX.
As a recap tuya data is sent/received in a dps array. Power on/off is dps[1]. To see what attributes your device supports you need to follow the how-to linked to above.
The logic is simple.
The Tuya device is polled via an inject node to retrieve the power state. Tuya power is controlled through boolean values and virtual switches use 1 and 0, so I use functions to convert to/from boolean as required.
There is an intentional feedback loop but a filter node on the Tuya device output prevents the flow continuously cycling.
[
{
"id": "ec0208710ac499cc",
"type": "inject",
"z": "20fec985ee013124",
"name": "GET state",
"props": [
{
"p": "payload"
}
],
"repeat": "10",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "{\"operation\":\"GET\",\"payload\":{\"dps\":1}}",
"payloadType": "json",
"x": 170,
"y": 200,
"wires": [
[
"b98bd0a5a9bf2054"
]
]
},
{
"id": "b98bd0a5a9bf2054",
"type": "tuya-smart-device",
"z": "20fec985ee013124",
"deviceName": "PoolPump",
"disableAutoStart": false,
"deviceId": "",
"deviceKey": "",
"storeAsCreds": true,
"deviceIp": "192.168.1.137",
"retryTimeout": 1000,
"findTimeout": 10000,
"tuyaVersion": "3.1",
"eventMode": "event-data",
"logLevel": "log-level-debug",
"x": 630,
"y": 240,
"wires": [
[
"e831f71357da5c22"
],
[]
]
},
{
"id": "3c22a235992df364",
"type": "inject",
"z": "20fec985ee013124",
"name": "ON",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "true",
"payloadType": "bool",
"x": 150,
"y": 240,
"wires": [
[
"b69053aa2ff3b9dd"
]
]
},
{
"id": "d37d814ce8a8c8fc",
"type": "inject",
"z": "20fec985ee013124",
"name": "OFF",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "false",
"payloadType": "bool",
"x": 150,
"y": 300,
"wires": [
[
"b69053aa2ff3b9dd"
]
]
},
{
"id": "b69053aa2ff3b9dd",
"type": "function",
"z": "20fec985ee013124",
"name": "",
"func": "msg.payload ={\n dps : 1,\n set : msg.payload\n}\n\n/*\n\n */\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 420,
"y": 240,
"wires": [
[
"b98bd0a5a9bf2054"
]
]
},
{
"id": "763564867c8d0939",
"type": "inject",
"z": "20fec985ee013124",
"d": true,
"name": "GET power",
"props": [
{
"p": "payload"
}
],
"repeat": "15",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "{\"operation\":\"GET\",\"payload\":{\"dps\":23}}",
"payloadType": "json",
"x": 170,
"y": 160,
"wires": [
[
"b98bd0a5a9bf2054"
]
]
},
{
"id": "faa882fee8907dae",
"type": "function",
"z": "20fec985ee013124",
"name": "Convert boolean",
"func": "// Get the value from payload.data.dps[\"1\"]\nlet value = msg.payload.data.dps[\"1\"];\n\n// Convert true/false to 1/0\nlet state;\nif (value === true) {\n state = 1;\n} else if (value === false) {\n state = 0;\n} else {\n state = null; // optional fallback\n}\n\n// Set payload in required JSON format\nmsg.payload = {\n \"SwitchableOutput/output_1/State\": state\n};\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 210,
"y": 380,
"wires": [
[
"ec1157a4803d566f"
]
]
},
{
"id": "ec1157a4803d566f",
"type": "victron-virtual-switch",
"z": "20fec985ee013124",
"name": "Pool pump switch",
"outputs": 2,
"switch_1_type": "1",
"switch_1_initial": 0,
"switch_1_label": "",
"switch_1_customname": "Pool Pump",
"switch_1_group": "Pumps",
"switch_1_include_measurement": false,
"switch_1_rgb_color_wheel": false,
"switch_1_cct_wheel": false,
"switch_1_rgb_white_dimmer": false,
"x": 210,
"y": 440,
"wires": [
[],
[
"799428095d9e1232"
]
]
},
{
"id": "799428095d9e1232",
"type": "function",
"z": "20fec985ee013124",
"name": "convert to boolean",
"func": "// Convert 0/1 to boolean false/true\nif (msg.payload === 1) {\n msg.payload = true;\n} else if (msg.payload === 0) {\n msg.payload = false;\n}\nmsg.payload ={\n dps : 1,\n set : msg.payload\n}\n\n/*\n\n */\nreturn msg;\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 470,
"y": 420,
"wires": [
[
"b98bd0a5a9bf2054"
]
]
},
{
"id": "e831f71357da5c22",
"type": "rbe",
"z": "20fec985ee013124",
"name": "",
"func": "rbei",
"gap": "",
"start": "",
"inout": "out",
"septopics": true,
"property": "payload",
"topi": "topic",
"x": 430,
"y": 320,
"wires": [
[
"faa882fee8907dae"
]
]
},
{
"id": "8ad5ee195137c4a3",
"type": "global-config",
"env": [],
"modules": {
"node-red-contrib-tuya-smart-device": "5.4.0",
"@victronenergy/node-red-contrib-victron": "1.7.3"
}
}
]
And on VRM it appears:
And the console UI:

