diff --git a/CHANGELOG.md b/CHANGELOG.md index 04c1b21..962ef80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,22 @@ # Changelog +## 4.1.0 + +- Added CONTROL commands as input (CONNECT, DISCONNECT, RECONNECT) +- Seperate output node for device status and client status +- Dynamically set the find and retry timeout (Not saved permanently in the config) +- Disable auto connect on start + ## 4.0.2 - Added support for both data and dp-refresh event [#54](https://github.com/vinodsr/node-red-contrib-tuya-smart-device/issues/54) + ## 4.0.1 - Renamed data event to dp-refresh (Tuya 7.1 changes) ## 4.0.0 + - Update to Tuya 7.1.0. Closes [#51](https://github.com/vinodsr/node-red-contrib-tuya-smart-device/issues/51) ## 3.1.0 @@ -34,23 +43,24 @@ Fixes [#39](https://github.com/vinodsr/node-red-contrib-tuya-smart-device/issues Fixes [#33](https://github.com/vinodsr/node-red-contrib-tuya-smart-device/issues/33) - Tuya api library updated to 6.1.1 -- No more null data payload on json undefined error. :) - +- No more null data payload on json undefined error. :) ## 1.2.1 Fixes [#25](https://github.com/vinodsr/node-red-contrib-tuya-smart-device/issues/25) ## 1.2.0 -* Added a generic SET node which can be controller with message.payload. One node to set command for many devices. Thanks to formatBCE for the suggestion. + +- Added a generic SET node which can be controller with message.payload. One node to set command for many devices. Thanks to formatBCE for the suggestion. ## 1.1.4 -* Bug fixes + +- Bug fixes ## 1.1.3 -* Added more extensive retry logic. Previously on find error, the logic hangs +- Added more extensive retry logic. Previously on find error, the logic hangs ## 1.1.0 -* Initial Version +- Initial Version diff --git a/README.md b/README.md index 55c3a82..50c309c 100644 --- a/README.md +++ b/README.md @@ -4,32 +4,32 @@ ![Build and Publish package](https://github.com/vinodsr/node-red-contrib-tuya-smart-device/workflows/Build%20and%20Publish%20package/badge.svg) ![License](https://img.shields.io/github/license/vinodsr/node-red-contrib-tuya-smart-device) - A node-red module which helps you to connect to any tuya device. ![image](./img/sample.png) # Table of contents + - [Getting Started](#getting-started) - [Setup](#setup) - - [Input Format](#input-format) - - [Output Format](#output-format) + - [Input Format](#input-format) + - [Output Format](#output-format) - [License](#license) - [Contributing](#contributing) - # Getting Started Instructions for getting the device id is available [here](https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md) You will get the device id and the key once you run the wizard program as per the instructiions - +Get more details about latest version changes in the [CHANGELOG.md](./changelog.md) + # Setup [(Back to top)](#table-of-contents) -The node takes one input and one output. Once you drop the node into the flow, you need to use the deviceid and devicekey that you got from the getting started step. +The node takes one input and one output. Once you drop the node into the flow, you need to use the deviceid and devicekey that you got from the getting started step. Once you setup the node, you can then use input to send any command to the device as per the tuya standards. @@ -45,6 +45,10 @@ Once you setup the node, you can then use input to send any command to the devic ![image](./img/output.png) +# Examples + +You can refer the [example flow](./examples/latest.json) to get started + # License [(Back to top)](#table-of-contents) diff --git a/examples/latest.json b/examples/latest.json new file mode 100644 index 0000000..9a9ad33 --- /dev/null +++ b/examples/latest.json @@ -0,0 +1,243 @@ +[ + { + "id": "b2e04683.825318", + "type": "tab", + "label": "Flow 1", + "disabled": false, + "info": "" + }, + { + "id": "e63245d0.e6cdc8", + "type": "tuya-smart-device", + "z": "b2e04683.825318", + "deviceName": "Tv Light", + "disableAutoStart": true, + "deviceId": "", + "deviceKey": "", + "deviceIp": "", + "retryTimeout": "1001", + "findTimeout": "2000", + "tuyaVersion": "3.1", + "eventMode": "event-both", + "x": 680, + "y": 360, + "wires": [["ff4bd686.53ae18"], ["15d22ba6.824b84"]] + }, + { + "id": "c016131.e98faf", + "type": "function", + "z": "b2e04683.825318", + "name": "", + "func": "msg.payload ={\n //dps : 1,\n dps: 20,\n set : msg.payload\n}\n\n/*\n\n */\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "x": 510, + "y": 360, + "wires": [["e63245d0.e6cdc8"]] + }, + { + "id": "993c8470.01a278", + "type": "inject", + "z": "b2e04683.825318", + "name": "Turn On", + "props": [{ "p": "payload" }, { "p": "topic", "vt": "str" }], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "true", + "payloadType": "bool", + "x": 310, + "y": 320, + "wires": [["c016131.e98faf"]] + }, + { + "id": "6403cde.251ab34", + "type": "inject", + "z": "b2e04683.825318", + "name": "Turn Off", + "props": [{ "p": "payload" }, { "p": "topic", "vt": "str" }], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "false", + "payloadType": "bool", + "x": 310, + "y": 400, + "wires": [["c016131.e98faf"]] + }, + { + "id": "e4cfcdc.9520a3", + "type": "tuya-smart-device-generic", + "z": "b2e04683.825318", + "name": "", + "x": 660, + "y": 540, + "wires": [["1e77cd28.a8c303"]] + }, + { + "id": "81a451cc.919f9", + "type": "function", + "z": "b2e04683.825318", + "name": "", + "func": "msg.payload ={\n \"deviceVirtualId\": \"\",\n //deviceIp: \"192.168.1.71\",\n \"deviceKey\": \"\",\n \"deviceName\": \"PM\",\n \"operation\": \"SET\", // optional default : SET\n \"payload\": {\n \"dps\": 20,\n \"set\": msg.payload\n },\n // \"version\":\"3.2\"\n \n }\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "x": 450, + "y": 540, + "wires": [["e4cfcdc.9520a3"]] + }, + { + "id": "dc28dca8.674c1", + "type": "inject", + "z": "b2e04683.825318", + "name": "ON", + "props": [{ "p": "payload" }, { "p": "topic", "vt": "str" }], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "true", + "payloadType": "bool", + "x": 240, + "y": 580, + "wires": [["81a451cc.919f9"]] + }, + { + "id": "1e77cd28.a8c303", + "type": "debug", + "z": "b2e04683.825318", + "name": "", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 940, + "y": 540, + "wires": [] + }, + { + "id": "4295327a.09bf3c", + "type": "inject", + "z": "b2e04683.825318", + "name": "OFF", + "props": [{ "p": "payload" }, { "p": "topic", "vt": "str" }], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "false", + "payloadType": "bool", + "x": 240, + "y": 500, + "wires": [["81a451cc.919f9"]] + }, + { + "id": "8f69fe68.9e4ce", + "type": "catch", + "z": "b2e04683.825318", + "name": "", + "scope": null, + "uncaught": false, + "x": 640, + "y": 140, + "wires": [["3764c769.6e1298"]] + }, + { + "id": "3764c769.6e1298", + "type": "debug", + "z": "b2e04683.825318", + "name": "On Error", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "true", + "targetType": "full", + "statusVal": "", + "statusType": "auto", + "x": 870, + "y": 140, + "wires": [] + }, + { + "id": "ff4bd686.53ae18", + "type": "debug", + "z": "b2e04683.825318", + "name": "Device Data", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "true", + "targetType": "full", + "statusVal": "", + "statusType": "auto", + "x": 880, + "y": 300, + "wires": [] + }, + { + "id": "15d22ba6.824b84", + "type": "debug", + "z": "b2e04683.825318", + "name": "Node State", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "payload", + "targetType": "msg", + "statusVal": "", + "statusType": "auto", + "x": 880, + "y": 400, + "wires": [] + }, + { + "id": "47ea1d2a.7d85f4", + "type": "inject", + "z": "b2e04683.825318", + "name": "RECONNECT ACTION", + "props": [{ "p": "payload" }, { "p": "topic", "vt": "str" }], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "{\"operation\":\"CONTROL\",\"action\":\"RECONNECT\"}", + "payloadType": "json", + "x": 330, + "y": 220, + "wires": [["e63245d0.e6cdc8"]] + }, + { + "id": "1991e72.c15c719", + "type": "inject", + "z": "b2e04683.825318", + "name": "DISCONNECT ACTION", + "props": [{ "p": "payload" }, { "p": "topic", "vt": "str" }], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "{\"operation\":\"CONTROL\",\"action\":\"DISCONNECT\"}", + "payloadType": "json", + "x": 330, + "y": 160, + "wires": [["e63245d0.e6cdc8"]] + } +] diff --git a/img/input.png b/img/input.png index 79d902f..93bfd54 100644 Binary files a/img/input.png and b/img/input.png differ diff --git a/img/output.png b/img/output.png index 5f10cae..620db3a 100644 Binary files a/img/output.png and b/img/output.png differ diff --git a/img/sample.png b/img/sample.png index 4b31ccc..1bb1939 100644 Binary files a/img/sample.png and b/img/sample.png differ diff --git a/package.json b/package.json index d4c7e70..bb9f473 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red-contrib-tuya-smart-device", - "version": "4.0.2", + "version": "4.1.0", "description": "A node-red module to interact with the tuya smart devices", "repository": "https://github.com/vinodsr/node-red-contrib-tuya-smart-device", "main": "index.js", @@ -21,4 +21,4 @@ "dependencies": { "tuyapi": "7.1.0" } -} \ No newline at end of file +} diff --git a/src/tuya-smart-device.html b/src/tuya-smart-device.html index 48d0386..89feffd 100644 --- a/src/tuya-smart-device.html +++ b/src/tuya-smart-device.html @@ -1,176 +1,328 @@ \ No newline at end of file + + +
  • + Client state +
    +
    + state + String +
    +
    + Shows the current status of the node (CONNECTED, CONNECTING, + DISCONNECTED,ERROR) +
    +
    +
  • + + diff --git a/src/tuya-smart-device.js b/src/tuya-smart-device.js index 92de1ee..68585c6 100644 --- a/src/tuya-smart-device.js +++ b/src/tuya-smart-device.js @@ -1,191 +1,322 @@ const TuyaDevice = require("tuyapi"); +const CLIENT_STATUS = { + DISCONNECTED: "DISCONNECTED", + CONNECTED: "CONNECTED", + CONNECTING: "CONNECTING", + ERROR: "ERROR", +}; module.exports = function (RED) { - function TuyaSmartDeviceNode(config) { - RED.nodes.createNode(this, config); - let node = this; - let isConnected = false; - let shouldTryReconnect = true; - let shouldSubscribeData = true; - let shouldSubscribeRefreshData = true; - this.name = config.deviceName; - this.deviceName = config.deviceName; - this.deviceId = config.deviceId; - this.deviceIp = config.deviceIp; - this.eventMode = config.eventMode || 'event-both' - node.log(`Recieved the config ${JSON.stringify(config)}`); - this.retryTimeout = (config.retryTimeout == null || typeof config.retryTimeout == "undefined" || (typeof config.retryTimeout == "string" && config.retryTimeout.trim() == "") || (typeof config.retryTimeout == "number" && config.retryTimeout <= 0) || isNaN(config.retryTimeout)) ? 1000 : config.retryTimeout; - this.findTimeout = (config.findTimeout == null || typeof config.findTimeout == "undefined" || (typeof config.findTimeout == "string" && config.findTimeout.trim() == "") || (typeof config.findTimeout == "number" && config.findTimeout <= 0) || isNaN(config.findTimeout)) ? 1000 : config.findTimeout; - this.tuyaVersion = (config.tuyaVersion == null || typeof config.tuyaVersion == "undefined" || (typeof config.tuyaVersion == "string" && config.tuyaVersion.trim() == "") || (typeof config.tuyaVersion == "number" && config.tuyaVersion <= 0) || isNaN(config.tuyaVersion)) ? '3.1' : config.tuyaVersion.trim(); - - if (this.eventMode == 'event-data') { - shouldSubscribeData = true; - shouldSubscribeRefreshData = false; - } else if (this.eventMode == 'event-dp-refresh') { - shouldSubscribeData = false; - shouldSubscribeRefreshData = true; - } else { // both case or default case - shouldSubscribeData = true; - shouldSubscribeRefreshData = true; - } - node.log(`Event subscription : shouldSubscribeData=>${shouldSubscribeData} , shouldSubscribeRefreshData=>${shouldSubscribeRefreshData}`); - if (this.retryTimeout <= 0) { - this.retryTimeout = 1000; - } - - if (this.findTimeout <= 0) { - this.findTimeout = 1000; - } - let findTimeoutHandler = null; - let retryTimerHandler = null; - - this.deviceKey = config.deviceKey; - node.on('input', function (msg) { - let operation = msg.payload.operation || 'SET'; - delete msg.payload.operation; - switch (operation) { - case "SET": - tuyaDevice.set(msg.payload); - break; - case "REFRESH": - tuyaDevice.refresh(msg.payload); - break; - case "GET": - tuyaDevice.get(msg.payload); - break; - } - }); - let setStatusConnecting = function () { return node.status({ fill: "yellow", shape: "ring", text: "connecting" }); }; - let setStatusConnected = function () { return node.status({ fill: "green", shape: "ring", text: "connected" }); }; - let setStatusDisconnected = function () { return node.status({ fill: "red", shape: "ring", text: "disconnected" }); }; - var setStatusOnError = function (errorText, errorShortText = "error", data) { - node.error(errorText, data); - return node.status({ fill: "red", shape: "ring", text: errorShortText }); - }; - const connectionParams = { - id: node.deviceId, - key: node.deviceKey, - ip: node.deviceIp, - issueGetOnConnect: false, - nullPayloadOnJSONError: false, - version: node.tuyaVersion - }; - node.log(`Connecting to Tuya with params ${JSON.stringify(connectionParams)} , findTimeout : ${node.findTimeout} , retryTimeout: ${node.retryTimeout} `); - let tuyaDevice = new TuyaDevice(connectionParams); - - let retryConnection = () => { - clearTimeout(retryTimerHandler); - retryTimerHandler = setTimeout(() => { - connectDevice(); - }, node.retryTimeout) - } - node.on('close', function () { - // tidy up any state - // clearInterval(int); - node.log("Cleaning up the state"); - shouldTryReconnect = false; - tuyaDevice.disconnect(); - node.log("Clearing the find timeout handler"); - clearTimeout(findTimeoutHandler); + function TuyaSmartDeviceNode(config) { + RED.nodes.createNode(this, config); + config.disableAutoStart = config.disableAutoStart || false; + let node = this; + let isConnected = false; + let shouldTryReconnect = true; + let shouldSubscribeData = true; + let shouldSubscribeRefreshData = true; + this.name = config.deviceName; + this.deviceName = config.deviceName; + this.deviceId = config.deviceId; + this.deviceIp = config.deviceIp; + this.disableAutoStart = config.disableAutoStart; + this.eventMode = config.eventMode || "event-both"; + node.log(`Recieved the config ${JSON.stringify(config)}`); + this.retryTimeout = + config.retryTimeout == null || + typeof config.retryTimeout == "undefined" || + (typeof config.retryTimeout == "string" && + config.retryTimeout.trim() == "") || + (typeof config.retryTimeout == "number" && config.retryTimeout <= 0) || + isNaN(config.retryTimeout) + ? 1000 + : config.retryTimeout; + this.findTimeout = + config.findTimeout == null || + typeof config.findTimeout == "undefined" || + (typeof config.findTimeout == "string" && + config.findTimeout.trim() == "") || + (typeof config.findTimeout == "number" && config.findTimeout <= 0) || + isNaN(config.findTimeout) + ? 1000 + : config.findTimeout; + this.tuyaVersion = + config.tuyaVersion == null || + typeof config.tuyaVersion == "undefined" || + (typeof config.tuyaVersion == "string" && + config.tuyaVersion.trim() == "") || + (typeof config.tuyaVersion == "number" && config.tuyaVersion <= 0) || + isNaN(config.tuyaVersion) + ? "3.1" + : config.tuyaVersion.trim(); - }); + this.deviceStatus = CLIENT_STATUS.DISCONNECTED; + // Variable definition ends here + if (this.eventMode == "event-data") { + shouldSubscribeData = true; + shouldSubscribeRefreshData = false; + } else if (this.eventMode == "event-dp-refresh") { + shouldSubscribeData = false; + shouldSubscribeRefreshData = true; + } else { + // both case or default case + shouldSubscribeData = true; + shouldSubscribeRefreshData = true; + } + node.log( + `Event subscription : shouldSubscribeData=>${shouldSubscribeData} , shouldSubscribeRefreshData=>${shouldSubscribeRefreshData}` + ); + if (this.retryTimeout <= 0) { + this.retryTimeout = 1000; + } - // Add event listeners - tuyaDevice.on('connected', () => { - node.log('Connected to device! ' + node.deviceId); - setStatusConnected(); - }); + if (this.findTimeout <= 0) { + this.findTimeout = 1000; + } + let findTimeoutHandler = null; + let retryTimerHandler = null; - tuyaDevice.on('disconnected', () => { - node.log('Disconnected from tuyaDevice.'); - setStatusDisconnected(); - if (shouldTryReconnect) { - retryConnection(); - } + this.deviceKey = config.deviceKey; + node.on("input", function (msg) { + node.log(`Recieved input : ${JSON.stringify(msg)}`); + let operation = msg.payload.operation || "SET"; + delete msg.payload.operation; + switch (operation) { + case "SET": + tuyaDevice.set(msg.payload); + break; + case "REFRESH": + tuyaDevice.refresh(msg.payload); + break; + case "GET": + tuyaDevice.get(msg.payload); + break; + case "CONTROL": + if (msg.payload.action == "CONNECT") { + startComm(); + } else if (msg.payload.action == "DISCONNECT") { + closeComm(); + } else if (msg.payload.action == "SET_FIND_TIMEOUT") { + setFindTimeout(msg.payload.value); + } else if (msg.payload.action == "SET_RETRY_TIMEOUT") { + setRetryTimeout(msg.payload.value); + } else if (msg.payload.action == "RECONNECT") { + startComm(); + } + break; + } + }); + const enableNode = () => { + console.log("enabling the node", node.id); + startComm(); + }; - }); + const disableNode = () => { + console.log("disabling the node", node.id); + closeComm(); + }; - tuyaDevice.on('error', error => { - setStatusOnError(error, 'Error', { - context: { - message: error, - deviceVirtualId: node.deviceId, - deviceIp: node.deviceIp, - deviceKey: node.deviceKey - } - }); - if (shouldTryReconnect) { - retryConnection(); - } - }); + const setFindTimeout = (newTimeout) => { + node.log("Setting new find timeout :" + newTimeout); + node.findTimeout = newTimeout; + }; + + const setRetryTimeout = (newTimeout) => { + node.log("Setting new retry timeout :" + newTimeout); + node.retryTimeout = newTimeout; + }; + + const closeComm = () => { + node.log("Cleaning up the state"); + clearTimeout(findTimeoutHandler); + shouldTryReconnect = false; + tuyaDevice.disconnect(); + node.log("Clearing the find timeout handler"); + setStatusDisconnected(); + }; - if (shouldSubscribeRefreshData) { - tuyaDevice.on('dp-refresh', data => { - node.log(`Data from device [event:dp-refresh]: ${JSON.stringify(data)}`); - setStatusConnected(); - node.send({ - payload: { - data: data, - deviceId: node.deviceId, - deviceName: node.deviceName - } - }); - }); - } - - if (shouldSubscribeData) { - tuyaDevice.on('data', data => { - node.log(`Data from device [event:data]: ${JSON.stringify(data)}`); - setStatusConnected(); - node.send({ - payload: { - data: data, - deviceId: node.deviceId, - deviceName: node.deviceName - } - }); - }); - } - let connectDevice = () => { - clearTimeout(findTimeoutHandler); - if (tuyaDevice.isConnected() === false) { - setStatusConnecting(); - tuyaDevice.connect(); - } else { - node.log("already connected. skippig the connect call"); - setStatusConnected(); - } - } - let findDevice = () => { - setStatusConnecting(); - node.log("initiating the find command"); - tuyaDevice.find().then(() => { - // Connect to device - connectDevice(); - }).catch((e) => { - // We need to retry - setStatusOnError(e.message, "Can't find device", { - context: { - message: e, - deviceVirtualId: node.deviceId, - deviceIp: node.deviceIp, - deviceKey: node.deviceKey - } - }); - if (shouldTryReconnect) { - node.log("Cannot find the device, re-trying..."); - findTimeoutHandler = setTimeout(findDevice, node.findTimeout); - } else { - node.log("not retrying the find as shouldTryReconnect = false"); - } - - }); - } - findTimeoutHandler = setTimeout(() => { - findDevice(); - }, 1000) + const startComm = () => { + closeComm(); + findTimeoutHandler = setTimeout(() => { + shouldTryReconnect = true; + node.log( + `Connecting to Tuya with params ${JSON.stringify( + connectionParams + )} , findTimeout : ${node.findTimeout} , retryTimeout: ${ + node.retryTimeout + } ` + ); + findDevice(); + }, 1000); + }; + const sendDeviceConnectStatus = (data) => { + return { + payload: { + state: node.deviceStatus, + ...data, + }, + }; + }; + const setStatusConnecting = function () { + node.deviceStatus = CLIENT_STATUS.CONNECTING; + node.send([null, sendDeviceConnectStatus()]); + return node.status({ fill: "yellow", shape: "ring", text: "connecting" }); + }; + const setStatusConnected = function () { + node.deviceStatus = CLIENT_STATUS.CONNECTED; + node.send([null, sendDeviceConnectStatus()]); + return node.status({ fill: "green", shape: "ring", text: "connected" }); + }; + const setStatusDisconnected = function () { + node.deviceStatus = CLIENT_STATUS.DISCONNECTED; + node.send([null, sendDeviceConnectStatus()]); + return node.status({ fill: "red", shape: "ring", text: "disconnected" }); + }; + const setStatusOnError = function ( + errorText, + errorShortText = "error", + data + ) { + node.deviceStatus = CLIENT_STATUS.ERROR; + node.error(errorText, data); + node.send([null, sendDeviceConnectStatus(data)]); + return node.status({ fill: "red", shape: "ring", text: errorShortText }); + }; + const connectionParams = { + id: node.deviceId, + key: node.deviceKey, + ip: node.deviceIp, + issueGetOnConnect: false, + nullPayloadOnJSONError: false, + version: node.tuyaVersion, + }; + + let tuyaDevice = new TuyaDevice(connectionParams); + + let retryConnection = () => { + clearTimeout(retryTimerHandler); + retryTimerHandler = setTimeout(() => { + connectDevice(); + }, node.retryTimeout); + }; + node.on("close", function () { + // tidy up any state + // clearInterval(int); + closeComm(); + }); + + // Add event listeners + tuyaDevice.on("connected", () => { + node.log("Connected to device! " + node.deviceId); + setStatusConnected(); + }); + + tuyaDevice.on("disconnected", () => { + node.log("Disconnected from tuyaDevice."); + setStatusDisconnected(); + if (shouldTryReconnect) { + retryConnection(); + } + }); + + tuyaDevice.on("error", (error) => { + setStatusOnError(error, "Error", { + context: { + message: error, + deviceVirtualId: node.deviceId, + deviceIp: node.deviceIp, + deviceKey: node.deviceKey, + }, + }); + if (shouldTryReconnect) { + retryConnection(); + } + }); + + if (shouldSubscribeRefreshData) { + tuyaDevice.on("dp-refresh", (data) => { + node.log( + `Data from device [event:dp-refresh]: ${JSON.stringify(data)}` + ); + setStatusConnected(); + node.send([ + { + payload: { + data: data, + deviceId: node.deviceId, + deviceName: node.deviceName, + }, + }, + null, + ]); + }); + } + + if (shouldSubscribeData) { + tuyaDevice.on("data", (data) => { + node.log(`Data from device [event:data]: ${JSON.stringify(data)}`); + setStatusConnected(); + node.send([ + { + payload: { + data: data, + deviceId: node.deviceId, + deviceName: node.deviceName, + }, + }, + null, + ]); + }); + } + let connectDevice = () => { + clearTimeout(findTimeoutHandler); + if (tuyaDevice.isConnected() === false) { + setStatusConnecting(); + tuyaDevice.connect(); + } else { + node.log("already connected. skippig the connect call"); + setStatusConnected(); + } + }; + let findDevice = () => { + setStatusConnecting(); + node.log("Initiating the find command"); + tuyaDevice + .find() + .then(() => { + // Connect to device + connectDevice(); + }) + .catch((e) => { + // We need to retry + setStatusOnError(e.message, "Can't find device", { + context: { + message: e, + deviceVirtualId: node.deviceId, + deviceIp: node.deviceIp, + deviceKey: node.deviceKey, + }, + }); + if (shouldTryReconnect) { + node.log("Cannot find the device, re-trying..."); + findTimeoutHandler = setTimeout(findDevice, node.findTimeout); + } else { + node.log("not retrying the find as shouldTryReconnect = false"); + } + }); + }; + // Start probing + if (!node.disableAutoStart) { + node.log("Auto start probe on connect..."); + startComm(); + } else { + node.log("Auto start probe is disabled "); + setTimeout(() => { + setStatusDisconnected(); + }, 1000); } - RED.nodes.registerType("tuya-smart-device", TuyaSmartDeviceNode); -} \ No newline at end of file + } + RED.nodes.registerType("tuya-smart-device", TuyaSmartDeviceNode); +};