mirror of
https://github.com/lehanspb/tuya-mqtt.git
synced 2025-12-16 17:54:36 +00:00
updated mqtt script to event based TuyAPI
This commit is contained in:
29
cleanup.js
Normal file
29
cleanup.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// Object to capture process exits and call app specific cleanup function
|
||||
var debug = require('debug')('Cleanup');
|
||||
|
||||
function noOp() {};
|
||||
|
||||
exports.Cleanup = function Cleanup(callback) {
|
||||
|
||||
// attach user callback to the process event emitter
|
||||
// if no callback, it will still exit gracefully on Ctrl-C
|
||||
callback = callback || noOp;
|
||||
process.on('cleanup', callback);
|
||||
|
||||
// do app specific cleaning before exiting
|
||||
process.on('exit', function () {
|
||||
process.emit('cleanup');
|
||||
});
|
||||
|
||||
// catch ctrl+c event and exit normally
|
||||
process.on('SIGINT', function () {
|
||||
debug('Ctrl-C...');
|
||||
process.exit(2);
|
||||
});
|
||||
|
||||
//catch uncaught exceptions, trace, then exit normally
|
||||
process.on('uncaughtException', function (e) {
|
||||
debug('Uncaught Exception...', e.stack);
|
||||
process.exit(99);
|
||||
});
|
||||
};
|
||||
178
package-lock.json
generated
178
package-lock.json
generated
@@ -43,18 +43,18 @@
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.1.0.tgz",
|
||||
"integrity": "sha512-YkIRgwsZwJWTnyQrsBTWefizHh+8GYj3kbL1BTiAQ/9pwpino0G7B2gp5tx/FUBqUlvtxV85KNR3mwfAtv15Yw==",
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==",
|
||||
"requires": {
|
||||
"base64-js": "^1.0.2",
|
||||
"ieee754": "^1.1.4"
|
||||
}
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz",
|
||||
"integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ=="
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
|
||||
},
|
||||
"callback-stream": {
|
||||
"version": "1.1.0",
|
||||
@@ -86,17 +86,17 @@
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz",
|
||||
"integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==",
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"requires": {
|
||||
"color-name": "1.1.1"
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz",
|
||||
"integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok="
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||
},
|
||||
"commist": {
|
||||
"version": "1.0.0",
|
||||
@@ -137,9 +137,9 @@
|
||||
}
|
||||
},
|
||||
"cron": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/cron/-/cron-1.3.0.tgz",
|
||||
"integrity": "sha512-K/SF7JlgMmNjcThWxkKvsHhey2EDB4CeOEWJ9aXWj3fbQJppsvTPIeyLdHfNq5IbbsMUUjRW1nr5dSO95f2E4w==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/cron/-/cron-1.4.1.tgz",
|
||||
"integrity": "sha512-HlglwQUNh6bhgfoDR6aEzyHN2T4bc0XhxJxkNPp+Ry7lK7Noby94pHcngYf634+MtxplwZm8okFgNe+R9PGDjg==",
|
||||
"requires": {
|
||||
"moment-timezone": "^0.5.x"
|
||||
}
|
||||
@@ -159,12 +159,20 @@
|
||||
"resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz",
|
||||
"integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
|
||||
"d": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
|
||||
"integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
"es5-ext": "^0.10.9"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
||||
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"decamelize": {
|
||||
@@ -191,6 +199,69 @@
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"es5-ext": {
|
||||
"version": "0.10.46",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz",
|
||||
"integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==",
|
||||
"requires": {
|
||||
"es6-iterator": "~2.0.3",
|
||||
"es6-symbol": "~3.1.1",
|
||||
"next-tick": "1"
|
||||
}
|
||||
},
|
||||
"es6-iterator": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
||||
"integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
|
||||
"requires": {
|
||||
"d": "1",
|
||||
"es5-ext": "^0.10.35",
|
||||
"es6-symbol": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"es6-map": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz",
|
||||
"integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=",
|
||||
"requires": {
|
||||
"d": "1",
|
||||
"es5-ext": "~0.10.14",
|
||||
"es6-iterator": "~2.0.1",
|
||||
"es6-set": "~0.1.5",
|
||||
"es6-symbol": "~3.1.1",
|
||||
"event-emitter": "~0.3.5"
|
||||
}
|
||||
},
|
||||
"es6-set": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz",
|
||||
"integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=",
|
||||
"requires": {
|
||||
"d": "1",
|
||||
"es5-ext": "~0.10.14",
|
||||
"es6-iterator": "~2.0.1",
|
||||
"es6-symbol": "3.1.1",
|
||||
"event-emitter": "~0.3.5"
|
||||
}
|
||||
},
|
||||
"es6-symbol": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
|
||||
"integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
|
||||
"requires": {
|
||||
"d": "1",
|
||||
"es5-ext": "~0.10.14"
|
||||
}
|
||||
},
|
||||
"event-emitter": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
|
||||
"integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
|
||||
"requires": {
|
||||
"d": "1",
|
||||
"es5-ext": "~0.10.14"
|
||||
}
|
||||
},
|
||||
"execa": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
|
||||
@@ -234,9 +305,9 @@
|
||||
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
|
||||
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
@@ -272,6 +343,11 @@
|
||||
"unique-stream": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
|
||||
},
|
||||
"help-me": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/help-me/-/help-me-1.1.0.tgz",
|
||||
@@ -451,21 +527,22 @@
|
||||
"integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y="
|
||||
},
|
||||
"moment-timezone": {
|
||||
"version": "0.5.17",
|
||||
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.17.tgz",
|
||||
"integrity": "sha512-Y/JpVEWIOA9Gho4vO15MTnW1FCmHi3ypprrkUaxsZ1TKg3uqC8q/qMBjTddkHoiwwZN3qvZSr4zJP7x9V3LpXA==",
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.21.tgz",
|
||||
"integrity": "sha512-j96bAh4otsgj3lKydm3K7kdtA3iKf2m6MY2iSYCzCm5a1zmHo1g+aK3068dDEeocLZQIS9kU8bsdQHLqEvgW0A==",
|
||||
"requires": {
|
||||
"moment": ">= 2.9.0"
|
||||
}
|
||||
},
|
||||
"mqtt": {
|
||||
"version": "2.18.3",
|
||||
"resolved": "https://registry.npmjs.org/mqtt/-/mqtt-2.18.3.tgz",
|
||||
"integrity": "sha512-BXCUugFgA6FOWJGxhvUWtVLOdt6hYTmiMGPksEyKuuF1FQ0ji7UJBJ/0kVRMUtUWCAtPGnt4mZZZgJpzNLcuQg==",
|
||||
"version": "2.18.8",
|
||||
"resolved": "https://registry.npmjs.org/mqtt/-/mqtt-2.18.8.tgz",
|
||||
"integrity": "sha512-3h6oHlPY/yWwtC2J3geraYRtVVoRM6wdI+uchF4nvSSafXPZnaKqF8xnX+S22SU/FcgEAgockVIlOaAX3fkMpA==",
|
||||
"requires": {
|
||||
"commist": "^1.0.0",
|
||||
"concat-stream": "^1.6.2",
|
||||
"end-of-stream": "^1.4.1",
|
||||
"es6-map": "^0.1.5",
|
||||
"help-me": "^1.0.1",
|
||||
"inherits": "^2.0.3",
|
||||
"minimist": "^1.2.0",
|
||||
@@ -490,14 +567,19 @@
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
||||
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
|
||||
},
|
||||
"next-tick": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
||||
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
|
||||
},
|
||||
"node-forge": {
|
||||
"version": "0.7.5",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz",
|
||||
"integrity": "sha1-bBUsNFzhHFL0ZcKr2VfoY5zWdN8="
|
||||
"version": "0.7.6",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz",
|
||||
"integrity": "sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw=="
|
||||
},
|
||||
"npm-run-path": {
|
||||
"version": "2.0.2",
|
||||
@@ -562,7 +644,7 @@
|
||||
"p-timeout": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz",
|
||||
"integrity": "sha1-2N0ZeVldLcATnh/ka4tkbLPN8Dg=",
|
||||
"integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==",
|
||||
"requires": {
|
||||
"p-finally": "^1.0.0"
|
||||
}
|
||||
@@ -742,6 +824,14 @@
|
||||
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
|
||||
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"through2": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
|
||||
@@ -770,14 +860,24 @@
|
||||
}
|
||||
},
|
||||
"tuyapi": {
|
||||
"version": "github:codetheweb/tuyapi#9b31d7c74b2e97ef30069ca0d9faabd59ef932b7",
|
||||
"version": "github:codetheweb/tuyapi#79f21968c31b5a19c15afe3a6e77cebd6eed909f",
|
||||
"from": "github:codetheweb/tuyapi",
|
||||
"requires": {
|
||||
"crc": "^3.5.0",
|
||||
"debug": "^3.1.0",
|
||||
"debug": "^4.0.0",
|
||||
"node-forge": "^0.7.1",
|
||||
"p-timeout": "^2.0.1",
|
||||
"retry": "^0.10.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz",
|
||||
"integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"typedarray": {
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"color-convert": "^1.9.2",
|
||||
"cron": "^1.3.0",
|
||||
"color-convert": "^1.9.3",
|
||||
"cron": "^1.4.1",
|
||||
"crypto": "^1.0.1",
|
||||
"mqtt": "^2.18.3",
|
||||
"debug": "^3.2.6",
|
||||
"mqtt": "^2.18.8",
|
||||
"supports-color": "^5.5.0",
|
||||
"tuyapi": "github:codetheweb/tuyapi",
|
||||
"yargs": "^11.1.0"
|
||||
}
|
||||
|
||||
294
tuya-device.js
Normal file
294
tuya-device.js
Normal file
@@ -0,0 +1,294 @@
|
||||
const TuyAPI = require('tuyapi');
|
||||
const TuyColor = require('./tuya-color');
|
||||
const debug = require('debug')('TuyAPI-device');
|
||||
const debugTuya = require('debug')('TuyAPI-ext');
|
||||
const debugTimer = require('debug')('TuyAPI-device-timer');
|
||||
|
||||
/**
|
||||
* Sets a property on a device.
|
||||
* @param {Object} options
|
||||
* @param {Number} [options.dps=1] DPS index to set
|
||||
* @param {*} options.set value to set
|
||||
* @example
|
||||
* // set default property
|
||||
* tuya.set({set: true}).then(() => console.log('device was changed'))
|
||||
* @example
|
||||
* // set custom property
|
||||
* tuya.set({dps: 2, set: true}).then(() => console.log('device was changed'))
|
||||
* @returns {Promise<Boolean>} - returns `true` if the command succeeded
|
||||
*/
|
||||
const Parser = require('tuyapi/lib/message-parser')
|
||||
TuyAPI.prototype.set = function (options) {
|
||||
let dps = {};
|
||||
|
||||
if (options.dps != undefined || options.set != undefined) {
|
||||
if (options.dps === undefined) {
|
||||
dps = {
|
||||
1: options.set
|
||||
};
|
||||
} else {
|
||||
dps = {
|
||||
[options.dps.toString()]: options.set
|
||||
};
|
||||
}
|
||||
} else {
|
||||
dps = options;
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const timeStamp = (parseInt(now.getTime() / 1000, 10)).toString();
|
||||
|
||||
const payload = {
|
||||
devId: this.device.id,
|
||||
uid: '',
|
||||
t: timeStamp,
|
||||
dps
|
||||
};
|
||||
|
||||
debugTuya('Payload:', this.device.ip);
|
||||
debugTuya(payload);
|
||||
|
||||
// Encrypt data
|
||||
const data = this.device.cipher.encrypt({
|
||||
data: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
// Create MD5 signature
|
||||
const md5 = this.device.cipher.md5('data=' + data +
|
||||
'||lpv=' + this.device.version +
|
||||
'||' + this.device.key);
|
||||
|
||||
// Create byte buffer from hex data
|
||||
const thisData = Buffer.from(this.device.version + md5 + data);
|
||||
const buffer = Parser.encode({
|
||||
data: thisData,
|
||||
commandByte: 7 // 0x07
|
||||
});
|
||||
|
||||
// Send request to change status
|
||||
return new Promise((resolve, reject) => {
|
||||
this._send(buffer, 7, false).then(() => {
|
||||
resolve(true);
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
var steckdose = new TuyaDevice({
|
||||
id: '03200240600194781244',
|
||||
key: 'b8bdebab418f5b55',
|
||||
ip: '192.168.178.45',
|
||||
type: "socket"
|
||||
});
|
||||
*/
|
||||
|
||||
var TuyaDevice = (function () {
|
||||
var devices = [];
|
||||
var events = {};
|
||||
|
||||
var autoTimeout = undefined;
|
||||
|
||||
function checkExisiting(id) {
|
||||
var existing = false;
|
||||
// Check for existing instance
|
||||
devices.forEach(device => {
|
||||
if (device.hasOwnProperty("options")) {
|
||||
if (id === device.options.id) {
|
||||
existing = device;
|
||||
}
|
||||
}
|
||||
});
|
||||
return existing;
|
||||
}
|
||||
|
||||
function resetTimer() {
|
||||
return;
|
||||
debugTimer("Reset timer for auto disconnect all devices");
|
||||
clearTimeout(autoTimeout);
|
||||
autoTimeout = setTimeout(() => {
|
||||
debugTimer("Auto disconnect all devices");
|
||||
TuyaDevice.disconnectAll();
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
function TuyaDevice(options) {
|
||||
var device = this;
|
||||
// Check for existing instance
|
||||
if (existing = checkExisiting(options.id)) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
if (!(this instanceof TuyaDevice)) {
|
||||
return new TuyaDevice(options);
|
||||
}
|
||||
|
||||
options.type = options.type || "socket";
|
||||
options.persistentConnection = true;
|
||||
|
||||
Object.defineProperty(this, 'type', {
|
||||
value: options.type
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'options', {
|
||||
value: options
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'device', {
|
||||
value: new TuyAPI(this.options)
|
||||
});
|
||||
|
||||
this.device.on('connected', () => {
|
||||
debug('Connected to device.');
|
||||
device.triggerAll('connected');
|
||||
});
|
||||
|
||||
this.device.on('disconnected', () => {
|
||||
debug('Disconnected from device.');
|
||||
device.triggerAll('disconnected');
|
||||
});
|
||||
|
||||
this.device.on('data', data => {
|
||||
debug('Data from device:', data);
|
||||
device.triggerAll('data', data);
|
||||
});
|
||||
|
||||
this.device.on('error', (err) => {
|
||||
debug('Error: ' + err);
|
||||
device.triggerAll('error', err);
|
||||
});
|
||||
|
||||
this.device.connect();
|
||||
devices.push(this);
|
||||
resetTimer();
|
||||
}
|
||||
|
||||
TuyaDevice.prototype.triggerAll = function (name, argument) {
|
||||
var device = this;
|
||||
var e = events[name] || [];
|
||||
e.forEach(event => {
|
||||
event.call(device, argument);
|
||||
});
|
||||
}
|
||||
|
||||
TuyaDevice.prototype.on = function (name, callback) {
|
||||
var device = this;
|
||||
this.device.on(name, function () {
|
||||
callback.apply(device, arguments);
|
||||
});
|
||||
}
|
||||
|
||||
TuyaDevice.prototype.get = function (options) {
|
||||
resetTimer();
|
||||
return this.device.get(options);
|
||||
}
|
||||
|
||||
TuyaDevice.prototype.set = function (options, callback) {
|
||||
var device = this;
|
||||
debug('Setting status:', options);
|
||||
return this.device.set(options).then(result => {
|
||||
device.get().then(status => {
|
||||
debug('Result of setting status to', status);
|
||||
if (callback != undefined) {
|
||||
callback.call(device, status);
|
||||
}
|
||||
return;
|
||||
});
|
||||
});
|
||||
resetTimer();
|
||||
}
|
||||
|
||||
TuyaDevice.prototype.onoff = function (newStatus, callback) {
|
||||
newStatus = newStatus.toLowerCase();
|
||||
debug("onoff: " + newStatus);
|
||||
if (newStatus == "on") {
|
||||
this.on(callback);
|
||||
}
|
||||
if (newStatus == "off") {
|
||||
this.off(callback);
|
||||
}
|
||||
if (newStatus == "toggle") {
|
||||
this.toggle(callback);
|
||||
}
|
||||
}
|
||||
|
||||
TuyaDevice.prototype.on = function (callback) {
|
||||
var device = this;
|
||||
device.get().then(status => {
|
||||
device.set({
|
||||
set: true
|
||||
}, callback);
|
||||
});
|
||||
}
|
||||
|
||||
TuyaDevice.prototype.off = function (callback) {
|
||||
var device = this;
|
||||
device.get().then(status => {
|
||||
device.set({
|
||||
set: false
|
||||
}, callback);
|
||||
});
|
||||
}
|
||||
|
||||
TuyaDevice.prototype.toggle = function (callback) {
|
||||
var device = this;
|
||||
device.get().then(status => {
|
||||
device.set({
|
||||
set: !status
|
||||
}, callback);
|
||||
});
|
||||
}
|
||||
|
||||
TuyaDevice.prototype.setColor = function (hexColor, callback) {
|
||||
debug("Set color to", hexColor);
|
||||
var device = this;
|
||||
var tuya = this.device;
|
||||
var color = new TuyColor(tuya);
|
||||
var dps = color.setColor(hexColor);
|
||||
|
||||
device.get().then(status => {
|
||||
device.set(dps, callback);
|
||||
});
|
||||
resetTimer();
|
||||
}
|
||||
|
||||
TuyaDevice.prototype.connect = function (callback) {
|
||||
this.device.connect(callback);
|
||||
}
|
||||
|
||||
TuyaDevice.prototype.disconnect = function (callback) {
|
||||
this.device.disconnect(callback);
|
||||
}
|
||||
|
||||
Object.defineProperty(TuyaDevice, 'devices', {
|
||||
value: devices
|
||||
});
|
||||
|
||||
TuyaDevice.connectAll = function () {
|
||||
devices.forEach(device => {
|
||||
device.connect();
|
||||
});
|
||||
}
|
||||
|
||||
TuyaDevice.disconnectAll = function () {
|
||||
devices.forEach(device => {
|
||||
device.disconnect();
|
||||
});
|
||||
}
|
||||
|
||||
TuyaDevice.onAll = function (name, callback) {
|
||||
if (events[name] == undefined) {
|
||||
events[name] = [];
|
||||
}
|
||||
events[name].push(callback);
|
||||
devices.forEach(device => {
|
||||
device.triggerAll(name);
|
||||
});
|
||||
}
|
||||
|
||||
return TuyaDevice;
|
||||
}());
|
||||
|
||||
module.exports = TuyaDevice;
|
||||
100
tuya-mqtt-2.js
Normal file
100
tuya-mqtt-2.js
Normal file
@@ -0,0 +1,100 @@
|
||||
const mqtt = require('mqtt');
|
||||
const TuyaDevice = require('./tuya-device');
|
||||
const debug = require('debug')('tuya-mqtt');
|
||||
var cleanup = require('./cleanup').Cleanup(onExit);
|
||||
|
||||
function bmap(istate) {
|
||||
return istate ? 'ON' : "OFF";
|
||||
}
|
||||
|
||||
function bmap(istate) {
|
||||
return istate ? 'ON' : "OFF";
|
||||
}
|
||||
|
||||
const CONFIG = {
|
||||
host: 'localhost',
|
||||
port: 1883,
|
||||
topic: "tuya/"
|
||||
}
|
||||
|
||||
const mqtt_client = mqtt.connect({
|
||||
host: CONFIG.host,
|
||||
port: CONFIG.port
|
||||
});
|
||||
|
||||
mqtt_client.on('connect', function () {
|
||||
var topic = CONFIG.topic + '#';
|
||||
mqtt_client.subscribe(topic, function (err) {
|
||||
if (!err) {
|
||||
//mqtt_client.publish(CONFIG.topic + 'presence', 'Hello mqtt')
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
mqtt_client.on('message', function (topic, message) {
|
||||
try {
|
||||
message = message.toString();
|
||||
message = message.toLowerCase();
|
||||
var topic = topic.split("/");
|
||||
var options = {
|
||||
type: topic[1],
|
||||
id: topic[2],
|
||||
key: topic[3],
|
||||
ip: topic[4],
|
||||
};
|
||||
var exec = topic[5];
|
||||
|
||||
if (options.type == "socket" || options.type == "lightbulb") {
|
||||
debug("device", options);
|
||||
debug("message", message);
|
||||
var device = new TuyaDevice(options);
|
||||
|
||||
if (exec == "command") {
|
||||
var status = topic[6];
|
||||
device.onoff(status);
|
||||
}
|
||||
if (exec == "color") {
|
||||
var color = message;
|
||||
device.setColor(color);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debug(e);
|
||||
}
|
||||
});
|
||||
|
||||
function publishStatus(device, status) {
|
||||
try {
|
||||
var type = device.type;
|
||||
var tuyaID = device.options.id;
|
||||
var tuyaKey = device.options.key;
|
||||
var tuyaIP = device.options.ip;
|
||||
|
||||
if (tuyaID != undefined && tuyaKey != undefined && tuyaIP != undefined) {
|
||||
var topic = "tuya/" + type + "/" + tuyaID + "/" + tuyaKey + "/" + tuyaIP + "/state";
|
||||
mqtt_client.publish(topic, status, {
|
||||
retain: true,
|
||||
qos: 2
|
||||
});
|
||||
debug("mqtt status updated to:" + topic + " -> " + status);
|
||||
} else {
|
||||
debug("mqtt status not updated");
|
||||
}
|
||||
} catch (e) {
|
||||
debug(e);
|
||||
}
|
||||
}
|
||||
|
||||
TuyaDevice.onAll('data', function (data) {
|
||||
debug('Data from device ' + this.type + ' :', data);
|
||||
var status = data.dps['1'];
|
||||
if (this.type == "lightbulb" && status == undefined) {
|
||||
status = true;
|
||||
}
|
||||
publishStatus(this, bmap(status));
|
||||
});
|
||||
|
||||
// defines app specific callback...
|
||||
function onExit() {
|
||||
TuyaDevice.disconnectAll();
|
||||
};
|
||||
@@ -81,8 +81,7 @@ TuyaDevice.prototype.set = function (options) {
|
||||
dps
|
||||
};
|
||||
|
||||
debug('Payload:');
|
||||
debug(payload);
|
||||
debug('Payload:', payload);
|
||||
|
||||
// Encrypt data
|
||||
const data = this.device.cipher.encrypt({
|
||||
@@ -111,76 +110,76 @@ TuyaDevice.prototype.set = function (options) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends a query to a device.
|
||||
* @private
|
||||
* @param {String} ip IP of device
|
||||
* @param {Buffer} buffer buffer of data
|
||||
* @returns {Promise<string>} returned data
|
||||
*/
|
||||
TuyaDevice.prototype._sendUnwrapped = function (ip, buffer) {
|
||||
debug('Sending this data: ', buffer.toString('hex'));
|
||||
// /**
|
||||
// * Sends a query to a device.
|
||||
// * @private
|
||||
// * @param {String} ip IP of device
|
||||
// * @param {Buffer} buffer buffer of data
|
||||
// * @returns {Promise<string>} returned data
|
||||
// */
|
||||
// TuyaDevice.prototype._sendUnwrapped = function (ip, buffer) {
|
||||
// debug('Sending this data: ', buffer.toString('hex'));
|
||||
|
||||
const client = new net.Socket();
|
||||
// const client = new net.Socket();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// Attempt to connect
|
||||
client.connect(6668, ip);
|
||||
// return new Promise((resolve, reject) => {
|
||||
// // Attempt to connect
|
||||
// client.connect(6668, ip);
|
||||
|
||||
// Default connect timeout is ~1 minute,
|
||||
// 10 seconds is a more reasonable default
|
||||
// since `retry` is used.
|
||||
client.setTimeout(1000, () => {
|
||||
client.emit('error', new Error('connection timed out'));
|
||||
client.destroy();
|
||||
});
|
||||
// // Default connect timeout is ~1 minute,
|
||||
// // 10 seconds is a more reasonable default
|
||||
// // since `retry` is used.
|
||||
// client.setTimeout(1000, () => {
|
||||
// client.emit('error', new Error('connection timed out'));
|
||||
// client.destroy();
|
||||
// });
|
||||
|
||||
// Send data when connected
|
||||
client.on('connect', () => {
|
||||
debug('Socket connected.');
|
||||
// // Send data when connected
|
||||
// client.on('connect', () => {
|
||||
// debug('Socket connected.');
|
||||
|
||||
// Remove connect timeout
|
||||
client.setTimeout(0);
|
||||
// // Remove connect timeout
|
||||
// client.setTimeout(0);
|
||||
|
||||
// Transmit data
|
||||
client.write(buffer);
|
||||
// // Transmit data
|
||||
// client.write(buffer);
|
||||
|
||||
this._sendTimeout = setTimeout(() => {
|
||||
client.destroy();
|
||||
reject(new Error('Timeout waiting for response'));
|
||||
}, this._responseTimeout * 1000);
|
||||
});
|
||||
// this._sendTimeout = setTimeout(() => {
|
||||
// client.destroy();
|
||||
// reject(new Error('Timeout waiting for response'));
|
||||
// }, this._responseTimeout * 1000);
|
||||
// });
|
||||
|
||||
// Parse response data
|
||||
client.on('data', data => {
|
||||
debug('Received data back:');
|
||||
debug(data.toString('hex'));
|
||||
// // Parse response data
|
||||
// client.on('data', data => {
|
||||
// debug('Received data back:');
|
||||
// debug(data.toString('hex'));
|
||||
|
||||
clearTimeout(this._sendTimeout);
|
||||
client.destroy();
|
||||
// clearTimeout(this._sendTimeout);
|
||||
// client.destroy();
|
||||
|
||||
data = Parser.parse(data);
|
||||
// data = Parser.parse(data);
|
||||
|
||||
if (typeof data === 'object' || typeof data === 'undefined') {
|
||||
} else {
|
||||
// Message is encrypted
|
||||
data =this.device.cipher.decrypt(data);
|
||||
}
|
||||
client.destroy(); // kill client after server's response
|
||||
resolve(data);
|
||||
});
|
||||
// if (typeof data === 'object' || typeof data === 'undefined') {
|
||||
// } else {
|
||||
// // Message is encrypted
|
||||
// data =this.device.cipher.decrypt(data);
|
||||
// }
|
||||
// client.destroy(); // kill client after server's response
|
||||
// resolve(data);
|
||||
// });
|
||||
|
||||
// Handle errors
|
||||
client.on('error', err => {
|
||||
debug('Error event from socket.');
|
||||
client.destroy(); // kill client after server's response
|
||||
// // Handle errors
|
||||
// client.on('error', err => {
|
||||
// debug('Error event from socket.');
|
||||
// client.destroy(); // kill client after server's response
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
err.message = 'Error communicating with device. Make sure nothing else is trying to control it or connected to it.';
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
// // eslint-disable-next-line max-len
|
||||
// err.message = 'Error communicating with device. Make sure nothing else is trying to control it or connected to it.';
|
||||
// reject(err);
|
||||
// });
|
||||
// });
|
||||
// };
|
||||
|
||||
TuyaDevice.prototype.getStatus = function (callback) {
|
||||
var tuya = this;
|
||||
@@ -225,6 +224,9 @@ TuyaDevice.prototype.onoff = function (newStatus, callback) {
|
||||
if (newStatus == "off") {
|
||||
this.off(callback);
|
||||
}
|
||||
if (newStatus == "toggle") {
|
||||
this.toggle(callback);
|
||||
}
|
||||
}
|
||||
|
||||
TuyaDevice.prototype.setColor = function (hexColor, callback) {
|
||||
|
||||
Reference in New Issue
Block a user