diff --git a/index.js b/index.js index c313388..1872781 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,13 @@ 'use strict'; // Import packages -const debug = require('debug')('TuyAPI'); const dgram = require('dgram'); -const forge = require('node-forge'); -const retryConnect = require('net-retry-connect'); +const net = require('net'); const stringOccurrence = require('string-occurrence'); +const timeout = require('p-timeout'); +const forge = require('node-forge'); +const retry = require('retry'); +const debug = require('debug')('TuyAPI'); // Import requests for devices const requests = require('./requests.json'); @@ -84,9 +86,10 @@ TuyaDevice.prototype.resolveIds = function () { } } - // Todo: add timeout for when IP cannot be found, then reject(with error) - // add IPs to devices in array and return true - return new Promise(resolve => { + debug('Finding IP for devices ' + needIP); + + // Add IPs to devices in array + return timeout(new Promise(resolve => { // Timeout this.listener.on('message', message => { debug('Received UDP message.'); @@ -111,6 +114,11 @@ TuyaDevice.prototype.resolveIds = function () { resolve(true); } }); + }), 10000, () => { + // Have to do this so we exit cleanly + this.listener.close(); + this.listener.removeAllListeners(); + throw new Error('resolveIds() timed out. Is the device ID correct and is the device powered on?'); }); }; @@ -230,7 +238,7 @@ TuyaDevice.prototype.set = function (options) { if (options.dps === undefined) { thisRequest.dps = {1: options.set}; } else { - thisRequest.dps[options.dps.toString] = options.set; + thisRequest.dps[options.dps.toString()] = options.set; } debug('Payload: '); @@ -274,23 +282,34 @@ TuyaDevice.prototype._send = function (ip, buffer) { debug('Sending this data: ', buffer.toString('hex')); return new Promise((resolve, reject) => { - retryConnect.to({port: 6668, host: ip, retryOptions: {retries: 5}}, (error, client) => { - if (error) { + const client = new net.Socket(); + const connectOperation = retry.operation(); + + client.on('error', error => { + if (!connectOperation.retry(error)) { reject(error); } + }); - client.write(buffer); - - client.on('data', data => { - client.destroy(); - - debug('Received data back.'); + connectOperation.attempt(() => { + client.connect(6668, ip, () => { + const writeOperation = retry.operation(); + writeOperation.attempt(() => { + client.write(buffer); - resolve(data); - }); - client.on('error', error => { - error.message = 'Error communicating with device. Make sure nothing else is trying to control it or connected to it.'; - reject(error); + client.on('data', data => { + client.destroy(); + debug('Received data back.'); + resolve(data); + }); + client.on('error', error => { + error.message = 'Error communicating with device. Make sure nothing else is trying to control it or connected to it.'; + console.log('here'); + if (!writeOperation.retry(error)) { + reject(error); + } + }); + }); }); }); }); diff --git a/package-lock.json b/package-lock.json index 777dc3e..993718e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "tuyapi", - "version": "2.0.0", + "version": "2.0.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -5613,6 +5613,13 @@ "integrity": "sha1-rJUsT1D+5DPcb8tZXzw/GZNsX80=", "requires": { "retry": "0.9.0" + }, + "dependencies": { + "retry": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.9.0.tgz", + "integrity": "sha1-b2l+UKDk3cjI9/tUeptg3q1DZ40=" + } } }, "node-forge": { @@ -5818,8 +5825,7 @@ "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, "p-limit": { "version": "1.1.0", @@ -5836,6 +5842,14 @@ "p-limit": "1.1.0" } }, + "p-timeout": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", + "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", + "requires": { + "p-finally": "1.0.0" + } + }, "package-json": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", @@ -6609,9 +6623,9 @@ } }, "retry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.9.0.tgz", - "integrity": "sha1-b2l+UKDk3cjI9/tUeptg3q1DZ40=" + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=" }, "rimraf": { "version": "2.6.2", diff --git a/package.json b/package.json index cf3421d..30a6c46 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tuyapi", - "version": "2.0.1", + "version": "2.0.2", "description": "An easy-to-use API for devices that use Tuya's cloud services (currently only supports smart plugs)", "main": "index.js", "scripts": { @@ -28,6 +28,8 @@ "debug": "^3.1.0", "net-retry-connect": "^0.1.1", "node-forge": "^0.7.1", + "p-timeout": "^2.0.1", + "retry": "^0.10.1", "string-occurrence": "^1.2.0" }, "devDependencies": {