UNPKG

@demirdeniz/node-red-contrib-tuya-kepler-device

Version:

A node-red module to interact with the tuya smart devices (updated with Tuya protocol 3.5)

573 lines (547 loc) 17.8 kB
<script type="text/javascript"> RED.nodes.registerType('tuya-kepler-device', { category: 'Kepler Home', color: '#fc8144', credentials: { secretConfig: { value: '', }, }, defaults: { deviceName: { value: '', required: true }, disableAutoStart: { value: false, required: true }, deviceId: { value: '', required: false, validate: function (v) { let value = v; let credsOffline = false; const storeAsCreds = this.storeAsCreds || false; if (storeAsCreds) { if (typeof this.credentials !== 'object') { credsOffline = true; } else { const secretConfig = this.credentials.secretConfig || '{}'; const secret = JSON.parse(secretConfig); value = secret.deviceId; } } return typeof this.deviceIp === 'string' && this.deviceIp.trim().length == 0 ? storeAsCreds && credsOffline ? true : value.trim().length > 0 : true; }, }, deviceKey: { value: '', required: false, validate: function (v) { let value = v; let credsOffline = false; const storeAsCreds = this.storeAsCreds || false; if (storeAsCreds) { if (typeof this.credentials !== 'object') { credsOffline = true; } else { const secretConfig = this.credentials.secretConfig || '{}'; const secret = JSON.parse(secretConfig); value = secret.deviceKey; } } return storeAsCreds && credsOffline ? true : value.trim().length > 0; }, }, storeAsCreds: { value: false, }, deviceIp: { value: '', required: false, validate: function (v) { return $('#node-input-deviceId').val() && $('#node-input-deviceId').val().trim().length == 0 ? v.trim().length > 0 : true; }, }, retryTimeout: { value: 1000, required: true, validate: RED.validators.number(), }, findTimeout: { value: 10000, required: true, validate: RED.validators.number(), }, tuyaVersion: { value: '3.3', required: false }, eventMode: { value: 'event-both', required: true }, enableKeepAlive: { value: false, required: true }, logLevel: { value: 'log-level-disable', required: false }, issueRefreshOnConnect: { value: false, required: true, validate: function(v) { if(typeof(v) == "boolean") return !(($('#node-input-issueRefreshOnConnect').is(':checked')) && ($('#node-input-issueGetOnConnect').is(':checked'))); else return true; } }, issueGetOnConnect: { value: false, required: true, validate: function(v) { if(typeof(v) == "boolean") return !(($('#node-input-issueRefreshOnConnect').is(':checked')) && ($('#node-input-issueGetOnConnect').is(':checked'))); else return true; } }, initialDelay: { value: 10000, required: true, validate: RED.validators.number() }, socketTimeout: { value: 5000, required: true, validate: RED.validators.number() }, HeartBeatInterval: { value: 25, required: true, validate: RED.validators.number() } }, inputs: 1, outputs: 2, inputLabels: 'Command for the device', outputLabels: ['Data from the device', 'Client State'], icon: 'tuya_smart_48x48.png', paletteLabel: 'tuya device', label: function () { return this.deviceName || 'tuya kepler device'; }, ////////// onEditPrepare /////////////// oneditprepare: function () { let eventMode = this.eventMode || 'event-both'; $("select[name='node-input-eventMode']") .find(`option[value='${eventMode}']`) .attr('selected', true); let logLevel = this.logLevel || 'log-level-disable'; $("select[name='node-input-logLevel']") .find(`option[value='${logLevel}']`) .attr('selected', true); const storeAsCreds = this.storeAsCreds || false; if (storeAsCreds) { const secretConfig = this.credentials.secretConfig || '{}'; const secret = JSON.parse(secretConfig); console.log('Loading config from secret'); $('#node-input-deviceId').val(secret.deviceId); $('#node-input-deviceKey').val(secret.deviceKey); } $('#node-input-findTimeout').val(this.findTimeout || 1000); $('#node-input-retryTimeout').val(this.retryTimeout || 1000); $('#node-input-initialDelay').val(this.initialDelay || 10000); $('#node-input-socketTimeout').val(this.socketTimeout || 5000); $('#node-input-HeartBeatInterval').val(this.HeartBeatInterval || 25); if ( this.tuyaVersion == null || typeof this.tuyaVersion == 'undefined' || this.tuyaVersion.trim().length == 0 ) { $('#node-input-tuyaVersion').val(3.1); } }, ////////// onEditSave /////////////// oneditsave: function () { const storeAsCreds = $('#node-input-storeAsCreds').is(':checked') || false; if (storeAsCreds) { const secret = { deviceId: $('#node-input-deviceId').val(), deviceKey: $('#node-input-deviceKey').val(), }; console.log('Saving secret'); $('#node-input-secretConfig').val(JSON.stringify(secret)); $('#node-input-deviceId').val(''); $('#node-input-deviceKey').val(''); } else { $('#node-input-secretConfig').val('{}'); }; //if($('#node-input-issueGetOnConnect').is(':checked')) // $('#node-input-issueRefreshOnConnect').prop("checked", false); } }); </script> <script type="text/html" data-template-name="tuya-kepler-device"> <input type="hidden" id="node-input-secretConfig" placeholder="Device Secret" /> <div class="form-row"> <label style="width:100%" for="node-input-deviceName" ><i class="fa fa-tag"></i> Device Name</label > <input type="text" id="node-input-deviceName" placeholder="Bulb" /> </div> <hr /> <div class="form-row" style="font-weight: bold;">Connection Details</div> <div class="form-row"> <label style="width:100%" for="node-input-deviceId" ><i class="fa fa-id-card-o"></i> Device Virtual ID</label > <input type="text" id="node-input-deviceId" placeholder="Device Virtual ID" /> </div> <div class="form-row"> <label style="width:100%" for="node-input-deviceKey" ><i class="fa fa-key"></i> Device Key</label > <input type="text" id="node-input-deviceKey" placeholder="Device Key" /> </div> <div class="form-row"> <label style="width:100%" for="node-input-storeAsCreds"> <input type="checkbox" style="width: auto;" id="node-input-storeAsCreds" /> Store Device Id and Device Key as credentials (Credentials will not appear in the flow export)</label > </div> <br /> <div class="form-row"> <label style="width:100%" for="node-input-deviceIp" ><i class="fa fa-info"></i> Device IP - for faster connections (Use Static IP) </label> <input type="text" id="node-input-deviceIp" placeholder="The ip of the device" /> </div> <div style="margin-bottom:10px;font-style: italic;color: brown;font-size: smaller;" > Device IP is optional (i.e. if not known, will try use Device Virtual ID to find it but will be slow and may be unsuccessful) </div> <hr /> <div class="form-row" style="font-weight: bold;">Advanced Options</div> <div class="form-row"> <input type="checkbox" id="node-input-disableAutoStart" placeholder="" style="width:auto" /> <label for="node-input-disableAutoStart" style="width:auto"> Disable auto connect on start </label> <br /> (You have to manually issue the CONNECT CONTROL COMMAND. Refer the help panel) <br /> </div> <div class="form-row"> <label style="width:100%" for="node-input-retryTimeout" ><i class="fa fa-clock-o"></i> Interval for retry connection incase of error (milliseconds)</label > </div> <div class="form-row"> <input type="number" id="node-input-retryTimeout" placeholder="" value="1000" /> </div> <div class="form-row"> <label style="width:100%" for="node-input-findTimeout" ><i class="fa fa-clock-o"></i> Interval for find operation incase of error (milliseconds)</label > <input type="number" id="node-input-findTimeout" placeholder="" value="1000" /> </div> <div class="form-row"> <label style="width:100%" for="node-input-tuyaVersion" ><i class="fa fa-lock"></i> Tuya Protocol Version, Default : 3.1 (don't change if you are not sure)</label > <input type="text" id="node-input-tuyaVersion" placeholder="" value="3.1" /> </div> <div class="form-row"> <label style="width:100%" for="node-input-subscribeMode" ><i class="fa fa-wrench"></i> Listen to tuya events</label > </div> <div class="form-row"> <select name="node-input-eventMode" id="node-input-eventMode"> <option value="event-data">Data Event</option> <option value="event-dp-refresh">DP-Refresh Event</option> <option value="event-both">Both Events</option> </select> </div> <hr /> <div class="form-row" style="font-weight: bold;">Kepler Options</div> <div class="form-row"> <label style="width:100%" for="node-input-loglevel"> <i class="fa fa-file"></i> Log Level (Tuya API Logging) </label> </div> <div class="form-row"> <select name="node-input-logLevel" id="node-input-logLevel"> <option value="log-level-debug">Debug</option> <option value="log-level-disable">Disable</option> </select> </div> <div class="form-row"> <input type="checkbox" id="node-input-enableKeepAlive" placeholder="" style="width:auto" /> <label for="node-input-enableKeepAlive" style="width:auto"> Enable TCP KeepAlive </label> </div> <div class="form-row"> <label style="width:100%" for="node-input-initialDelay"> <i class="fa fa-clock-o"></i> Interval after last packet to send KeepAlive (milliseconds) </label> </div> <div class="form-row"> <input type="number" id="node-input-initialDelay" placeholder="" value="10000" /> </div> <div class="form-row"> <label style="width:100%" for="node-input-socketTimeout" ><i class="fa fa-clock-o"></i> Socket Connection Timeout (milliseconds) </label> <input type="number" id="node-input-socketTimeout" placeholder="" value="5000" /> </div> <div class="form-row"> <label style="width:100%" for="node-input-HeartBeatInterval" ><i class="fa fa-clock-o"></i> Interval between Heartbeat messages (in seconds, minimum 5 seconds) </label> <input type="number" id="node-input-HeartBeatInterval" placeholder="" value="25" /> </div> <div class="form-row"> <input type="checkbox" id="node-input-issueGetOnConnect" placeholder="" style="width:auto" /> <label for="node-input-issueGetOnConnect" style="width:auto"> Issue Get Command upon device connect (cannot be combined with RefreshOnConnect) </label> </div> <div class="form-row"> <input type="checkbox" id="node-input-issueRefreshOnConnect" placeholder="" style="width:auto" /> <label for="node-input-issueRefreshOnConnect" style="width:auto"> Issue Refresh Command upon device connect (cannot be combined with GetOnConnect) </label> </div> </script> <script type="text/html" data-help-name="tuya-kepler-device"> <p>This node will help you to configure any tuya kepler device.</p> <h3>Details</h3> <p> Instructions for getting the device id is available <a href="" >here</a > </p> <code> You can get the device id and the key once you follow any of the resources available in youtube</code> <p> The node takes the input which needs to be sent to the device and outouts the message if the state of the device changes. </p> <h3>Input</h3> <h4>payload</h4> <dl class="message-properties"> <dt class="optional"> operation <span class="property-type">SET/GET</span> </dt> <dd> <p> <b>SET</b> (default): Use SET the run a set operation in the device <a href="" >SET documentation</a > </p> <p> <b>GET</b>: Use GET the run a get operation in the device <a href="" >GET documentation</a > </p> <p> <b>REFRESH</b>: Use REFRESH to refresh a device status <a href="" >REFRESH documentation</a > </p> <p> <b>CONTROL</b>: Send the control events to the node. This can be used to issue actions like connect, disconnect etc. </p> Any output of the operation will be triggered on the output of the node. </dd> <dt class="">dps <span class="property-type">Number</span></dt> <dd>The dps index you want to update or query</dd> <dt class="">set <span class="property-type">Any</span></dt> <dd>The value you want to set for the dps index</dd> <dt class="optional"> multiple <span class="property-type">Boolean</span> </dt> <dd> Incase you want to send multiple dps index for a SET operation. Use the data argument in that case. </dd> <dt class="optional">data <span class="property-type">Object</span></dt> <dd> Object of the below format with multiple dps index and the values <code> payload = { "multiple": true, "data": { "20": true, "24": "00e203e803de" } }</code > </dd> <dt class="">action <span class="property-type">string</span></dt> <dd> This property works only with operation is set as "CONTROL" <table border> <tr> <td>Action</td> <td>Purpose</td> </tr> <tr> <td>CONNECT</td> <td>Connects to the device</td> </tr> <tr> <td>DISCONNECT</td> <td>Disconnect from the device</td> </tr> <tr> <td>RECONNECT</td> <td>Reconnects to the device</td> </tr> <tr> <td>SET_FIND_TIMEOUT</td> <td> Sets the find timeout dynamically. This is not saved in the config </td> </tr> <tr> <td>SET_RETRY_TIMEOUT</td> <td> Sets the retry timeout dynamically. This is not saved in the config </td> </tr> <tr> <td>SET_EVENT_MODE</td> <td> Sets the event mode dynamically. This is not saved in the config. <br /> <br /> value = event-data //for subscribing to only data event <br /> <br /> value = event-dp-refresh //for subscribing to only dp-refresh event <br /> <br /> value = both //for subscribing to both events </td> </tr> </table> example : <div> action : CONNECT <code> payload = { "operation": "CONTROL", "action": "CONNECT" } </code> </div> <div> action : SET_EVENT_MODE <code> payload = { "operation": "CONTROL", "action": "SET_EVENT_MODE", "value": "event-data" } </code> </div> </dd> </dl> <h3>Output</h3> <ol class="node-ports"> <li> Data from device <dl class="message-properties"> <dt class=""> deviceId <span class="property-type">String</span> </dt> <dd>Device Id</dd> <dt class="">deviceName <span class="property-type">String</span></dt> <dd>Device Name</dd> <dt class="optional">data <span class="property-type">Object</span></dt> <dd> The response from the Tuya device. Sample response is shown below. <code> data: { dps : { 1 : false }, t: 1600955043 }, deviceId: "39390e7421ia", deviceName: "Monitor Plug" </code> </dd> </dl> </li> <li> Client state <dl class="message-properties"> <dt class=""> state <span class="property-type">String</span> </dt> <dd> Shows the current status of the node (CONNECTED, CONNECTING, DISCONNECTED,ERROR) </dd> </dl> </li> </ol> </script>