tuya extended class integrated and updated mqtt client

This commit is contained in:
KarstenSiedentopp
2018-07-26 18:54:27 +02:00
parent 2e2d5801d3
commit b6ee6b4706
8 changed files with 693 additions and 41 deletions

78
package-lock.json generated
View File

@@ -19,6 +19,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"base64-js": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
"integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
},
"bl": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
@@ -37,6 +42,15 @@
"concat-map": "0.0.1"
}
},
"buffer": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.1.0.tgz",
"integrity": "sha512-YkIRgwsZwJWTnyQrsBTWefizHh+8GYj3kbL1BTiAQ/9pwpino0G7B2gp5tx/FUBqUlvtxV85KNR3mwfAtv15Yw==",
"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",
@@ -71,6 +85,19 @@
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"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==",
"requires": {
"color-name": "1.1.1"
}
},
"color-name": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz",
"integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok="
},
"commist": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/commist/-/commist-1.0.0.tgz",
@@ -102,9 +129,12 @@
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"crc": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/crc/-/crc-3.5.0.tgz",
"integrity": "sha1-mLi6fUiWZbo5efWbITgTdBAaGWQ="
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz",
"integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==",
"requires": {
"buffer": "^5.1.0"
}
},
"cron": {
"version": "1.3.0",
@@ -124,10 +154,15 @@
"which": "^1.2.9"
}
},
"crypto": {
"version": "1.0.1",
"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": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
"requires": {
"ms": "2.0.0"
}
@@ -171,9 +206,9 @@
}
},
"extend": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
"integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"find-up": {
"version": "2.1.0",
@@ -189,9 +224,9 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"get-caller-file": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
"integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U="
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w=="
},
"get-stream": {
"version": "3.0.0",
@@ -248,6 +283,11 @@
"xtend": "^4.0.0"
}
},
"ieee754": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz",
"integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA=="
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -419,9 +459,9 @@
}
},
"mqtt": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/mqtt/-/mqtt-2.18.1.tgz",
"integrity": "sha512-p+RIMFsNb5z65/dy5beKgTnycd3+N8gQ+E2Jnx+0g0OoRza/LCXtUp/vEb3mgWJdljTU+5n4Lc3h0ya994zmVg==",
"version": "2.18.3",
"resolved": "https://registry.npmjs.org/mqtt/-/mqtt-2.18.3.tgz",
"integrity": "sha512-BXCUugFgA6FOWJGxhvUWtVLOdt6hYTmiMGPksEyKuuF1FQ0ji7UJBJ/0kVRMUtUWCAtPGnt4mZZZgJpzNLcuQg==",
"requires": {
"commist": "^1.0.0",
"concat-stream": "^1.6.2",
@@ -457,7 +497,7 @@
"node-forge": {
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz",
"integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ=="
"integrity": "sha1-bBUsNFzhHFL0ZcKr2VfoY5zWdN8="
},
"npm-run-path": {
"version": "2.0.2",
@@ -522,7 +562,7 @@
"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==",
"integrity": "sha1-2N0ZeVldLcATnh/ka4tkbLPN8Dg=",
"requires": {
"p-finally": "^1.0.0"
}
@@ -730,7 +770,7 @@
}
},
"tuyapi": {
"version": "github:codetheweb/tuyapi#bfc7c414621b7ad5956f51bae6301a40ce88f113",
"version": "github:codetheweb/tuyapi#9b31d7c74b2e97ef30069ca0d9faabd59ef932b7",
"from": "github:codetheweb/tuyapi",
"requires": {
"crc": "^3.5.0",
@@ -868,9 +908,9 @@
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
},
"yargs": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-11.0.0.tgz",
"integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==",
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz",
"integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==",
"requires": {
"cliui": "^4.0.0",
"decamelize": "^1.1.1",

View File

@@ -9,9 +9,11 @@
"author": "",
"license": "ISC",
"dependencies": {
"color-convert": "^1.9.2",
"cron": "^1.3.0",
"mqtt": "^2.18.1",
"crypto": "^1.0.1",
"mqtt": "^2.18.3",
"tuyapi": "github:codetheweb/tuyapi",
"yargs": "^11.0.0"
"yargs": "^11.1.0"
}
}

237
tuya-color.js Normal file
View File

@@ -0,0 +1,237 @@
const convert = require('color-convert');
function TuyaColorLight(tuya) {
this.tuya = tuya;
this.colorMode = 'white';
this.brightness = 100; // percentage value use _convertValToPercentage functions below.
this.color = {
H: 130,
S: 100,
L: 50
};
this.color2 = {
H: 0,
S: 100,
L: 50
};
this.hue = this.color.H;
this.saturation = this.color.S;
this.lightness = this.color.L;
this.colorTemperature = 255;
this.colorTempMin = 153;
this.colorTempMax = 500;
this.dps = {};
this.debug = false;
}
TuyaColorLight.prototype._convertPercentageToVal = function (percentage) {
var tmp = Math.round(255 * (percentage / 100));
this.tuyaDebug('Converted ' + percentage + ' to: ' + tmp);
return tmp;
};
TuyaColorLight.prototype._convertValToPercentage = function (val) {
var tmp = Math.round((val / 255) * 100);
this.tuyaDebug('Converted ' + val + ' to: ' + tmp);
return tmp;
};
TuyaColorLight.prototype._convertColorTemperature = function (val) {
var tmpRange = this.colorTempMax - this.colorTempMin;
var tmpCalc = Math.round((val / this.colorTempMax) * 100);
this.tuyaDebug('HK colorTemp Value: ' + val);
this.tuyaDebug('HK colorTemp scale min : ' + this.colorTempMin);
this.tuyaDebug('HK colorTemp scale max : ' + this.colorTempMax);
this.tuyaDebug('HK colorTemp range (tmpRange): ' + tmpRange);
this.tuyaDebug('HK colorTemp % tmpCalc: ' + tmpCalc);
var tuyaColorTemp = this._convertPercentageToVal(tmpCalc);
this.tuyaDebug('HK tuyaColorTemp: ' + tuyaColorTemp);
return tuyaColorTemp;
};
TuyaColorLight.prototype._convertColorTemperatureToHK = function (val) {
var tuyaColorTempPercent = this._convertValToPercentage(this.colorTemperature);
var tmpRange = this.colorTempMax - this.colorTempMin;
var tmpCalc = Math.round((tmpRange * (tuyaColorTempPercent / 100)) + this.colorTempMin);
var hkValue = Math.round(tmpCalc);
this.tuyaDebug('Tuya color Temperature : ' + val);
this.tuyaDebug('Tuya color temp Percent of 255: ' + tuyaColorTempPercent + '%');
this.tuyaDebug('HK colorTemp scale min : ' + this.colorTempMin);
this.tuyaDebug('HK colorTemp scale max : ' + this.colorTempMax);
this.tuyaDebug('HK Color Temp Range: ' + tmpRange);
this.tuyaDebug('HK range %: ' + tuyaColorTempPercent);
this.tuyaDebug('HK Value: ' + hkValue);
return hkValue;
};
TuyaColorLight.prototype.tuyaDebug = function (args) {
if (this.debug === true) {
console.log(args);
}
};
TuyaColorLight.prototype._getAlphaHex = function (brightness) {
// for (var i = 1; i >= 0; i -= 0.01) {
var i = brightness / 100;
this.tuyaDebug('input brightness: ' + brightness + ' and i is ' + i);
var alpha = Math.round(i * 255);
var hex = (alpha + 0x10000).toString(16).substr(-2);
var perc = Math.round(i * 100);
this.tuyaDebug('alpha percent: ' + perc + '% hex: ' + hex + ' alpha: ' + alpha);
return hex;
};
TuyaColorLight.prototype.setSaturation = function (value, callback) {
var colorMode = 'colour';
var saturation = value;
var color = this.color;
color.S = value;
this.color = color;
this.colorMode = colorMode;
this.saturation = saturation;
this.tuyaDebug(' SET SATURATION: ' + value);
};
TuyaColorLight.prototype.setBrightness = function (value, callback) {
this.brightness = value;
var newValue = this._convertPercentageToVal(value);
this.tuyaDebug(this.debugPrefix + " BRIGHTNESS from UI: " + value + ' Converted from 100 to 255 scale: ' + newValue);
}
TuyaColorLight.prototype.setHue = function (value, callback) {
this.tuyaDebug('SET HUE: ' + value);
this.tuyaDebug('Saturation Value: ' + this.color.S);
this.color.H = value;
if (value === 0 && this.color.S === 0) {
this.colorMode = 'white';
this.tuyaDebug('SET Color Mode: \'white\'');
} else {
this.colorMode = 'colour';
this.tuyaDebug('SET Color Mode: \'colour\' -- dahhhhhh british spelling \'coulour\' really is annoying... why you gotta be special?');
}
var returnVal = {};
returnVal.color = this.color;
returnVal.colorMode = this.colorMode;
returnVal.hue = this.color.H;
returnVal.saturation = this.saturation;
};
TuyaColorLight.prototype.setHSL = function (hue, saturation, brightness) {
this.setBrightness(brightness);
this.setSaturation(saturation);
this.setHue(hue);
}
TuyaColorLight.prototype.setColor = function (hexColor) {
var color = convert.hex.hsl(hexColor);
this.tuyaDebug(color);
this.setHSL(color[0], color[1], color[2]);
return this.getDps();
}
TuyaColorLight.prototype.getDps = function () {
var color = this.color;
var color2 = this.color2;
var lightness = Math.round(this.brightness / 2);
var brightness = this.brightness;
var apiBrightness = this._convertPercentageToVal(brightness);
var alphaBrightness = this._getAlphaHex(brightness);
var hexColorOriginal1 = convert.hsl.hex(color.H, color.S, color.L);
var rgbColorOriginal1 = convert.hsl.rgb(color.H, color.S, color.L);
var hexColorOriginal2 = convert.hsl.hex(0, 0, 50);
var rgbColorOriginal2 = convert.hsl.rgb(0, 0, 50);
var hexColor1 = convert.hsl.hex(color.H, color.S, lightness);
var rgbColor1 = convert.hsl.rgb(color.H, color.S, lightness);
var hexColor2 = convert.hsl.hex(0, 0, lightness);
var rgbColor2 = convert.hsl.rgb(0, 0, lightness);
var colorTemperature = this.colorTemperature;
// var ww = Math.round((this.brightness * 255) / 100);
var lightColor = (hexColor1 + hexColor2 + alphaBrightness).toLowerCase();
var temperature = (this.colorMode === 'colour') ? 255 : this._convertColorTemperature(colorTemperature);
dpsTmp = {
'1': true,
'2': this.colorMode,
'3': apiBrightness,
'4': temperature,
'5': lightColor
// '6' : hexColor + hexColor + 'ff'
};
this.tuyaDebug(dpsTmp);
return dpsTmp;
}
module.exports = TuyaColorLight;
//module.exports.color.setColor('#ff2600');
//module.exports.color.setColor('#ffffff');
// let tuya = new TuyaDevice({
// id: '05200399bcddc2e02ec9',
// key: 'b58cf92e8bc5c899',
// ip: '192.168.178.49'
// });
// tuya.get().then(status => {
// console.log('Status:', status);
// var promisses = [];
// promisses.push(
// tuya.set({
// dps: 2,
// set: "colour"
// })
// );
// promisses.push(
// tuya.set({
// dps: 3,
// set: 0
// })
// );
// promisses.push(
// tuya.set({
// dps: 4,
// set: 255
// })
// );
// Promise.all(promisses).then(result => {
// console.log('Result of setting status to ' + !status + ': ' + result);
// tuya.get().then(status => {
// console.log('New status:', status);
// return;
// });
// });
// });

View File

@@ -2,7 +2,7 @@ const TuyaDevice = require('tuyapi');
const TuyaStatus = require('./tuya-status');
let tuya = undefined;
_DEBUG = false;
_DEBUG = true;
exports.setDebug = function (debug) {
_DEBUG = debug;
@@ -59,6 +59,11 @@ exports.setStatus = function (newState, callback) {
}
}
exports.setColor = function (hexColor, callback) {
console.log("tuya-connector.setColor");
TuyaStatus.setColor(hexColor, callback);
}
exports.getCurrent = function () {
TuyaStatus.getCurrent();
}

View File

@@ -24,6 +24,7 @@ function bmap(istate) {
client.on('connect', function () {
var topic = 'tuya/#';
client.subscribe(topic);
console.log("MQTT Subscribed");
})
var knowDevice = function (tuyaID, tuyaKey, tuyaIP) {
@@ -35,18 +36,19 @@ var knowDevice = function (tuyaID, tuyaKey, tuyaIP) {
});
return isKnown;
}
var addDevice = function (tuyaID, tuyaKey, tuyaIP) {
var addDevice = function (type, tuyaID, tuyaKey, tuyaIP) {
var newDevice = {
id: tuyaID,
key: tuyaKey,
ip: tuyaIP
ip: tuyaIP,
type: type
};
autoUpdate.push(newDevice);
}
exports.publishStatus = function (tuyaID, tuyaKey, tuyaIP, status) {
exports.publishStatus = function (tuyaID, tuyaKey, tuyaIP, type, status) {
if (tuyaID != undefined && tuyaKey != undefined && tuyaIP != undefined) {
var topic = "tuya/socket/" + tuyaID + "/" + tuyaKey + "/" + tuyaIP + "/state";
var topic = "tuya/" + type + "/" + tuyaID + "/" + tuyaKey + "/" + tuyaIP + "/state";
client.publish(topic, status, {
retain: true,
qos: 2
@@ -54,38 +56,80 @@ exports.publishStatus = function (tuyaID, tuyaKey, tuyaIP, status) {
}
}
exports.setStatus = function (tuyaID, tuyaKey, tuyaIP, status) {
exports.setStatus = function (type, tuyaID, tuyaKey, tuyaIP, status) {
if (tuyaID != undefined && tuyaKey != undefined && tuyaIP != undefined) {
if (!knowDevice(tuyaID, tuyaKey, tuyaIP)) {
addDevice(tuyaID, tuyaKey, tuyaIP);
addDevice(type, tuyaID, tuyaKey, tuyaIP);
}
TuyaDevice.createDevice(tuyaID, tuyaKey, tuyaIP);
if (TuyaDevice.hasDevice()) {
TuyaDevice.setStatus(status, function (newStatus) {
module.exports.publishStatus(tuyaID, tuyaKey, tuyaIP, newStatus);
module.exports[type].publishStatus(tuyaID, tuyaKey, tuyaIP, newStatus);
});
}
}
}
exports.getStatus = function (tuyaID, tuyaKey, tuyaIP) {
exports.getStatus = function (type, tuyaID, tuyaKey, tuyaIP) {
if (tuyaID != undefined && tuyaKey != undefined && tuyaIP != undefined) {
TuyaDevice.createDevice(tuyaID, tuyaKey, tuyaIP);
if (TuyaDevice.hasDevice()) {
TuyaDevice.getStatus(function (status) {
module.exports.publishStatus(tuyaID, tuyaKey, tuyaIP, bmap(status));
module.exports[type].publishStatus(tuyaID, tuyaKey, tuyaIP, bmap(status));
})
}
}
}
exports.socket = {};
exports.socket.publishStatus = function (tuyaID, tuyaKey, tuyaIP, status) {
return module.exports.publishStatus(tuyaID, tuyaKey, tuyaIP, "socket", status);
}
exports.lightbulb = {};
exports.lightbulb.publishStatus = function (tuyaID, tuyaKey, tuyaIP, status) {
return module.exports.publishStatus(tuyaID, tuyaKey, tuyaIP, "lightbulb", status);
}
exports.lightbulb.publishColor = function (tuyaID, tuyaKey, tuyaIP, color) {
if (tuyaID != undefined && tuyaKey != undefined && tuyaIP != undefined) {
var topic = "tuya/lightbulb/" + tuyaID + "/" + tuyaKey + "/" + tuyaIP + "/state/color";
client.publish(topic, color, {
retain: true,
qos: 2
});
}
}
exports.lightbulb.setColor = function (tuyaID, tuyaKey, tuyaIP, color) {
if (tuyaID != undefined && tuyaKey != undefined && tuyaIP != undefined) {
if (!knowDevice(tuyaID, tuyaKey, tuyaIP)) {
//addDevice("lightbulb", tuyaID, tuyaKey, tuyaIP);
}
TuyaDevice.createDevice(tuyaID, tuyaKey, tuyaIP);
if (TuyaDevice.hasDevice()) {
console.log("tuya-mqtt.lightbulb.setColor: " + color);
TuyaDevice.setColor(color, function (newStatus) {
console.log(newStatus);
module.exports.lightbulb.publishColor(tuyaID, tuyaKey, tuyaIP, newStatus);
});
}
}
}
client.on('message', function (topic, message) {
try {
var topic = topic.split("/");
var type = topic[1];
var exec = topic[5];
if (type == "socket" && exec == "command" && topic.length == 7) {
module.exports.setStatus(topic[2], topic[3], topic[4], topic[6]);
module.exports.setStatus(type, topic[2], topic[3], topic[4], topic[6]);
}
if (type == "lightbulb" && exec == "command" && topic.length == 7) {
module.exports.setStatus(type, topic[2], topic[3], topic[4], topic[6]);
}
if (type == "lightbulb" && exec == "color" && topic.length == 6) {
message = message.toString();
message = message.toLowerCase();
module.exports.lightbulb.setColor(topic[2], topic[3], topic[4], message);
}
} catch (e) {
console.error(e);
@@ -95,7 +139,7 @@ client.on('message', function (topic, message) {
new CronJob('0 */1 * * * *', function () {
try {
autoUpdate.forEach(function (entry) {
module.exports.getStatus(entry.id, entry.key, entry.ip);
module.exports.getStatus(entry.type, entry.id, entry.key, entry.ip);
});
} catch (e) {
console.error(e);

132
tuya-mqtt2.js Normal file
View File

@@ -0,0 +1,132 @@
const mqtt = require('mqtt');
const TuyaDevice = require('./tuyaapi-extended');
const CronJob = require('cron').CronJob;
const crypto = require('crypto');
const autoUpdate = {};
/**
* MQTT Settings
*/
var options = {
clientId: 'tuya_mqtt',
port: 1883,
keepalive: 60
};
const client = mqtt.connect({
host: 'localhost',
port: options.port
});
function bmap(istate) {
return istate ? 'ON' : "OFF";
}
client.on('connect', function () {
var topic = 'tuya/#';
client.subscribe(topic);
console.log("MQTT Subscribed");
updateDeviceStatus();
})
function createHash(tuyaID, tuyaKey, tuyaIP) {
return crypto.createHmac('sha256', "")
.update(tuyaID + tuyaKey + tuyaIP)
.digest('hex');
}
function isKnowDevice(tuyaID, tuyaKey, tuyaIP) {
var isKnown = false;
var searchKey = createHash(tuyaID, tuyaKey, tuyaIP);
if (autoUpdate[searchKey] != undefined) {
isKnown = true;
}
return isKnown;
}
function getKnownDevice(tuyaID, tuyaKey, tuyaIP) {
var searchKey = createHash(tuyaID, tuyaKey, tuyaIP);
return autoUpdate[searchKey];
}
function addDevice(key, device) {
autoUpdate[key] = device;
}
function createDevice(tuyaID, tuyaKey, tuyaIP, tuyaType) {
if (tuyaID != undefined && tuyaKey != undefined) {
var tuya = undefined;
if (isKnowDevice(tuyaID, tuyaKey, tuyaIP)) {
tuya = getKnownDevice(tuyaID, tuyaKey, tuyaIP);
} else {
var key = createHash(tuyaID, tuyaKey, tuyaIP);
var tuya = new TuyaDevice({
id: tuyaID,
key: tuyaKey,
ip: tuyaIP,
type: tuyaType
});
addDevice(key, tuya);
}
return tuya;
}
return undefined;
};
client.on('message', function (topic, message) {
try {
var topic = topic.split("/");
var type = topic[1];
var exec = topic[5];
if (type == "socket" && exec == "command" && topic.length == 7) {
var tuya = createDevice(topic[2], topic[3], topic[4], type);
tuya.onoff(topic[6], function (status) {
publishStatus(tuya, bmap(status));
});
}
if (type == "lightbulb" && exec == "command" && topic.length == 7) {
var tuya = createDevice(topic[2], topic[3], topic[4], type);
tuya.onoff(topic[6], function (status) {
publishStatus(tuya, bmap(status));
});
}
if (type == "lightbulb" && exec == "color" && topic.length == 6) {
message = message.toString();
message = message.toLowerCase();
var tuya = createDevice(topic[2], topic[3], topic[4]);
tuya.setColor(message);
}
} catch (e) {
console.error(e);
}
});
function publishStatus(tuya, status) {
var device = tuya.getDevice();
var type = device.type;
var tuyaID = device.id;
var tuyaKey = device.key;
var tuyaIP = device.ip;
if (tuyaID != undefined && tuyaKey != undefined && tuyaIP != undefined) {
var topic = "tuya/" + type + "/" + tuyaID + "/" + tuyaKey + "/" + tuyaIP + "/state";
client.publish(topic, status, {
retain: true,
qos: 2
});
}
}
function updateDeviceStatus() {
try {
Object.keys(autoUpdate).forEach(function (k) {
var tuya = autoUpdate[k];
tuya.getStatus(function (status) {
publishStatus(tuya, bmap(status));
})
});
} catch (e) {
console.error(e);
}
}
new CronJob('0 */10 * * * *', function () {
updateDeviceStatus();
}, null, true, 'America/Los_Angeles');

View File

@@ -1,4 +1,5 @@
const TuyaDevice = require('tuyapi');
const TuyaColor = require('./tuya-color');
let tuya = undefined;
_DEBUG = true;
@@ -8,10 +9,12 @@ function bmap(istate) {
exports.setDebug = function (debug) {
_DEBUG = debug;
TuyaColor.setDebug(debug);
}
exports.setDevice = function (newTuya) {
tuya = newTuya;
TuyaColor.setDevice(tuya);
}
exports.hasDevice = function () {
@@ -35,13 +38,11 @@ exports.get = function (callback) {
}
}
exports.set = function (newState, callback) {
exports.set = function (options, callback) {
if (this.hasDevice()) {
tuya.set({
set: newState
}).then(result => {
tuya.set(options).then(result => {
if (_DEBUG) {
console.log('Result of setting status to ' + newState + ': ' + result);
console.log('Result of setting status to ' + options + ': ' + result);
}
tuya.get().then(status => {
if (_DEBUG) {
@@ -68,20 +69,31 @@ exports.getCurrent = function () {
exports.toggle = function (callback) {
var self = this;
self.get(function (newStatus) {
self.set(!newStatus, callback);
self.set({
set: !newState
}, callback);
})
}
exports.setColor = function (hexColor, callback) {
var color = new TuyaColor.color();
color.setColor(hexColor);
}
exports.on = function (callback) {
var self = this;
tuya.resolveId().then(() => {
self.set(true, callback);
self.set({
set: true
}, callback);
});
}
exports.off = function (callback) {
var self = this;
tuya.resolveId().then(() => {
self.set(false, callback);
self.set({
set: false
}, callback);
});
}

180
tuyaapi-extended.js Normal file
View File

@@ -0,0 +1,180 @@
const TuyaDevice = require('tuyapi');
const TuyaColor = require('./tuya-color');
const debug = require('debug')('TuyAPI');
// Helpers
const Cipher = require('tuyapi/lib/cipher');
const Parser = require('tuyapi/lib/message-parser')
TuyaDevice.prototype.getDevice = function () {
return this.device;
}
TuyaDevice.prototype.get = function (options) {
// Set empty object as default
options = options ? options : {};
const payload = {
gwId: this.device.id,
devId: this.device.id
};
debug('Payload: ', payload);
// Create byte buffer
const buffer = Parser.encode({
data: payload,
commandByte: '0a'
});
return new Promise((resolve, reject) => {
this._send(this.device.ip, buffer).then(data => {
var dps = data.dps;
if (options.schema === true) {
resolve(data);
} else if (options.dps) {
resolve(dps[options.dps]);
} else {
if (dps != undefined && dps["1"] != undefined) {
resolve(dps['1']);
} else {
resolve(dps);
}
}
}).catch(err => {
reject(err);
});
});
};
TuyaDevice.prototype.set = function (options) {
let dps = {};
var count = Object.keys(options).length;
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
};
debug('Payload:');
debug(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: '07'
});
// Send request to change status
return new Promise((resolve, reject) => {
this._send(this.device.ip, buffer).then(() => {
resolve(true);
}).catch(err => {
reject(err);
});
});
};
TuyaDevice.prototype.getStatus = function (callback) {
var tuya = this;
tuya.get().then(status => {
debug('Current Status: ' + status);
callback.call(this, status);
});
}
TuyaDevice.prototype.setStatus = function (options, callback) {
var tuya = this;
tuya.set(options).then(result => {
debug('Result of setting status to ' + options + ': ' + result);
tuya.get().then(status => {
debug('New status: ' + status);
if (callback != undefined) {
callback.call(this, status);
} else {
debug(status);
}
return;
});
});
}
TuyaDevice.prototype.toggle = function (callback) {
var tuya = this;
tuya.get().then(status => {
tuya.setStatus({
set: !status
}, callback);
});
}
TuyaDevice.prototype.onoff = function (newStatus, callback) {
newStatus = newStatus.toLowerCase();
debug("onoff: " + newStatus);
if (newStatus == "on") {
this.on(callback);
}
if (newStatus == "off") {
this.off(callback);
}
}
TuyaDevice.prototype.setColor = function (hexColor, callback) {
var tuya = this;
var color = new TuyaColor(tuya);
var dps = color.setColor(hexColor);
tuya.get().then(status => {
tuya.setStatus(dps, callback);
});
}
TuyaDevice.prototype.on = function (callback) {
var tuya = this;
tuya.get().then(status => {
tuya.setStatus({
set: true
}, callback);
});
}
TuyaDevice.prototype.off = function (callback) {
debug("off: ");
var tuya = this;
tuya.get().then(status => {
tuya.setStatus({
set: false
}, callback);
});
}
module.exports = TuyaDevice;