From 61433e74a9cef0c1447e80a002f6c250612c7ac3 Mon Sep 17 00:00:00 2001 From: GadgetAngel <33468777+GadgetAngel@users.noreply.github.com> Date: Sat, 20 Apr 2019 09:29:01 -0400 Subject: [PATCH 01/20] This update allows for an essential TuyAPI command to be implemented via the tuya-mqtt.exe MQTT server. {"schema": true} is the ONLY COMMAND that the TuyAPI GET method implements. {"schema": true} allows the user to establish that proper communications with the tuya device can occur WITHOUT actually changing the present STATE of the device. This is the only command that will query the tuya device. The current documentation says that you can query the tuya device over the "dps" TOPIC but from what I see the present state of the software does not force the tuya device to respond when using the "dps" TOPIC. Since {"schema": true} is a command that forces a response from the tuya device, this command has been implemented under the "command" TOPIC. So "command" is the action and '{"schema": true}' becomes the command. The response is returned just like all other commands. I, use this command to guarantee communications has been established with the tuya device. If this "schema" command fails, tuya-mqtt will indicate the result in the openhab.log file and then I, can find out what is physically wrong with the communications. If this command fails the first time due to "socket" error and then goes through on the second attempt then I know that the error was due to TCP communications problem on initial startup. This command helps as a work-a-round for the "ERROR: socket problem" --- tuya-device.js | 8 +++++++- tuya-mqtt.js | 18 +++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/tuya-device.js b/tuya-device.js index 66c21cd..189835c 100644 --- a/tuya-device.js +++ b/tuya-device.js @@ -265,6 +265,12 @@ var TuyaDevice = (function () { }); } + TuyaDevice.prototype.schema = function(obj){ + return this.get(obj).then((status) => { + debug("get", obj); + }); + } + TuyaDevice.prototype.setColor = function (hexColor) { if (!this.connected) return; debugColor("Set color to: ", hexColor); @@ -318,4 +324,4 @@ var TuyaDevice = (function () { return TuyaDevice; }()); -module.exports = TuyaDevice; \ No newline at end of file +module.exports = TuyaDevice; diff --git a/tuya-mqtt.js b/tuya-mqtt.js index 9056b67..850e745 100644 --- a/tuya-mqtt.js +++ b/tuya-mqtt.js @@ -190,6 +190,22 @@ mqtt_client.on('message', function (topic, message) { device.switch(command).then((data) => { debug("set device status completed", data); }); + } + if (command.schema === true) { + // this command is very useful. IT IS A COMMAND. It's place under the command topic. + // It's the ONLY command that does not use device.set to get a result. + // You have to use device.get and send the get method an exact JSON string of { schema: true } + // This schema command does NOT + // change the state of the device, all it does is query the device + // as a confirmation that all communications are working properly. + // Otherwise you have to physically change the state of the device just to + // find out if you can talk to it. If this command returns no errors than + // we know we are have an established communication channel. This is a native TuyAPI call that + // the TuyAPI interface defines (its only available via the GET command. + // this call returns a object of results + device.schema(command).then((data) => { + }); + debug("get (schema) device status completed"); } else { device.set(command).then((data) => { debug("set device status completed", data); @@ -355,4 +371,4 @@ var tester = new MQTT_Tester(); function onExit() { TuyaDevice.disconnectAll(); if (tester) tester.destroy(); -}; \ No newline at end of file +}; From 4ea15db77203717db73dc3c1ea62be8bbcc4f242 Mon Sep 17 00:00:00 2001 From: GadgetAngel <33468777+GadgetAngel@users.noreply.github.com> Date: Sat, 20 Apr 2019 17:50:38 -0400 Subject: [PATCH 02/20] This update allows for an essential TuyAPI command to be implemented via the tuya-mqtt.exe MQTT server. {"schema": true} is the ONLY COMMAND that the TuyAPI GET method implements. Also fixes a problem with the setColor method of TuyaColorLight. When passing colorValue the curly braces are part of the colorValue string, but when you hit the .split to break up the string into Hue, Saturation and Brightness the curly braces are still considered part of the string so the Hue value was always returning a NaN value due to { was part of the Hue. Therefore I created a private function that strips off the beginning and ending curly braces so the right numeric values can be found. {"schema": true} allows the user to establish that proper communications with the tuya device can occur WITHOUT actually changing the present STATE of the device. This is the only command that will query the tuya device. The current documentation says that you can query the tuya device over the "dps" TOPIC but from what I see the present state of the software does not force the tuya device to respond when using the "dps" TOPIC. Since {"schema": true} is a command that forces a response from the tuya device, this command has been implemented under the "command" TOPIC. So "command" is the action and '{"schema": true}' becomes the command. The response is returned just like all other commands. I, use this command to guarantee communications has been established with the tuya device. If this "schema" command fails, tuya-mqtt will indicate the result in the openhab.log file and then I, can find out what is physically wrong with the communications. If this command fails the first time due to "socket" error and then goes through on the second attempt then I know that the error was due to TCP communications problem on initial startup. This command helps as a work-a-round for the "ERROR: socket problem" --- tuya-color.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tuya-color.js b/tuya-color.js index d9d41f2..84b58c1 100644 --- a/tuya-color.js +++ b/tuya-color.js @@ -181,20 +181,26 @@ TuyaColorLight.prototype.setHSL = function (hue, saturation, brightness) { this.setHue(hue); } +TuyaColorLight.prototype._stripBraces = function (string) { + return string.slice(1,-1); +} /** * Set color from given string * @param {String} colorValue could be HEX or HSL color type * @returns {Object} dps settings for given color */ TuyaColorLight.prototype.setColor = function (colorValue) { - debug("Recieved color", colorValue); + var colorValue2; - if (this._ValIsHex(colorValue)) { + debug("Received color", colorValue); + colorValue2 = this._stripBraces(colorValue); + debug("colorValue2 = "+colorValue2); + if (this._ValIsHex(colorValue2)) { debug("Color is Hex"); - var color = convert.hex.hsl(colorValue); + color = convert.hex.hsl(colorValue2); } else { debug("Color is HSL"); - var color = colorValue.split(","); + var color = colorValue2.split(","); // convert strings to numbers color.forEach(function (element, key) { color[key] = parseInt(element, 10); @@ -244,4 +250,4 @@ TuyaColorLight.prototype.getDps = function () { return dpsTmp; } -module.exports = TuyaColorLight; \ No newline at end of file +module.exports = TuyaColorLight; From b5f1f1fd3152dc1c5e3be85824298c1f2197fa33 Mon Sep 17 00:00:00 2001 From: GadgetAngel Date: Sat, 20 Apr 2019 22:57:29 -0400 Subject: [PATCH 03/20] correct file sorry for the screw up.....must be getting tired --- tuya-color.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/tuya-color.js b/tuya-color.js index 84b58c1..d9d41f2 100644 --- a/tuya-color.js +++ b/tuya-color.js @@ -181,26 +181,20 @@ TuyaColorLight.prototype.setHSL = function (hue, saturation, brightness) { this.setHue(hue); } -TuyaColorLight.prototype._stripBraces = function (string) { - return string.slice(1,-1); -} /** * Set color from given string * @param {String} colorValue could be HEX or HSL color type * @returns {Object} dps settings for given color */ TuyaColorLight.prototype.setColor = function (colorValue) { - var colorValue2; + debug("Recieved color", colorValue); - debug("Received color", colorValue); - colorValue2 = this._stripBraces(colorValue); - debug("colorValue2 = "+colorValue2); - if (this._ValIsHex(colorValue2)) { + if (this._ValIsHex(colorValue)) { debug("Color is Hex"); - color = convert.hex.hsl(colorValue2); + var color = convert.hex.hsl(colorValue); } else { debug("Color is HSL"); - var color = colorValue2.split(","); + var color = colorValue.split(","); // convert strings to numbers color.forEach(function (element, key) { color[key] = parseInt(element, 10); @@ -250,4 +244,4 @@ TuyaColorLight.prototype.getDps = function () { return dpsTmp; } -module.exports = TuyaColorLight; +module.exports = TuyaColorLight; \ No newline at end of file From 54745c4f8b5d39f071b519cd07c18dcaade98595 Mon Sep 17 00:00:00 2001 From: GadgetAngel <33468777+GadgetAngel@users.noreply.github.com> Date: Fri, 26 Apr 2019 04:46:15 -0400 Subject: [PATCH 04/20] 1. This update allows for an essential TuyAPI command to be implemented via the tuya-mqtt.exe MQTT server. {"schema": true} is the ONLY COMMAND that the TuyAPI GET method implements. Also fixes a problem with the setColor method of TuyaColorLight. 2. This update does not set dps 3 and dps 4 when setting the bulb in colour mode because some tuya bulbs ignore the dps 5 setting if you set either dps 3 or dps 4 3. This update uses the correct format for dps 5: if the bulb is in colour mode than the dps 3 and dps 4 are ignored but if you set it now some tuya bulbs will ignore dps 5 because you set dps 3 or dps 4 A. So, FOR colour mode the bulb looks at dps 1, dps 2, and dps 5. i. DPS 5 is in the following format: ii. HSL to HEX format are the leftmost hex digits (hex digits 14 - 9) iii. hex digits 8 - 5 are the HSB/HSL Hue value in HEX format iv. hex digits 4 - 3 are the HSB/HSL Saturation percentage as a value (converted to 0-255 scale) in HEX format v. hex digits 2 - 1 are the HSB Brightness percentage as a value (converted to 25-255 scale) in HEX format B. if the bulb is in white mode then the dps 5 value is ignored by the bulb, FOR white mode the bulb looks at dps 1, dps 2, dps 3 and dps 4 i. DPS 3 is the HSB/HSL Brightness percentage converted to a value from 25 to 255 in decimal format ii. DPS 4 is the HSB/HSL Saturation percentage converted to a value from 0 to 255 in decimal format {"schema": true} allows the user to establish that proper communications with the tuya device can occur WITHOUT actually changing the present STATE of the device. This is the only command that will query the tuya device. The current documentation says that you can query the tuya device over the "dps" TOPIC but from what I see the present state of the software does not force the tuya device to respond when using the "dps" TOPIC. Since {"schema": true} is a command that forces a response from the tuya device, this command has been implemented under the "command" TOPIC. So "command" is the action and '{"schema": true}' becomes the command. The response is returned just like all other commands. I, use this command to guarantee communications has been established with the tuya device. If this "schema" command fails, tuya-mqtt will indicate the result in the openhab.log file and then I, can find out what is physically wrong with the communications. If this command fails the first time due to "socket" error and then goes through on the second attempt then I know that the error was due to TCP communications problem on initial startup. This command helps as a work-a-round for the "ERROR: socket problem" --- README.md | 62 ++++++++++++++------- tuya-color.js | 145 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 162 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 69a7427..2168e31 100644 --- a/README.md +++ b/README.md @@ -42,14 +42,19 @@ node tuya-mqtt.js // For debugging purpose, to use DEBUG : https://www.npmjs.com/package/debug -//on Linux machines at the bash command prompt: +//on Linux machines at the bash command prompt, to turn ON DEBUG: DEBUG=* tuya-mqtt.js +//on Linux machines at the bash command prompt, to turn OFF DEBUG: +DEBUG=-* tuya-mqtt.js -// on Windows machines at the cmd.exe command prompt: -Set DEBUG=* tuya-mqtt.js +// on Windows machines at the cmd.exe command prompt, to turn ON DEBUG: +Set DEBUG=* & node c:/openhab2/userdata/etc/scripts/tuya-mqtt.js + +// on Windows machines at the cmd.exe command prompt, to turn OFF DEBUG: +Set DEBUG=-* & node c:/openhab2/userdata/etc/scripts/tuya-mqtt.js ``` -URL to [DEBUG](https://www.npmjs.com/package/debug) +URL to install [DEBUG](https://www.npmjs.com/package/debug) @@ -68,7 +73,12 @@ Change device state (by topic): - tuya////command/toggle - tuya////command/TOGGLE - tuya////command/{ "dps": 1, "set": true } + - tuya////command/{ "dps": 7, "set": true } - tuya////command/{ "multiple": true, "data": { "1": true, "7": true } } + - tuya////command/{ "schema": true } + - tuya////command/{ "multiple": true, "data": { "1": true, "2": "scene_4" } } + - tuya////command/{ "multiple": true, "data": + { "1": true, "2": "scene", "6": "c479000025ffc3" } } Change device state (by payload) Use with OpenHAB 2.X MQTT bindings or others where only a single command topic is preferred: @@ -86,13 +96,18 @@ NOTE: notice that nothing follows the word command, DO NOT but a "/" in after co "toggle" "TOGGLE" "{ \"dps\": 1, \"set\": true }" + "{ \"dps\": 7, \"set\": true }" "{ \"multiple\": true, \"data\": { \"1\": true, \"7\": true } }" + "{ \"schema\": true }" + "{ \"multiple\": true, \"data\": { \"1\": true, \"2\": \"scene_4\" } }" + "{ \"multiple\": true, \"data\": { \"1\": true, \"2\": \"scene\", \"6\": \"c479000025ffc3\" } }" Change color of lightbulb (payload as HSB-Color) tuya////color Example: 64,0,100 + 0,0,89 ``` ### MQTT Topic's (read data) @@ -125,9 +140,9 @@ Switch tuya_kitchen_coffeemachine_mqtt "Steckdose Kaffeemaschine" ( () ["Lighting"] { - mqtt="<[broker:tuya/lightbulb////state:state:default:.*], - >[broker:tuya/lightbulb////command/on:command:ON:true], - >[broker:tuya/lightbulb////command/off:command:OFF:false]" + mqtt="<[broker:tuya////state:state:default:.*], + >[broker:tuya////command/on:command:ON:true], + >[broker:tuya////command/off:command:OFF:false]" } ``` @@ -140,7 +155,7 @@ Group gTuyaLivingColor "Tuya color group" Color tuya_livingroom_colorpicker "Stehlampe farbe" (LivingDining) String tuya_livingroom_ledstrip_tv_color "Set color [%s]" (gTuyaLivingColor, LivingDining) { - mqtt=">[broker:tuya/lightbulb////color:command:*:default]" + mqtt=">[broker:tuya////color:command:*:default]" } @@ -195,12 +210,12 @@ Bridge mqtt:broker:myUnsecureBroker [ host="localhost", secure=false ] Thing mqtt:topic:myCustomMQTT { Channels: - Type switch : tuya_kitchen_coffeemachine_mqtt "Kitchen Coffee Machine MQTT Channel" [ + Type switch : tuya_kitchen_coffeemachine_mqtt_channel "Kitchen Coffee Machine MQTT Channel" [ stateTopic="tuya////state", commandTopic="tuya////command", // optional custom mqtt-payloads for ON and OFF - on="{ \"dps": 1, \"set\": true }, + on="{ \"dps\": 1, \"set\": true }", off="0" ] } @@ -209,7 +224,7 @@ Bridge mqtt:broker:myUnsecureBroker [ host="localhost", secure=false ] # *.item Example Switch tuya_kitchen_coffeemachine_mqtt "Kitchen Coffee Machine Switch" (gKitchen, gTuya) ["Switchable"] { - channel="mqtt:topic:myMosquitto:tuya:coffeemachine" + channel="mqtt:topic:myUnsecureBroker:myCustomMQTT:tuya_kitchen_coffeemachine_mqtt_channel" } ``` @@ -221,17 +236,18 @@ For one RGB bulb you would need a separate channel with the command topic set to Bridge mqtt:broker:myUnsecureBroker [ host="localhost", secure=false ] { - - Type colorHSB : livingroom_floorlamp_1_color "Livingroom floorlamp color MQTT Channel" [ - stateTopic="tuya/lightbulb/05200399bcddc2e02ec9/b58cf92e8bc5c899/192.168.178.49/state", - commandTopic="tuya/lightbulb/05200399bcddc2e02ec9/b58cf92e8bc5c899/192.168.178.49/color" - ] - + Thing mqtt:topic:myCustomMQTT { + Channels: + Type colorHSB : livingroom_floorlamp_1_color "Livingroom floorlamp color MQTT Channel" [ + stateTopic="tuya/05200399bcddc2e02ec9/b58cf92e8bc5c899/192.168.178.49/state", + commandTopic="tuya/05200399bcddc2e02ec9/b58cf92e8bc5c899/192.168.178.49/color" + ] + } } # *.item Example Color tuya_livingroom_colorpicker "Floorlamp colorpicker" (gLivingroom){ - channel="mqtt:topic:myMosquitto:tuya:livingroom_floorlamp_1_color" + channel="mqtt:topic:myUnsecureBroker:myCustomMQTT:livingroom_floorlamp_1_color" } ``` @@ -241,9 +257,15 @@ Color tuya_livingroom_colorpicker "Floorlamp colorpicker" (gLivingroom){ Switch item=tuya_kitchen_coffeemachine_mqtt +# turn the color bulb off or on +Switch item=tuya_livingroom_colorpicker label="RGB lamp [%s]" + +# pick the color level to send to the color bulb via MQTT color Channel +Slider item=tuya_livingroom_colorpicker label="RGB lamp level [%s]" minValue=0 maxValue=100 step=1 + +# color picked and sent via MQTT Color channel +Colorpicker item=tuya_livingroom_colorpicker label="RGB lamp color [%s]" icon="colorpicker" sendFrequency=30000 -# Colorpicker for Lightbulbs -Colorpicker item=tuya_livingroom_colorpicker label="RGB lamp color" sendFrequency=30000 ``` diff --git a/tuya-color.js b/tuya-color.js index 84b58c1..2e702aa 100644 --- a/tuya-color.js +++ b/tuya-color.js @@ -26,6 +26,58 @@ function TuyaColorLight() { this.dps = {}; } +/** + * calculate color value from given brightness percentage + * @param (Integer) percentage 0-100 percentage value + * @returns (Integer) color value from 25 - 255 + * @private + */ +TuyaColorLight.prototype._convertBrightnessPercentageToVal = function(brt_percentage){ + // the brightness scale does not start at 0 but starts at 25 - 255 + // this linear equation is a better fit to the conversion to 255 scale + var tmp = Math.round(2.3206*brt_percentage+22.56); + debug('Converted brightness percentage ' + brt_percentage + ' to: ' + tmp); + return tmp; +} + +/** + * calculate percentage from brightness color value + * @param brt_val 25 - 255 brightness color value + * @returns {Integer} 0 - 100 integer percent + * @private + */ +TuyaColorLight.prototype._convertValtoBrightnessPercentage = function(brt_val){ + var tmp = Math.round( (brt_val-22.56)/2.3206); + debug('Converted brightness value ' + brt_val + ' to: ' + tmp); + return tmp; +} + +/** + * calculate color value from given saturation percentage OR color temperature percentage + * @param (Integer) temp_percentage 0-100 percentage value + * @returns {Integer} saturation or color temperature value from 0 - 255 + * @private + */ +TuyaColorLight.prototype._convertSATorColorTempPercentageToVal = function(temp_percentage){ + // the saturation OR temperature scale does start at 0 - 255 + // this is a perfect linear equation fit for the saturation OR temperature scale conversion + var tmp = Math.round(((2.5498*temp_percentage)-0.4601)); + debug('Converted saturation OR temperature percentage ' + temp_percentage + ' to: ' + tmp); + return tmp; +} + +/** + * calculate percentage from saturation value OR color temperature value + * @param temp_val 0 - 255 saturation or color temperature value + * @returns {Integer} 0 - 100 integer percent + * @private + */ +TuyaColorLight.prototype._convertValtoSATorColorTempPercentage = function(temp_val){ + var tmp = Math.round( (temp_val+0.4601/2.5498)); + debug('Converted saturation OR temperature value ' + temp_val + ' to: ' + tmp); + return tmp; +} + /** * calculate color value from given percentage * @param {Integer} percentage 0-100 percentage value @@ -105,6 +157,18 @@ TuyaColorLight.prototype._ValIsHex = function (h) { return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(h) }; +/** + * get width Hex digits from given value + * @param (Integer) value, decimal value to convert to hex string + * @param (Integer) width, the number of hex digits to return + * @returns {string} value as HEX containing (width) number of hex digits + * @private + */ +TuyaColorLight.prototype._getHex = function (value,width){ + var hex = (value+Math.pow(16, width)).toString(16).slice(-width).toLowerCase(); + debug('value: ' + value + ' hex: ' + hex); + return hex; +} /** * get AlphaHex from percentage brightness * @param {Integer} brightness @@ -138,7 +202,8 @@ TuyaColorLight.prototype.setSaturation = function (value) { */ TuyaColorLight.prototype.setBrightness = function (value) { this.brightness = value; - var newValue = this._convertPercentageToVal(value); + //var newValue = this._convertPercentageToVal(value); + var newValue = this._convertBrightnessPercentageToVal(value); debug("BRIGHTNESS from UI: " + value + ' Converted from 100 to 255 scale: ' + newValue); } @@ -181,26 +246,20 @@ TuyaColorLight.prototype.setHSL = function (hue, saturation, brightness) { this.setHue(hue); } -TuyaColorLight.prototype._stripBraces = function (string) { - return string.slice(1,-1); -} /** * Set color from given string * @param {String} colorValue could be HEX or HSL color type * @returns {Object} dps settings for given color */ TuyaColorLight.prototype.setColor = function (colorValue) { - var colorValue2; + debug("Recieved color", colorValue); - debug("Received color", colorValue); - colorValue2 = this._stripBraces(colorValue); - debug("colorValue2 = "+colorValue2); - if (this._ValIsHex(colorValue2)) { + if (this._ValIsHex(colorValue)) { debug("Color is Hex"); - color = convert.hex.hsl(colorValue2); + var color = convert.hex.hsl(colorValue); } else { debug("Color is HSL"); - var color = colorValue2.split(","); + var color = colorValue.split(","); // convert strings to numbers color.forEach(function (element, key) { color[key] = parseInt(element, 10); @@ -225,29 +284,65 @@ TuyaColorLight.prototype.getDps = function () { var lightness = Math.round(this.brightness / 2); var brightness = this.brightness; - var apiBrightness = this._convertPercentageToVal(brightness); - var alphaBrightness = this._getAlphaHex(brightness); + //var apiBrightness = this._convertPercentageToVal(brightness); + var apiBrightness = this._convertBrightnessPercentageToVal(brightness); + + //var alphaBrightness = this._getAlphaHex(brightness); + var alphaBrightness = this._getHex(apiBrightness,2); var hexColor1 = convert.hsl.hex(color.H, color.S, lightness); - var hexColor2 = convert.hsl.hex(0, 0, lightness); + //var hexColor2 = convert.hsl.hex(0, 0, lightness); + var hexColor2 = this._getHex(color.H,4); + hexColor2 = hexColor2 + this._getHex(this._convertSATorColorTempPercentageToVal(color.S),2); var colorTemperature = this.colorTemperature; var lightColor = (hexColor1 + hexColor2 + alphaBrightness).toLowerCase(); - var temperature = (this.colorMode === 'colour') ? 255 : this._convertColorTemperature(colorTemperature); + //var temperature = (this.colorMode === 'colour') ? 255 : this._convertColorTemperature(colorTemperature); + // color temperature percentage is at a fixed 51% + var temperature = this._convertSATorColorTempPercentageToVal(51); + + // if the bulb is in colour mode than the dps 3 and dps 4 are ignored by the bulb but if you set it now + // some tuya bulbs will ignore dps 5 because you set dps 3 or dps 4 + // FOR colour mode the bulb looks at dps 1, dps 2, and dps 5. + // DPS 5 is in the following format: + // HSL to HEX format are the leftmost hex digits (hex digits 14 - 9) + // hex digits 8 - 5 are the HSB/HSL Hue value in HEX format + // hex digits 4 - 3 are the HSB/HSL Saturation percentage as a value (converted to 0-255 scale) in HEX format + // hex digits 2 - 1 are the HSB Brightness percentage as a value (converted to 25-255 scale) in HEX format + + if (this.colorMode === 'colour') { + dpsTmp = { + '1': true, + '2': this.colorMode, + //'3': apiBrightness, + //'4': temperature, + '5': lightColor + // '6' : hexColor + hexColor + 'ff' + }; + debug("dps", dpsTmp); + return dpsTmp; + } - dpsTmp = { - '1': true, - '2': this.colorMode, - '3': apiBrightness, - '4': temperature, - '5': lightColor - // '6' : hexColor + hexColor + 'ff' - }; - debug("dps", dpsTmp); - return dpsTmp; + // if the bulb is in white mode then the dps 5 value is ignored by the bulb but if you set dps 5 value now + // you may not get a response back from the bulb on the dps values + // FOR white mode the bulb looks at dps 1, dps 2, dps 3 and dps 4 + // DPS 3 is the HSB/HSL Brightness percentage converted to a value from 25 to 255 in decimal format + // DPS 4 is the HSB/HSL Saturation percentage converted to a value from 0 to 255 in decimal format + if (this.colorMode === 'white'){ + dpsTmp = { + '1': true, + '2': this.colorMode, + '3': apiBrightness, + '4': temperature, + //'5': lightColor + // '6' : hexColor + hexColor + 'ff' + }; + debug("dps", dpsTmp); + return dpsTmp; + } } module.exports = TuyaColorLight; From 3b72a000c860c381a1fa7c7b426f7fbc44588b3e Mon Sep 17 00:00:00 2001 From: tsightler Date: Tue, 18 Jun 2019 22:32:18 -0400 Subject: [PATCH 05/20] tuyapi 5.1.x and protocol 3.3 Initial update to work with tuyapi 5.1.x and support for Tuya protocol ver 3.3 via device discovery. --- package-lock.json | 124 +++++++++++++++++++++++----------------------- package.json | 4 +- tuya-device.js | 5 +- tuya-mqtt.js | 18 +++++-- 4 files changed, 80 insertions(+), 71 deletions(-) diff --git a/package-lock.json b/package-lock.json index fb5398d..f2dd6fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "tuya-api", - "version": "1.0.0", + "name": "tuya-mqtt", + "version": "2.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -65,11 +65,11 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "commist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/commist/-/commist-1.0.0.tgz", - "integrity": "sha1-wMNSUBz29S6RJOPvicmAbiAi6+8=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", + "integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==", "requires": { - "leven": "^1.0.0", + "leven": "^2.1.0", "minimist": "^1.1.0" } }, @@ -95,11 +95,12 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", "requires": { - "es5-ext": "^0.10.9" + "es5-ext": "^0.10.50", + "type": "^1.0.1" } }, "debug": { @@ -111,9 +112,9 @@ } }, "duplexify": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", - "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", "requires": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", @@ -130,13 +131,13 @@ } }, "es5-ext": { - "version": "0.10.46", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz", - "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==", + "version": "0.10.50", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.50.tgz", + "integrity": "sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==", "requires": { "es6-iterator": "~2.0.3", "es6-symbol": "~3.1.1", - "next-tick": "1" + "next-tick": "^1.0.0" } }, "es6-iterator": { @@ -203,9 +204,9 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -319,23 +320,15 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, - "json-stable-stringify": { + "json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "requires": { - "jsonify": "~0.0.0" - } - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" }, "leven": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/leven/-/leven-1.0.2.tgz", - "integrity": "sha1-kUS27ryl8dBoAWnxpncNzqYLdcM=" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=" }, "minimatch": { "version": "3.0.4", @@ -372,9 +365,9 @@ } }, "mqtt-packet": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-5.6.0.tgz", - "integrity": "sha512-QECe2ivqcR1LRsPobRsjenEKAC3i1a5gmm+jNKJLrsiq9PaSQ18LlKFuxvhGxWkvGEPadWv6rKd31O4ICqS1Xw==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-5.6.1.tgz", + "integrity": "sha512-eaF9rO2uFrIYEHomJxziuKTDkbWW5psLBaIGCazQSKqYsTaB3n4SpvJ1PexKaDBiPnMLPIFWBIiTYT3IfEJfww==", "requires": { "bl": "^1.2.1", "inherits": "^2.0.3", @@ -383,9 +376,9 @@ } }, "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "next-tick": { "version": "1.0.0", @@ -423,9 +416,9 @@ } }, "p-timeout": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.0.0.tgz", - "integrity": "sha512-HKUsVzU+2A+CcItUxgZ4Q1th5Hh2DHtSsh7gLTMkrL8Ki4Ss736nFp+yqb9M/ZKSKb5il0IXeLzBmUqD3k3mzQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.1.0.tgz", + "integrity": "sha512-C27DYI+tCroT8J8cTEyySGydl2B7FlxrGNF5/wmMbl1V+jeehUCzEE/BVgzRebdm2K3ZitKOKx8YbdFumDyYmw==", "requires": { "p-finally": "^1.0.0" } @@ -531,18 +524,18 @@ } }, "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "requires": { - "readable-stream": "^2.1.5", + "readable-stream": "~2.3.6", "xtend": "~4.0.1" } }, "through2-filter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", - "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", "requires": { "through2": "~2.0.0", "xtend": "~4.0.0" @@ -558,13 +551,13 @@ } }, "tuyapi": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/tuyapi/-/tuyapi-4.0.4.tgz", - "integrity": "sha512-0gU6um3Imj3jHNm1cUuP1mXJdf0Z7H+kjhqlQfxuqUwwaB4hAmENatF6mmRhpF6NDzq181rXxU89hJpmw37Lmg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tuyapi/-/tuyapi-5.1.1.tgz", + "integrity": "sha512-CHcyGPDtOMNt4sUUxKSm1KbaciqrMCIswTTLbqh90UFBeEyDHIwhFR70fyNuF3ez4KwbTI2UjfqjQb7dAUCAVw==", "requires": { "debug": "4.1.1", "p-retry": "4.1.0", - "p-timeout": "3.0.0" + "p-timeout": "3.1.0" }, "dependencies": { "debug": { @@ -577,6 +570,11 @@ } } }, + "type": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/type/-/type-1.0.1.tgz", + "integrity": "sha512-MAM5dBMJCJNKs9E7JXo4CXRAansRfG0nlJxW7Wf6GZzSOvH31zClSaHdIMWLehe/EGMBkqeC55rrkaOr5Oo7Nw==" + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -593,12 +591,12 @@ "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" }, "unique-stream": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", - "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", "requires": { - "json-stable-stringify": "^1.0.0", - "through2-filter": "^2.0.0" + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" } }, "util-deprecate": { @@ -607,14 +605,14 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "websocket-stream": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.1.2.tgz", - "integrity": "sha512-lchLOk435iDWs0jNuL+hiU14i3ERSrMA0IKSiJh7z6X/i4XNsutBZrtqu2CPOZuA4G/zabiqVAos0vW+S7GEVw==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.5.0.tgz", + "integrity": "sha512-EXy/zXb9kNHI07TIMz1oIUIrPZxQRA8aeJ5XYg5ihV8K4kD1DuA+FY6R96HfdIHzlSzS8HiISAfrm+vVQkZBug==", "requires": { "duplexify": "^3.5.1", "inherits": "^2.0.1", "readable-stream": "^2.3.3", - "safe-buffer": "^5.1.1", + "safe-buffer": "^5.1.2", "ws": "^3.2.0", "xtend": "^4.0.0" } diff --git a/package.json b/package.json index 75417cc..e454d1d 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,10 @@ "color-convert": "^1.9.3", "debug": "^3.2.6", "mqtt": "^2.18.8", - "tuyapi": "^4.0.4" + "tuyapi": "^5.1.1" }, "repository": { "type": "git", "url": "git://github.com/TheAgentK/tuya-mqtt.git" } -} \ No newline at end of file +} diff --git a/tuya-device.js b/tuya-device.js index 66c21cd..32fe22c 100644 --- a/tuya-device.js +++ b/tuya-device.js @@ -15,7 +15,8 @@ const debugColor = require('debug')('TuyAPI:device:color'); */ // Helpers -const Parser = require('tuyapi/lib/message-parser'); +const MessageParser = require('tuyapi/lib/message-parser').MessageParser; +const Parser = new MessageParser() /** * Extends default TuyAPI-Class to add some more error handlers @@ -318,4 +319,4 @@ var TuyaDevice = (function () { return TuyaDevice; }()); -module.exports = TuyaDevice; \ No newline at end of file +module.exports = TuyaDevice; diff --git a/tuya-mqtt.js b/tuya-mqtt.js index 9056b67..3d06d1c 100644 --- a/tuya-mqtt.js +++ b/tuya-mqtt.js @@ -177,7 +177,9 @@ mqtt_client.on('message', function (topic, message) { message: message, options: options })); - + if (options.ip == "discover") { + delete options.ip + } var device = new TuyaDevice(options); device.then(function (params) { var device = params.device; @@ -226,7 +228,11 @@ function publishStatus(device, status) { var tuyaKey = device.options.key; var tuyaIP = device.options.ip; - if (typeof tuyaID != "undefined" && typeof tuyaKey != "undefined" && typeof tuyaIP != "undefined") { + if (typeof tuyaIP == "undefined") { + tuyaIP = "discover" + } + + if (typeof tuyaID != "undefined" && typeof tuyaKey != "undefined") { var topic = CONFIG.topic; if (typeof type != "undefined") { topic += type + "/"; @@ -264,7 +270,11 @@ function publishDPS(device, dps) { var tuyaKey = device.options.key; var tuyaIP = device.options.ip; - if (typeof tuyaID != "undefined" && typeof tuyaKey != "undefined" && typeof tuyaIP != "undefined") { + if (typeof tuyaIP == "undefined") { + tuyaIP = "discover" + } + + if (typeof tuyaID != "undefined" && typeof tuyaKey != "undefined") { var baseTopic = CONFIG.topic; if (typeof type != "undefined") { baseTopic += type + "/"; @@ -355,4 +365,4 @@ var tester = new MQTT_Tester(); function onExit() { TuyaDevice.disconnectAll(); if (tester) tester.destroy(); -}; \ No newline at end of file +}; From 604256709ada8747568b6aec06206a396c543c44 Mon Sep 17 00:00:00 2001 From: tsightler Date: Tue, 18 Jun 2019 22:48:59 -0400 Subject: [PATCH 06/20] Update README.md --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 69a7427..573ca67 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,16 @@ This project provides an MQTT client for communication with the home automation :exclamation: There is a greate Step-By-Step guide from user HolgiHab at openhab community ([Step-By-Step Guide]( https://community.openhab.org/t/step-by-step-guide-for-adding-tuya-bulbs-smart-life-to-oh2-using-tuya-mqtt-js-by-agentk/59371)). This guide is not only for light bulbs, but also applies to sockets. :exclamation: +:exclamation: This branch of tuya-mqtt provides initial support for Tuya devices using firmware that implements the 3.3 tuya protocol (fully encrypted communications). Because tuyapi requires specifying the protocol version when using the device IP, this required modifications to the script to either support specifying a version, or use the tuyapi auto discovery function which finds devices via UDP and allows it to auto-detect the protocol. + +To support this you can now replace the IP address in the topic with the word "discover" and the script will find the IP of the devices and automatically detect to correct protocol version. This has the added advantage that, if the IP of the device changes, no changes are required to the topic. To use automatic IP discovery the format of the topics are as follows: + +``` + tuya///discover/state + tuya///discover/command +``` +:exclamation: + ## Instructions: Download this project to your openhab2-script-folder "/etc/openhab2/scripts" and install tuyapi from the same folder that the tuya-mqtt.js is in @@ -54,6 +64,9 @@ URL to [DEBUG](https://www.npmjs.com/package/debug) ### MQTT Topic's (send data) + +*NOTE* that you can replace with "discover" to have the API attempt to automatically discover the IP address. This capability allows support for 3.3 protocol devices without additional configuraiton but does require the system running this script to be on the same IP subnet as the Tuya device because discover relies on UDP broadcast from the devices. + ``` Change device state (by topic): tuya////command/ From a747e122c6238e5de3f1fed7ece5eadc946888d9 Mon Sep 17 00:00:00 2001 From: tsightler Date: Tue, 18 Jun 2019 22:52:34 -0400 Subject: [PATCH 07/20] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 573ca67..548fce7 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,8 @@ URL to [DEBUG](https://www.npmjs.com/package/debug) ### MQTT Topic's (send data) -*NOTE* that you can replace with "discover" to have the API attempt to automatically discover the IP address. This capability allows support for 3.3 protocol devices without additional configuraiton but does require the system running this script to be on the same IP subnet as the Tuya device because discover relies on UDP broadcast from the devices. +**IMPORTANT NOTE** +**It's possible to replace the device IP address \ with the word "discover" to have the API attempt to automatically discover the device IP address. This capability allows support for 3.3 protocol devices without additional configuraiton but does require the system running this script to be on the same IP subnet as the Tuya device because discover relies on UDP broadcast from the devices.** ``` Change device state (by topic): From c6e88ac08b7bb067eb0b136760ea4c3818f0bbbb Mon Sep 17 00:00:00 2001 From: tsightler Date: Tue, 18 Jun 2019 22:53:56 -0400 Subject: [PATCH 08/20] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 548fce7..822e604 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,8 @@ URL to [DEBUG](https://www.npmjs.com/package/debug) ### MQTT Topic's (send data) -**IMPORTANT NOTE** +**-----IMPORTANT NOTE-----** + **It's possible to replace the device IP address \ with the word "discover" to have the API attempt to automatically discover the device IP address. This capability allows support for 3.3 protocol devices without additional configuraiton but does require the system running this script to be on the same IP subnet as the Tuya device because discover relies on UDP broadcast from the devices.** ``` From 6f0e03d3f2700a47d25c17baa7cd1e47edeede8b Mon Sep 17 00:00:00 2001 From: tsightler Date: Tue, 18 Jun 2019 23:04:18 -0400 Subject: [PATCH 09/20] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 822e604..a2671d0 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ This project provides an MQTT client for communication with the home automation :exclamation: There is a greate Step-By-Step guide from user HolgiHab at openhab community ([Step-By-Step Guide]( https://community.openhab.org/t/step-by-step-guide-for-adding-tuya-bulbs-smart-life-to-oh2-using-tuya-mqtt-js-by-agentk/59371)). This guide is not only for light bulbs, but also applies to sockets. :exclamation: -:exclamation: This branch of tuya-mqtt provides initial support for Tuya devices using firmware that implements the 3.3 tuya protocol (fully encrypted communications). Because tuyapi requires specifying the protocol version when using the device IP, this required modifications to the script to either support specifying a version, or use the tuyapi auto discovery function which finds devices via UDP and allows it to auto-detect the protocol. +:exclamation: This branch of tuya-mqtt provides initial support for Tuya devices using firmware that implements the 3.3 tuya protocol (fully encrypted communications). Because tuyapi requires specifying the protocol version when the device IP is specified directly, this required modifications to the script to either support setting the protocol version, or use the tuyapi auto discovery function. This code attempts to implement the minimum changes required to provide support for this protcol via a simple implementation of automatic device discovery. -To support this you can now replace the IP address in the topic with the word "discover" and the script will find the IP of the devices and automatically detect to correct protocol version. This has the added advantage that, if the IP of the device changes, no changes are required to the topic. To use automatic IP discovery the format of the topics are as follows: +To support this it is now possible replace the IP address in the topic (tuyAPI-key) with the word "discover" which will trigger the script to use the automatic device discovery capability. This has the added advantage that, if the IP of the device changes, no changes are required to the topic. To use automatic IP discovery the format of the topics are as follows: ``` tuya///discover/state From 279590eb7178383f304d2f2585915e36ee18d51d Mon Sep 17 00:00:00 2001 From: tsightler Date: Sat, 22 Jun 2019 23:31:38 -0400 Subject: [PATCH 10/20] Support for manual protocol 3.3 Add support to explisitly set protocol version. --- tuya-device.js | 72 +++++--------------------------------------------- tuya-mqtt.js | 20 +++++++++----- 2 files changed, 20 insertions(+), 72 deletions(-) diff --git a/tuya-device.js b/tuya-device.js index 32fe22c..e1594a9 100644 --- a/tuya-device.js +++ b/tuya-device.js @@ -10,72 +10,10 @@ const debugColor = require('debug')('TuyAPI:device:color'); id: '03200240600194781244', key: 'b8bdebab418f5b55', ip: '192.168.178.45', - type: "socket" + type: "ver33" }); */ -// Helpers -const MessageParser = require('tuyapi/lib/message-parser').MessageParser; -const Parser = new MessageParser() - -/** - * Extends default TuyAPI-Class to add some more error handlers - */ -class CustomTuyAPI extends TuyAPI { - get(options) { - // Set empty object as default - options = options ? options : {}; - - const payload = { - gwId: this.device.gwID, - devId: this.device.id - }; - - debug('GET Payload:'); - debug(payload); - - // Create byte buffer - const buffer = Parser.encode({ - data: payload, - commandByte: 10 // 0x0a - }); - - // Send request and parse response - return new Promise((resolve, reject) => { - try { - // Send request - this._send(buffer).then(() => { - // Runs when data event is emitted - const resolveGet = data => { - // Remove self listener - this.removeListener('data', resolveGet); - - try { - if (options.schema === true) { - // Return whole response - resolve(data); - } else if (options.dps) { - // Return specific property - resolve(data.dps[options.dps]); - } else { - // Return first property by default - resolve(data.dps['1']); - } - } catch (error) { - reject(error); - } - }; - - // Add listener - this.on('data', resolveGet); - }); - } catch (error) { - reject(error); - } - }); - } -} - var TuyaDevice = (function () { var devices = []; var events = {}; @@ -126,7 +64,7 @@ var TuyaDevice = (function () { this.options = options; Object.defineProperty(this, 'device', { - value: new CustomTuyAPI(JSON.parse(JSON.stringify(this.options))) + value: new TuyAPI(JSON.parse(JSON.stringify(this.options))) }); this.device.on('data', data => { @@ -183,7 +121,11 @@ var TuyaDevice = (function () { } TuyaDevice.prototype.toString = function () { - return this.type + " (" + this.options.ip + ", " + this.options.id + ", " + this.options.key + ")"; + if (typeof this.type != "undefined") { + return this.type + " (" + this.options.ip + ", " + this.options.id + ", " + this.options.key + ")"; + } else { + return " (" + this.options.ip + ", " + this.options.id + ", " + this.options.key + ")"; + } } TuyaDevice.prototype.triggerAll = function (name, argument) { diff --git a/tuya-mqtt.js b/tuya-mqtt.js index 3d06d1c..6b90765 100644 --- a/tuya-mqtt.js +++ b/tuya-mqtt.js @@ -79,10 +79,10 @@ function IsJsonString(text) { * check mqtt-topic string for old notation with included device type * @param {String} topic */ -function checkTopicForOldNotation(_topic) { +function checkTopicNotation(_topic) { var topic = _topic.split("/"); var type = topic[1]; - var result = (type == "socket" || type == "lightbulb"); + var result = (type == "socket" || type == "lightbulb" || type == "ver3.1" || type == "ver3.3"); return result; } @@ -94,7 +94,7 @@ function checkTopicForOldNotation(_topic) { function getActionFromTopic(_topic) { var topic = _topic.split("/"); - if (checkTopicForOldNotation(_topic)) { + if (checkTopicNotation(_topic)) { return topic[5]; } else { return topic[4]; @@ -111,7 +111,7 @@ function getActionFromTopic(_topic) { function getDeviceFromTopic(_topic) { var topic = _topic.split("/"); - if (checkTopicForOldNotation(_topic)) { + if (checkTopicNotation(_topic)) { return { id: topic[2], key: topic[3], @@ -137,7 +137,7 @@ function getCommandFromTopic(_topic, _message) { var topic = _topic.split("/"); var command = null; - if (checkTopicForOldNotation(_topic)) { + if (checkTopicNotation(_topic)) { command = topic[6]; } else { command = topic[5]; @@ -178,7 +178,13 @@ mqtt_client.on('message', function (topic, message) { options: options })); if (options.ip == "discover") { - delete options.ip + delete options.ip; + } else if (options.type == "ver3.3") { + delete options.type; + options.version = "3.3"; + } else if (options.type == "ver3.1") { + delete options.type; + options.version = "3.1"; } var device = new TuyaDevice(options); device.then(function (params) { @@ -314,7 +320,7 @@ function publishDPS(device, dps) { TuyaDevice.onAll('data', function (data) { try { if (typeof data.dps != "undefined") { - debugTuya('Data from device ' + this.type + ' :', data); + debugTuya('Data from device ' + this.tuyID + ' :', data); var status = data.dps['1']; if (typeof status != "undefined") { publishStatus(this, bmap(status)); From a554cd3f5800c8105c1b1e9e70ab81e5b689b1cf Mon Sep 17 00:00:00 2001 From: tsightler Date: Mon, 29 Jul 2019 08:03:06 -0400 Subject: [PATCH 11/20] Work around memory leak in tuyapi >5.1.x Work around memory leak in tuyapi >5.1.x --- package.json | 6 +- tuya-mqtt.js | 329 ++++++++++++++++++++++++++++----------------------- 2 files changed, 184 insertions(+), 151 deletions(-) diff --git a/package.json b/package.json index e454d1d..ec905fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tuya-mqtt", - "version": "2.0.1", + "version": "2.0.2", "description": "", "homepage": "https://github.com/TheAgentK/tuya-mqtt#readme", "main": "tuya-mqtt.js", @@ -14,8 +14,8 @@ "license": "ISC", "dependencies": { "color-convert": "^1.9.3", - "debug": "^3.2.6", - "mqtt": "^2.18.8", + "debug": "^4.1.1", + "mqtt": "^3.0.0", "tuyapi": "^5.1.1" }, "repository": { diff --git a/tuya-mqtt.js b/tuya-mqtt.js index 6b90765..1ee8c17 100644 --- a/tuya-mqtt.js +++ b/tuya-mqtt.js @@ -1,3 +1,4 @@ +'use strict' const mqtt = require('mqtt'); const TuyaDevice = require('./tuya-device'); const debug = require('debug')('TuyAPI:mqtt'); @@ -6,6 +7,14 @@ const debugTuya = require('debug')('TuyAPI:mqtt:device'); const debugError = require('debug')('TuyAPI:mqtt:error'); var cleanup = require('./cleanup').Cleanup(onExit); +var CONFIG = undefined; +var mqtt_client = undefined; + +// Gloabal variable to track all registered Tuya devices +// Used to disconnect/reconnect devices every 60 minutes +// due to memory leak in tuyapi >5.1.x +const tuyaDevices = new Array(); + function bmap(istate) { return istate ? 'ON' : "OFF"; } @@ -14,56 +23,7 @@ function boolToString(istate) { return istate ? 'true' : "false"; } -var connected = undefined; -var CONFIG = undefined; - -try { - CONFIG = require("./config"); -} catch (e) { - console.error("Configuration file not found") - debugError(e) - process.exit(1) -} - -if (typeof CONFIG.qos == "undefined") { - CONFIG.qos = 2; -} -if (typeof CONFIG.retain == "undefined") { - CONFIG.retain = false; -} - -const mqtt_client = mqtt.connect({ - host: CONFIG.host, - port: CONFIG.port, - username: CONFIG.mqtt_user, - password: CONFIG.mqtt_pass, -}); - -mqtt_client.on('connect', function (err) { - debug("Verbindung mit MQTT-Server hergestellt"); - connected = true; - var topic = CONFIG.topic + '#'; - mqtt_client.subscribe(topic, { - retain: CONFIG.retain, - qos: CONFIG.qos - }); -}); - -mqtt_client.on("reconnect", function (error) { - if (connected) { - debug("Verbindung mit MQTT-Server wurde unterbrochen. Erneuter Verbindungsversuch!"); - } else { - debug("Verbindung mit MQTT-Server konnte nicht herrgestellt werden."); - } - connected = false; -}); - -mqtt_client.on("error", function (error) { - debug("Verbindung mit MQTT-Server konnte nicht herrgestellt werden.", error); - connected = false; -}); - -/** +/* * execute function on topic message */ @@ -112,18 +72,34 @@ function getDeviceFromTopic(_topic) { var topic = _topic.split("/"); if (checkTopicNotation(_topic)) { - return { + var options = { id: topic[2], - key: topic[3], - ip: topic[4], - type: topic[1] + key: topic[3] }; - } else { - return { - id: topic[1], - key: topic[2], - ip: topic[3] + + if (ip !== "discover") { + options.ip = topic[4] + if (type == "ver3.3") { + options.version = "3.3" + } else if (type == "ver3.1") { + options.version = "3.1" + } else { + options.type = topic[1] + }; }; + + return options; + } else { + var options = { + id: topic[1], + key: topic[2] + }; + + if (topic[3] !== "discover") { + options.ip = topic[3] + }; + + return options; } } @@ -165,62 +141,6 @@ function getCommandFromTopic(_topic, _message) { return command; } -mqtt_client.on('message', function (topic, message) { - try { - message = message.toString(); - var action = getActionFromTopic(topic); - var options = getDeviceFromTopic(topic); - - debug("receive settings", JSON.stringify({ - topic: topic, - action: action, - message: message, - options: options - })); - if (options.ip == "discover") { - delete options.ip; - } else if (options.type == "ver3.3") { - delete options.type; - options.version = "3.3"; - } else if (options.type == "ver3.1") { - delete options.type; - options.version = "3.1"; - } - var device = new TuyaDevice(options); - device.then(function (params) { - var device = params.device; - - switch (action) { - case "command": - var command = getCommandFromTopic(topic, message); - debug("receive command", command); - if (command == "toggle") { - device.switch(command).then((data) => { - debug("set device status completed", data); - }); - } else { - device.set(command).then((data) => { - debug("set device status completed", data); - }); - } - break; - case "color": - var color = message.toLowerCase(); - debugColor("set color: ", color); - device.setColor(color).then((data) => { - debug("set device color completed", data); - }); - break; - } - - }).catch((err) => { - debugError(err); - }); - } catch (e) { - debugError(e); - } -}); - /** * Publish current TuyaDevice state to MQTT-Topic * @param {TuyaDevice} device @@ -333,42 +253,155 @@ TuyaDevice.onAll('data', function (data) { }); /** - * MQTT connection tester + * Function call on script exit */ -function MQTT_Tester() { - this.interval = null; - - function mqttConnectionTest() { - if (mqtt_client.connected != connected) { - connected = mqtt_client.connected; - if (connected) { - debug('MQTT-Server verbunden.'); - } else { - debug('MQTT-Server nicht verbunden.'); - } - } - } +function onExit() { + TuyaDevice.disconnectAll(); +}; - this.destroy = function () { - clearInterval(this.interval); - this.interval = undefined; - } +/** + * Function to check if devices has previously been created + * Used for memory leak hack for tuyapi >5.1.x + */ +function existingTuyaDevice(device) { + var existing = false; + tuyaDevices.forEach(tuyaDev => { + if (tuyaDev.hasOwnProperty("options")) { + if (tuyaDev.options.id === device.options.id) { + existing = true; + }; + }; + }); + return existing; +} - this.connect = function () { - this.interval = setInterval(mqttConnectionTest, 1500); - mqttConnectionTest(); - } +// Simple sleep to pause in async functions +function sleep(sec) { + return new Promise(res => setTimeout(res, sec*1000)); +} + +// Main code loop +const main = async() => { + + try { + CONFIG = require("./config"); + } catch (e) { + console.error("Configuration file not found") + debugError(e) + process.exit(1) + } + + if (typeof CONFIG.qos == "undefined") { + CONFIG.qos = 2; + } + if (typeof CONFIG.retain == "undefined") { + CONFIG.retain = false; + } + + mqtt_client = mqtt.connect({ + host: CONFIG.host, + port: CONFIG.port, + username: CONFIG.mqtt_user, + password: CONFIG.mqtt_pass, + }); + + mqtt_client.on('connect', function (err) { + debug("Connection established to MQTT server"); + var topic = CONFIG.topic + '#'; + mqtt_client.subscribe(topic, { + retain: CONFIG.retain, + qos: CONFIG.qos + }); + }); + + mqtt_client.on("reconnect", function (error) { + if (mqtt_client.connected) { + debug("Connection to MQTT server lost. Attempting to reconnect..."); + } else { + debug("Unable to connect to MQTT server"); + } + }); + + mqtt_client.on("error", function (error) { + debug("Unable to connect to MQTT server", error); + }); + + mqtt_client.on('message', function (topic, message) { + try { + message = message.toString(); + var action = getActionFromTopic(topic); + var options = getDeviceFromTopic(topic); + + debug("receive settings", JSON.stringify({ + topic: topic, + action: action, + message: message, + options: options + })); + + var device = new TuyaDevice(options); + + device.then(function (params) { + var device = params.device; + + // If new device add to registered device list + // Used only for reconnecting devices due to tuyapi 5.1.1 memory leak + if (!existingTuyaDevice(device)) { + tuyaDevices.push(device); + } + + switch (action) { + case "command": + var command = getCommandFromTopic(topic, message); + debug("receive command", command); + if (command == "toggle") { + device.switch(command).then((data) => { + debug("set device status completed", data); + }); + } else { + device.set(command).then((data) => { + debug("set device status completed", data); + }); + } + break; + case "color": + var color = message.toLowerCase(); + debugColor("set color: ", color); + device.setColor(color).then((data) => { + debug("set device color completed", data); + }); + break; + } + + }).catch((err) => { + debugError(err); + }); + } catch (e) { + debugError(e); + } + }); - var constructor = (function (that) { - that.connect.call(that); - })(this); } -var tester = new MQTT_Tester(); -/** - * Function call on script exit - */ -function onExit() { - TuyaDevice.disconnectAll(); - if (tester) tester.destroy(); -}; +// Call the main code +main() + +// Hack for memory leak in Tuyapi > 5.1.x +// Disconnect and reconnect all devices every 60 minutes +setInterval(async function() { + tuyaDevices.forEach(tuyaDev => { + var device = new TuyaDevice(tuyaDev.options); + device.then(function (params) { + device = params.device; + device.disconnect(); + }); + }); + await sleep(1); + tuyaDevices.forEach(tuyaDev => { + var device = new TuyaDevice(tuyaDev.options); + device.then(function (params) { + device = params.device; + device.connect(); + }); + }); +}, 3600000); From ec8d2c62a555ef25f254030a07d455fdbc764fa5 Mon Sep 17 00:00:00 2001 From: tsightler Date: Mon, 29 Jul 2019 11:53:46 -0400 Subject: [PATCH 12/20] Fix silly bug in discover/3.3 support Fix silly bug in discover/3.3 support --- tuya-mqtt.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tuya-mqtt.js b/tuya-mqtt.js index 1ee8c17..cd13cac 100644 --- a/tuya-mqtt.js +++ b/tuya-mqtt.js @@ -72,29 +72,40 @@ function getDeviceFromTopic(_topic) { var topic = _topic.split("/"); if (checkTopicNotation(_topic)) { + // When there are 5 topic levels + // topic 2 is id, and topic 3 is key var options = { id: topic[2], key: topic[3] }; - if (ip !== "discover") { + // 4th topic is IP address or "discover" keyword + if (topic[4] !== "discover") { options.ip = topic[4] - if (type == "ver3.3") { + // If IP is manually specified check if topic 1 + // is protocol version and set accordingly + if (topic[1] == "ver3.3") { options.version = "3.3" - } else if (type == "ver3.1") { + } else if (topic[1] == "ver3.1") { options.version = "3.1" } else { + // If topic is not version then it's device type + // Not used anymore but still supported for legacy setups options.type = topic[1] }; }; return options; } else { + // When there are 4 topic levels + // topic 1 is id, topic 2 is key var options = { id: topic[1], key: topic[2] }; + // If topic 3 is not discover assume it is IP address + // Todo: Validate it is an IP address if (topic[3] !== "discover") { options.ip = topic[3] }; From 4a605f4536581a5549df6d44769cf26abd23224f Mon Sep 17 00:00:00 2001 From: tsightler Date: Wed, 31 Jul 2019 23:55:15 -0400 Subject: [PATCH 13/20] Bump dependency to tuyapi 5.1.2 tuyapi 5.1.2 includes fix for memory leak so also removed hack for disconnect/reconnecting devices. --- package.json | 4 ++-- tuya-mqtt.js | 53 +++------------------------------------------------- 2 files changed, 5 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index ec905fb..f958ef6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tuya-mqtt", - "version": "2.0.2", + "version": "2.0.3", "description": "", "homepage": "https://github.com/TheAgentK/tuya-mqtt#readme", "main": "tuya-mqtt.js", @@ -16,7 +16,7 @@ "color-convert": "^1.9.3", "debug": "^4.1.1", "mqtt": "^3.0.0", - "tuyapi": "^5.1.1" + "tuyapi": "^5.1.2" }, "repository": { "type": "git", diff --git a/tuya-mqtt.js b/tuya-mqtt.js index cd13cac..e58b057 100644 --- a/tuya-mqtt.js +++ b/tuya-mqtt.js @@ -10,11 +10,6 @@ var cleanup = require('./cleanup').Cleanup(onExit); var CONFIG = undefined; var mqtt_client = undefined; -// Gloabal variable to track all registered Tuya devices -// Used to disconnect/reconnect devices every 60 minutes -// due to memory leak in tuyapi >5.1.x -const tuyaDevices = new Array(); - function bmap(istate) { return istate ? 'ON' : "OFF"; } @@ -72,7 +67,7 @@ function getDeviceFromTopic(_topic) { var topic = _topic.split("/"); if (checkTopicNotation(_topic)) { - // When there are 5 topic levels + // When there are 5 topic levels // topic 2 is id, and topic 3 is key var options = { id: topic[2], @@ -82,7 +77,7 @@ function getDeviceFromTopic(_topic) { // 4th topic is IP address or "discover" keyword if (topic[4] !== "discover") { options.ip = topic[4] - // If IP is manually specified check if topic 1 + // If IP is manually specified check if topic 1 // is protocol version and set accordingly if (topic[1] == "ver3.3") { options.version = "3.3" @@ -94,7 +89,7 @@ function getDeviceFromTopic(_topic) { options.type = topic[1] }; }; - + return options; } else { // When there are 4 topic levels @@ -270,22 +265,6 @@ function onExit() { TuyaDevice.disconnectAll(); }; -/** - * Function to check if devices has previously been created - * Used for memory leak hack for tuyapi >5.1.x - */ -function existingTuyaDevice(device) { - var existing = false; - tuyaDevices.forEach(tuyaDev => { - if (tuyaDev.hasOwnProperty("options")) { - if (tuyaDev.options.id === device.options.id) { - existing = true; - }; - }; - }); - return existing; -} - // Simple sleep to pause in async functions function sleep(sec) { return new Promise(res => setTimeout(res, sec*1000)); @@ -355,12 +334,6 @@ const main = async() => { device.then(function (params) { var device = params.device; - // If new device add to registered device list - // Used only for reconnecting devices due to tuyapi 5.1.1 memory leak - if (!existingTuyaDevice(device)) { - tuyaDevices.push(device); - } - switch (action) { case "command": var command = getCommandFromTopic(topic, message); @@ -396,23 +369,3 @@ const main = async() => { // Call the main code main() - -// Hack for memory leak in Tuyapi > 5.1.x -// Disconnect and reconnect all devices every 60 minutes -setInterval(async function() { - tuyaDevices.forEach(tuyaDev => { - var device = new TuyaDevice(tuyaDev.options); - device.then(function (params) { - device = params.device; - device.disconnect(); - }); - }); - await sleep(1); - tuyaDevices.forEach(tuyaDev => { - var device = new TuyaDevice(tuyaDev.options); - device.then(function (params) { - device = params.device; - device.connect(); - }); - }); -}, 3600000); From 89a7bfd04d78ebad1868188b5015545b115fbb47 Mon Sep 17 00:00:00 2001 From: tsightler Date: Thu, 17 Sep 2020 21:38:52 -0400 Subject: [PATCH 14/20] Release 2.1.0 * Merge protocol 3.3 support * Update TuyAPI to 5.3.1 * Fixes for color settings issues with some devices * Support to force device status update with schema query --- CHANGELOG.md | 14 ++- README.md | 184 +++--------------------------- docs/openHAB.md | 142 +++++++++++++++++++++++ package-lock.json | 279 ++++++++++++++++++++++++++-------------------- package.json | 12 +- tuya-mqtt.js | 96 +++------------- 6 files changed, 347 insertions(+), 380 deletions(-) create mode 100644 docs/openHAB.md diff --git a/CHANGELOG.md b/CHANGELOG.md index d09a957..fa2622a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -## [Unreleased] +## [2.1.0] +### Added +- Added ability to update validate communicaton with device and update state topic by issuing { "schema": true } command +- Added support for protocol 3.3 either via automatic device discovery or manual specification when using IP address + +### Changed +- Can specify "discover" instead of IP address to automatically find device (only works if device on same IP subnet as system running this script). This mode will also automatically detect 3.1 and 3.3 protocol devices +- Can manually specific protocol via ver3.1/ver3.3 in topic line after tuya/ +- Bump Tuyapi version to v5.3.x +- Bump MQTT version to v4.x.x +- Moved openHAB config to it's own document since many users use this with other tools +- Verious other fixes and cleanups ## [2.0.1] ### Added - Added capability to set multiple dps values over MQTT-Command -- - Custom Set-Function for TuyAPI-Class (added error handling for "index [1] not found" error) ### Changed diff --git a/README.md b/README.md index 38cc36f..30055ad 100644 --- a/README.md +++ b/README.md @@ -2,29 +2,17 @@ MQTT interface for Tuya home automation devices sold under various names. This is a wrapper script for the Project codetheweb/tuyapi. https://github.com/codetheweb/tuyapi -This project provides an MQTT client for communication with the home automation devices. - -:exclamation: There is a greate Step-By-Step guide from user HolgiHab at openhab community ([Step-By-Step Guide]( -https://community.openhab.org/t/step-by-step-guide-for-adding-tuya-bulbs-smart-life-to-oh2-using-tuya-mqtt-js-by-agentk/59371)). This guide is not only for light bulbs, but also applies to sockets. :exclamation: - -:exclamation: This branch of tuya-mqtt provides initial support for Tuya devices using firmware that implements the 3.3 tuya protocol (fully encrypted communications). Because tuyapi requires specifying the protocol version when the device IP is specified directly, this required modifications to the script to either support setting the protocol version, or use the tuyapi auto discovery function. This code attempts to implement the minimum changes required to provide support for this protcol via a simple implementation of automatic device discovery. - -To support this it is now possible replace the IP address in the topic (tuyAPI-key) with the word "discover" which will trigger the script to use the automatic device discovery capability. This has the added advantage that, if the IP of the device changes, no changes are required to the topic. To use automatic IP discovery the format of the topics are as follows: - -``` - tuya///discover/state - tuya///discover/command -``` -:exclamation: +This project provides an MQTT gateway for locally controlling home automation devices made by Tuya Inc. To use this script you will need to obtain the device ID and local keys for each of your devices after they are configured via the Tuya/Smart Life or other Tuya compatible app (there are many). With this information it is possible to communicate locally with Tuya devices using protocol 3.1 and 3.3, without using the Tuya Cloud service, however, getting the keys requires signing up for a Tuya IOT developer account or using one of several other alternative methods (such as dumping the memory of a Tuya based app running on Andriod). Acquiring keys is not part of this project, please see the instructions at the TuyAPI project (on which this script is based) available at https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md. ## Instructions: -Download this project to your openhab2-script-folder "/etc/openhab2/scripts" and install tuyapi from the same folder that the tuya-mqtt.js is in +Download this project to your system into any directory (example below uses /opt/tuya-mqtt) and install tuyapi from the same folder that the tuya-mqtt.js is in ``` -cd /etc/openhab2/scripts +// switch to opt directory +cd /opt // clone this project -git clone git@github.com:TheAgentK/tuya-mqtt.git +git clone https://github.com/TheAgentK/tuya-mqtt // change directory to the project directory cd tuya-mqtt @@ -33,7 +21,7 @@ cd tuya-mqtt npm install ``` -See the setup instructions found here: https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md +This found here: https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md ## Basic Usage @@ -64,16 +52,18 @@ Set DEBUG=* & node c:/openhab2/userdata/etc/scripts/tuya-mqtt.js // on Windows machines at the cmd.exe command prompt, to turn OFF DEBUG: Set DEBUG=-* & node c:/openhab2/userdata/etc/scripts/tuya-mqtt.js ``` -URL to install [DEBUG](https://www.npmjs.com/package/debug) - - ### MQTT Topic's (send data) - **-----IMPORTANT NOTE-----** - **It's possible to replace the device IP address \ with the word "discover" to have the API attempt to automatically discover the device IP address. This capability allows support for 3.3 protocol devices without additional configuraiton but does require the system running this script to be on the same IP subnet as the Tuya device because discover relies on UDP broadcast from the devices.** - +``` + tuya///discover/state + tuya///discover/command +``` +If discovery will not work for your case you can still use the IP address, but, to use protocol 3.3 you must specify it in the topic explicitly +``` + tuya/ver3.3//////command ``` Change device state (by topic): tuya////command/ @@ -139,150 +129,10 @@ Device DPS-Values: ``` ## Issues -There are some reliability issues with tuyapi. Latest changes changed the syntax but still getting error maybe at an even higher rate. - -All questions regarding the tuyAPI please ask in the project https://github.com/codetheweb/tuyapi . - - -## Example items for OpenHAB 1.x Bindings (still works with OH > 2.4 but only if legacy 1.x MQTT bindings are enabled) -### simple switch on/off -``` - -Switch tuya_kitchen_coffeemachine_mqtt "Steckdose Kaffeemaschine" () ["Switchable"] { - mqtt="<[broker:tuya////state:state:default:.*], - >[broker:tuya////command/on:command:ON:true], - >[broker:tuya////command/off:command:OFF:false]" -} - -Switch tuya_livingroom_ledstrip_tv "LED Regal" () ["Lighting"] { - mqtt="<[broker:tuya////state:state:default:.*], - >[broker:tuya////command/on:command:ON:true], - >[broker:tuya////command/off:command:OFF:false]" -} - -``` - -### change color of lightbulb -``` - -# .items -Group gTuyaLivingColor "Tuya color group" -Color tuya_livingroom_colorpicker "Stehlampe farbe" (LivingDining) - -String tuya_livingroom_ledstrip_tv_color "Set color [%s]" (gTuyaLivingColor, LivingDining) { - mqtt=">[broker:tuya////color:command:*:default]" -} - - - -# .rules -import org.openhab.core.library.types.HSBType; - -rule "Set HSB value of item RGBLed to RGB color value" - when - Item tuya_livingroom_colorpicker received command - then - var appName = "Colorpicker.livingroom" - var color = receivedCommand.toString; - - // get all colors and send it via mqtt if light ist enabled - gTuyaLivingColor.members.forEach[ i | - var name = i.name; - var stateName = name.toString.split("_color").get(0); - var stateItem = gTuyaLights.allMembers.filter [ conf | conf.name.contains(stateName.toString) ].head; - - if(stateItem.state == ON){ - logInfo(appName, name + " change to color: " + color); - i.sendCommand(color); - Thread::sleep(400); - } - ] - end +Not all Tuya protocols are supported. For example, some devices use protocol 3.2 which currently remains unsupported by the TuyAPI project due to lack of enough information to reverse engineer the protcol. If you are unable to control your devices with tuya-mqtt please verify that you can query and control them with tuya-cli first. If tuya-cli works, then this script should also work, if it doesn't then this script will not work either. -``` - -## Example items for OpenHAB 2.4 Bindings -### simple switch on/off - -With OpenHAB 2.X MQTT bindings you can add devices using a generic MQTT Thing via PaperUI or -configuration files. For PaperUI simply at the generic MQTT Thing and set the state and -command topics as follows: -``` - - tuya////state - - tuya////command - -``` - -If you prefer using configuration files vs PaperUI, it should look something like this: -See also OpenHAB 2.X MQTT binding [documentation](https://www.openhab.org/v2.4/addons/bindings/mqtt.generic/) - -``` - -Bridge mqtt:broker:myUnsecureBroker [ host="localhost", secure=false ] -{ - - Thing mqtt:topic:myCustomMQTT { - Channels: - Type switch : tuya_kitchen_coffeemachine_mqtt_channel "Kitchen Coffee Machine MQTT Channel" [ - stateTopic="tuya////state", - commandTopic="tuya////command", - - // optional custom mqtt-payloads for ON and OFF - on="{ \"dps\": 1, \"set\": true }", - off="0" - ] - } - -} - -# *.item Example -Switch tuya_kitchen_coffeemachine_mqtt "Kitchen Coffee Machine Switch" (gKitchen, gTuya) ["Switchable"] { - channel="mqtt:topic:myUnsecureBroker:myCustomMQTT:tuya_kitchen_coffeemachine_mqtt_channel" -} - -``` - -For one RGB bulb you would need a separate channel with the command topic set to -`tuya////color` and link that to your color item. - -``` - -Bridge mqtt:broker:myUnsecureBroker [ host="localhost", secure=false ] -{ - Thing mqtt:topic:myCustomMQTT { - Channels: - Type colorHSB : livingroom_floorlamp_1_color "Livingroom floorlamp color MQTT Channel" [ - stateTopic="tuya/05200399bcddc2e02ec9/b58cf92e8bc5c899/192.168.178.49/state", - commandTopic="tuya/05200399bcddc2e02ec9/b58cf92e8bc5c899/192.168.178.49/color" - ] - } -} - -# *.item Example -Color tuya_livingroom_colorpicker "Floorlamp colorpicker" (gLivingroom){ - channel="mqtt:topic:myUnsecureBroker:myCustomMQTT:livingroom_floorlamp_1_color" -} - -``` - -#### Basic UI sitemap -``` - -Switch item=tuya_kitchen_coffeemachine_mqtt - -# turn the color bulb off or on -Switch item=tuya_livingroom_colorpicker label="RGB lamp [%s]" - -# pick the color level to send to the color bulb via MQTT color Channel -Slider item=tuya_livingroom_colorpicker label="RGB lamp level [%s]" minValue=0 maxValue=100 step=1 - -# color picked and sent via MQTT Color channel -Colorpicker item=tuya_livingroom_colorpicker label="RGB lamp color [%s]" icon="colorpicker" sendFrequency=30000 - - -``` +## Integration with other tools +openHAB examples are (here)[docs/openHAB.md]. ## Contributors - [TheAgentK](https://github.com/TheAgentK) diff --git a/docs/openHAB.md b/docs/openHAB.md new file mode 100644 index 0000000..2c0510f --- /dev/null +++ b/docs/openHAB.md @@ -0,0 +1,142 @@ +:exclamation: There is a greate Step-By-Step guide from user HolgiHab at openhab community ([Step-By-Step Guide]( +https://community.openhab.org/t/step-by-step-guide-for-adding-tuya-bulbs-smart-life-to-oh2-using-tuya-mqtt-js-by-agentk/59371)). This guide is not only for light bulbs, but also applies to sockets. :exclamation: + +## Example items for OpenHAB 1.x Bindings (still works with OH > 2.4 but only if legacy 1.x MQTT bindings are enabled) +### simple switch on/off +``` + +Switch tuya_kitchen_coffeemachine_mqtt "Steckdose Kaffeemaschine" () ["Switchable"] { + mqtt="<[broker:tuya////state:state:default:.*], + >[broker:tuya////command/on:command:ON:true], + >[broker:tuya////command/off:command:OFF:false]" +} + +Switch tuya_livingroom_ledstrip_tv "LED Regal" () ["Lighting"] { + mqtt="<[broker:tuya////state:state:default:.*], + >[broker:tuya////command/on:command:ON:true], + >[broker:tuya////command/off:command:OFF:false]" +} + +``` + +### change color of lightbulb +``` + +# .items +Group gTuyaLivingColor "Tuya color group" +Color tuya_livingroom_colorpicker "Stehlampe farbe" (LivingDining) + +String tuya_livingroom_ledstrip_tv_color "Set color [%s]" (gTuyaLivingColor, LivingDining) { + mqtt=">[broker:tuya////color:command:*:default]" +} + + + +# .rules +import org.openhab.core.library.types.HSBType; + +rule "Set HSB value of item RGBLed to RGB color value" + when + Item tuya_livingroom_colorpicker received command + then + var appName = "Colorpicker.livingroom" + var color = receivedCommand.toString; + + // get all colors and send it via mqtt if light ist enabled + gTuyaLivingColor.members.forEach[ i | + var name = i.name; + var stateName = name.toString.split("_color").get(0); + var stateItem = gTuyaLights.allMembers.filter [ conf | conf.name.contains(stateName.toString) ].head; + + if(stateItem.state == ON){ + logInfo(appName, name + " change to color: " + color); + i.sendCommand(color); + Thread::sleep(400); + } + ] + end + +``` + +## Example items for OpenHAB 2.4 Bindings +### simple switch on/off + +With OpenHAB 2.X MQTT bindings you can add devices using a generic MQTT Thing via PaperUI or +configuration files. For PaperUI simply at the generic MQTT Thing and set the state and +command topics as follows: +``` + + tuya////state + + tuya////command + +``` + +If you prefer using configuration files vs PaperUI, it should look something like this: +See also OpenHAB 2.X MQTT binding [documentation](https://www.openhab.org/v2.4/addons/bindings/mqtt.generic/) + +``` + +Bridge mqtt:broker:myUnsecureBroker [ host="localhost", secure=false ] +{ + + Thing mqtt:topic:myCustomMQTT { + Channels: + Type switch : tuya_kitchen_coffeemachine_mqtt_channel "Kitchen Coffee Machine MQTT Channel" [ + stateTopic="tuya////state", + commandTopic="tuya////command", + + // optional custom mqtt-payloads for ON and OFF + on="{ \"dps\": 1, \"set\": true }", + off="0" + ] + } + +} + +# *.item Example +Switch tuya_kitchen_coffeemachine_mqtt "Kitchen Coffee Machine Switch" (gKitchen, gTuya) ["Switchable"] { + channel="mqtt:topic:myUnsecureBroker:myCustomMQTT:tuya_kitchen_coffeemachine_mqtt_channel" +} + +``` + +For one RGB bulb you would need a separate channel with the command topic set to +`tuya////color` and link that to your color item. + +``` + +Bridge mqtt:broker:myUnsecureBroker [ host="localhost", secure=false ] +{ + Thing mqtt:topic:myCustomMQTT { + Channels: + Type colorHSB : livingroom_floorlamp_1_color "Livingroom floorlamp color MQTT Channel" [ + stateTopic="tuya/05200399bcddc2e02ec9/b58cf92e8bc5c899/192.168.178.49/state", + commandTopic="tuya/05200399bcddc2e02ec9/b58cf92e8bc5c899/192.168.178.49/color" + ] + } +} + +# *.item Example +Color tuya_livingroom_colorpicker "Floorlamp colorpicker" (gLivingroom){ + channel="mqtt:topic:myUnsecureBroker:myCustomMQTT:livingroom_floorlamp_1_color" +} + +``` + +#### Basic UI sitemap +``` + +Switch item=tuya_kitchen_coffeemachine_mqtt + +# turn the color bulb off or on +Switch item=tuya_livingroom_colorpicker label="RGB lamp [%s]" + +# pick the color level to send to the color bulb via MQTT color Channel +Slider item=tuya_livingroom_colorpicker label="RGB lamp level [%s]" minValue=0 maxValue=100 step=1 + +# color picked and sent via MQTT Color channel +Colorpicker item=tuya_livingroom_colorpicker label="RGB lamp color [%s]" icon="colorpicker" sendFrequency=30000 + + +``` diff --git a/package-lock.json b/package-lock.json index f2dd6fd..9a4d42f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "tuya-mqtt", - "version": "2.0.1", + "version": "2.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -9,23 +9,36 @@ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, "bl": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", + "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } } }, "brace-expansion": { @@ -37,6 +50,15 @@ "concat-map": "0.0.1" } }, + "buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -52,17 +74,17 @@ } }, "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "requires": { - "color-name": "1.1.3" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "commist": { "version": "1.1.0", @@ -104,9 +126,9 @@ } }, "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "requires": { "ms": "^2.1.1" } @@ -123,21 +145,21 @@ } }, "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "requires": { "once": "^1.4.0" } }, "es5-ext": { - "version": "0.10.50", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.50.tgz", - "integrity": "sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==", + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", "requires": { "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.1", - "next-tick": "^1.0.0" + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" } }, "es6-iterator": { @@ -173,15 +195,26 @@ "es6-iterator": "~2.0.1", "es6-symbol": "3.1.1", "event-emitter": "~0.3.5" + }, + "dependencies": { + "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" + } + } } }, "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "d": "^1.0.1", + "ext": "^1.1.2" } }, "event-emitter": { @@ -193,6 +226,21 @@ "es5-ext": "~0.10.14" } }, + "ext": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "requires": { + "type": "^2.0.0" + }, + "dependencies": { + "type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", + "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -204,9 +252,9 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -253,6 +301,11 @@ "xtend": "^4.0.0" } }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -263,9 +316,9 @@ } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "is-absolute": { "version": "1.0.0", @@ -339,40 +392,41 @@ } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "mqtt": { - "version": "2.18.8", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-2.18.8.tgz", - "integrity": "sha512-3h6oHlPY/yWwtC2J3geraYRtVVoRM6wdI+uchF4nvSSafXPZnaKqF8xnX+S22SU/FcgEAgockVIlOaAX3fkMpA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.1.tgz", + "integrity": "sha512-Iv893r+jWlo5GkNcPOfCGwW8M49IixwHiKLFFYTociEymSibUVCORVEjPXWPGzSxhn7BdlUeHicbRmWiv0Crkg==", "requires": { + "base64-js": "^1.3.0", "commist": "^1.0.0", "concat-stream": "^1.6.2", + "debug": "^4.1.1", "end-of-stream": "^1.4.1", "es6-map": "^0.1.5", "help-me": "^1.0.1", "inherits": "^2.0.3", - "minimist": "^1.2.0", - "mqtt-packet": "^5.6.0", + "minimist": "^1.2.5", + "mqtt-packet": "^6.3.2", "pump": "^3.0.0", "readable-stream": "^2.3.6", "reinterval": "^1.1.0", - "split2": "^2.1.1", - "websocket-stream": "^5.1.2", + "split2": "^3.1.0", + "ws": "^7.3.1", "xtend": "^4.0.1" } }, "mqtt-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-5.6.1.tgz", - "integrity": "sha512-eaF9rO2uFrIYEHomJxziuKTDkbWW5psLBaIGCazQSKqYsTaB3n4SpvJ1PexKaDBiPnMLPIFWBIiTYT3IfEJfww==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.6.0.tgz", + "integrity": "sha512-LvghnKMFC70hKWMVykmhJarlO5e7lT3t9s9A2qPCUx+lazL3Mq55U+eCV0eLi7/nRRQYvEUWo/2tTo89EjnCJQ==", "requires": { - "bl": "^1.2.1", - "inherits": "^2.0.3", - "process-nextick-args": "^2.0.0", - "safe-buffer": "^5.1.0" + "bl": "^4.0.2", + "debug": "^4.1.1", + "process-nextick-args": "^2.0.1" } }, "ms": { @@ -407,18 +461,18 @@ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, "p-retry": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.1.0.tgz", - "integrity": "sha512-oepllyG9gX1qH4Sm20YAKxg1GA7L7puhvGnTfimi31P07zSIj7SDV6YtuAx9nbJF51DES+2CIIRkXs8GKqWJxA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz", + "integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==", "requires": { "@types/retry": "^0.12.0", "retry": "^0.12.0" } }, "p-timeout": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.1.0.tgz", - "integrity": "sha512-C27DYI+tCroT8J8cTEyySGydl2B7FlxrGNF5/wmMbl1V+jeehUCzEE/BVgzRebdm2K3ZitKOKx8YbdFumDyYmw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", "requires": { "p-finally": "^1.0.0" } @@ -434,9 +488,9 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "pump": { "version": "3.0.0", @@ -469,9 +523,9 @@ } }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -503,17 +557,29 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "split2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", - "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", "requires": { - "through2": "^2.0.2" + "readable-stream": "^3.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } } }, "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" }, "string_decoder": { "version": "1.1.1", @@ -551,40 +617,25 @@ } }, "tuyapi": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tuyapi/-/tuyapi-5.1.1.tgz", - "integrity": "sha512-CHcyGPDtOMNt4sUUxKSm1KbaciqrMCIswTTLbqh90UFBeEyDHIwhFR70fyNuF3ez4KwbTI2UjfqjQb7dAUCAVw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/tuyapi/-/tuyapi-5.3.1.tgz", + "integrity": "sha512-l0bbWxe4L8J7/bAQn0bJtBVbVDAEglC1T3a/YKYM3UvDXaKgFQUDVKhfQfHFAt0bzXVq1TeqU0zG4WIrxgiTHg==", "requires": { "debug": "4.1.1", - "p-retry": "4.1.0", - "p-timeout": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } + "p-retry": "4.2.0", + "p-timeout": "3.2.0" } }, "type": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/type/-/type-1.0.1.tgz", - "integrity": "sha512-MAM5dBMJCJNKs9E7JXo4CXRAansRfG0nlJxW7Wf6GZzSOvH31zClSaHdIMWLehe/EGMBkqeC55rrkaOr5Oo7Nw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" - }, "unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -604,38 +655,20 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, - "websocket-stream": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.5.0.tgz", - "integrity": "sha512-EXy/zXb9kNHI07TIMz1oIUIrPZxQRA8aeJ5XYg5ihV8K4kD1DuA+FY6R96HfdIHzlSzS8HiISAfrm+vVQkZBug==", - "requires": { - "duplexify": "^3.5.1", - "inherits": "^2.0.1", - "readable-stream": "^2.3.3", - "safe-buffer": "^5.1.2", - "ws": "^3.2.0", - "xtend": "^4.0.0" - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" }, "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" } } } diff --git a/package.json b/package.json index f958ef6..f889d3b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tuya-mqtt", - "version": "2.0.3", - "description": "", + "version": "2.1.0", + "description": "Control Tuya devices locally via MQTT", "homepage": "https://github.com/TheAgentK/tuya-mqtt#readme", "main": "tuya-mqtt.js", "scripts": { @@ -9,14 +9,14 @@ }, "author": { "name": "TheAgentK", - "email": "lulatsch22@googlemail.com" + "email": "lulattsch22@googlemail.com" }, "license": "ISC", "dependencies": { - "color-convert": "^1.9.3", + "color-convert": "^2.0.1", "debug": "^4.1.1", - "mqtt": "^3.0.0", - "tuyapi": "^5.1.2" + "mqtt": "^4.2.1", + "tuyapi": "^5.3.1" }, "repository": { "type": "git", diff --git a/tuya-mqtt.js b/tuya-mqtt.js index f62acb4..ca802c2 100644 --- a/tuya-mqtt.js +++ b/tuya-mqtt.js @@ -1,4 +1,3 @@ -'use strict' const mqtt = require('mqtt'); const TuyaDevice = require('./tuya-device'); const debug = require('debug')('TuyAPI:mqtt'); @@ -147,70 +146,6 @@ function getCommandFromTopic(_topic, _message) { return command; } -mqtt_client.on('message', function (topic, message) { - try { - message = message.toString(); - var action = getActionFromTopic(topic); - var options = getDeviceFromTopic(topic); - - debug("receive settings", JSON.stringify({ - topic: topic, - action: action, - message: message, - options: options - })); - - var device = new TuyaDevice(options); - device.then(function (params) { - var device = params.device; - - switch (action) { - case "command": - var command = getCommandFromTopic(topic, message); - debug("receive command", command); - if (command == "toggle") { - device.switch(command).then((data) => { - debug("set device status completed", data); - }); - } - if (command.schema === true) { - // this command is very useful. IT IS A COMMAND. It's place under the command topic. - // It's the ONLY command that does not use device.set to get a result. - // You have to use device.get and send the get method an exact JSON string of { schema: true } - // This schema command does NOT - // change the state of the device, all it does is query the device - // as a confirmation that all communications are working properly. - // Otherwise you have to physically change the state of the device just to - // find out if you can talk to it. If this command returns no errors than - // we know we are have an established communication channel. This is a native TuyAPI call that - // the TuyAPI interface defines (its only available via the GET command. - // this call returns a object of results - device.schema(command).then((data) => { - }); - debug("get (schema) device status completed"); - } else { - device.set(command).then((data) => { - debug("set device status completed", data); - }); - } - break; - case "color": - var color = message.toLowerCase(); - debugColor("set color: ", color); - device.setColor(color).then((data) => { - debug("set device color completed", data); - }); - break; - } - - }).catch((err) => { - debugError(err); - }); - } catch (e) { - debugError(e); - } -}); - /** * Publish current TuyaDevice state to MQTT-Topic * @param {TuyaDevice} device @@ -334,7 +269,7 @@ function sleep(sec) { return new Promise(res => setTimeout(res, sec*1000)); } -// Main code loop +// Main code function const main = async() => { try { @@ -401,22 +336,28 @@ const main = async() => { switch (action) { case "command": var command = getCommandFromTopic(topic, message); - debug("receive command", command); + debug("Received command: ", command); if (command == "toggle") { device.switch(command).then((data) => { - debug("set device status completed", data); + debug("Set device status completed: ", data); }); - } else { + } + if (command.schema === true) { + // Trigger device schema update to update state + device.schema(command).then((data) => { + }); + debug("Get schema status command complete"); + } else { device.set(command).then((data) => { - debug("set device status completed", data); + debug("Set device status completed: ", data); }); } break; case "color": var color = message.toLowerCase(); - debugColor("set color: ", color); + debugColor("Set color: ", color); device.setColor(color).then((data) => { - debug("set device color completed", data); + debug("Set device color completed: ", data); }); break; } @@ -428,16 +369,7 @@ const main = async() => { debugError(e); } }); - } // Call the main code -main() - -/** - * Function call on script exit - */ -function onExit() { - TuyaDevice.disconnectAll(); - if (tester) tester.destroy(); -}; \ No newline at end of file +main() \ No newline at end of file From 82faeff78e69251953de7b567252e4d3803b29b3 Mon Sep 17 00:00:00 2001 From: tsightler Date: Thu, 17 Sep 2020 21:40:28 -0400 Subject: [PATCH 15/20] Update README.md --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 30055ad..a96028a 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,11 @@ MQTT interface for Tuya home automation devices sold under various names. This is a wrapper script for the Project codetheweb/tuyapi. https://github.com/codetheweb/tuyapi -This project provides an MQTT gateway for locally controlling home automation devices made by Tuya Inc. To use this script you will need to obtain the device ID and local keys for each of your devices after they are configured via the Tuya/Smart Life or other Tuya compatible app (there are many). With this information it is possible to communicate locally with Tuya devices using protocol 3.1 and 3.3, without using the Tuya Cloud service, however, getting the keys requires signing up for a Tuya IOT developer account or using one of several other alternative methods (such as dumping the memory of a Tuya based app running on Andriod). Acquiring keys is not part of this project, please see the instructions at the TuyAPI project (on which this script is based) available at https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md. +This project provides an MQTT gateway for locally controlling home automation devices made by Tuya Inc. To use this script you will need to obtain the device ID and local keys for each of your devices after they are configured via the Tuya/Smart Life or other Tuya compatible app (there are many). With this information it is possible to communicate locally with Tuya devices using protocol 3.1 and 3.3, without using the Tuya Cloud service, however, getting the keys requires signing up for a Tuya IOT developer account or using one of several other alternative methods (such as dumping the memory of a Tuya based app running on Andriod). Acquiring keys is not part of this project, please see the instructions at the TuyAPI project (on which this script is based) available at the TuyAPI project site: -## Instructions: +https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md. +## Instructions: Download this project to your system into any directory (example below uses /opt/tuya-mqtt) and install tuyapi from the same folder that the tuya-mqtt.js is in ``` // switch to opt directory @@ -21,11 +22,8 @@ cd tuya-mqtt npm install ``` -This found here: https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md - ## Basic Usage - ### Create your configuration file: ``` cp config.json.sample config.json From 868b0977b01165c90798ffb688346530ff8604ba Mon Sep 17 00:00:00 2001 From: tsightler Date: Thu, 17 Sep 2020 21:47:21 -0400 Subject: [PATCH 16/20] Update README.md --- README.md | 37 ++++++++----------------------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index a96028a..6708093 100644 --- a/README.md +++ b/README.md @@ -63,34 +63,11 @@ If discovery will not work for your case you can still use the IP address, but, tuya/ver3.3//////command ``` -Change device state (by topic): - tuya////command/ - - Example: - - tuya////command/on - - tuya////command/off - - tuya////command/ON - - tuya////command/OFF - - tuya////command/1 - - tuya////command/0 - - tuya////command/toggle - - tuya////command/TOGGLE - - tuya////command/{ "dps": 1, "set": true } - - tuya////command/{ "dps": 7, "set": true } - - tuya////command/{ "multiple": true, "data": { "1": true, "7": true } } - - tuya////command/{ "schema": true } - - tuya////command/{ "multiple": true, "data": { "1": true, "2": "scene_4" } } - - tuya////command/{ "multiple": true, "data": - { "1": true, "2": "scene", "6": "c479000025ffc3" } } - -Change device state (by payload) -Use with OpenHAB 2.X MQTT bindings or others where only a single command topic is preferred: -NOTE: notice that nothing follows the word command, DO NOT but a "/" in after command. - +Command topic to change device state tuya////command - Example: - "ON" + Example MQTT message payload for basic command (assumes DPS 1 is "on/off" control): + "ON" "OFF" "on" "off" @@ -98,6 +75,8 @@ NOTE: notice that nothing follows the word command, DO NOT but a "/" in after co "0" "toggle" "TOGGLE" + + Example MQTT message payload for advanced commands (set any DPS value): "{ \"dps\": 1, \"set\": true }" "{ \"dps\": 7, \"set\": true }" "{ \"multiple\": true, \"data\": { \"1\": true, \"7\": true } }" @@ -105,15 +84,15 @@ NOTE: notice that nothing follows the word command, DO NOT but a "/" in after co "{ \"multiple\": true, \"data\": { \"1\": true, \"2\": \"scene_4\" } }" "{ \"multiple\": true, \"data\": { \"1\": true, \"2\": \"scene\", \"6\": \"c479000025ffc3\" } }" -Change color of lightbulb (payload as HSB-Color) +Command topic for color change of lightbulb tuya////color - Example: + Example MQTT message payload: 64,0,100 0,0,89 ``` -### MQTT Topic's (read data) +### MQTT State Topic's (get device data) ``` Current device state (allways DPS[1]-Value): tuya////state From d05ac857d8fd5522ed4922247f252bd759781101 Mon Sep 17 00:00:00 2001 From: tsightler Date: Thu, 17 Sep 2020 21:50:10 -0400 Subject: [PATCH 17/20] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6708093..abba412 100644 --- a/README.md +++ b/README.md @@ -52,18 +52,18 @@ Set DEBUG=-* & node c:/openhab2/userdata/etc/scripts/tuya-mqtt.js ``` ### MQTT Topic's (send data) -**-----IMPORTANT NOTE-----** -**It's possible to replace the device IP address \ with the word "discover" to have the API attempt to automatically discover the device IP address. This capability allows support for 3.3 protocol devices without additional configuraiton but does require the system running this script to be on the same IP subnet as the Tuya device because discover relies on UDP broadcast from the devices.** +**It's possible to replace the device IP address \ with the word "discover" to have the API attempt to automatically discover the device IP address. This allows support for 3.3 protocol devices transparently, without additional configuraiton, but does require the system running this script to be on the same IP subnet as the Tuya device since the discovery protocol relies on UDP broadcast packets from the devices.** ``` tuya///discover/state tuya///discover/command ``` -If discovery will not work for your case you can still use the IP address, but, to use protocol 3.3 you must specify it in the topic explicitly +**If discovery will not work for your case you can still use the IP address, but, to use protocol 3.3 you must specify it in the topic explicitly** ``` tuya/ver3.3//////command ``` -Command topic to change device state +Example command topic to set the device state: +``` tuya////command Example MQTT message payload for basic command (assumes DPS 1 is "on/off" control): From df08c6fc8ae07800938dc33277db74d81c561688 Mon Sep 17 00:00:00 2001 From: tsightler Date: Thu, 17 Sep 2020 21:52:15 -0400 Subject: [PATCH 18/20] Update README.md --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index abba412..0f330f5 100644 --- a/README.md +++ b/README.md @@ -62,12 +62,13 @@ Set DEBUG=-* & node c:/openhab2/userdata/etc/scripts/tuya-mqtt.js tuya/ver3.3//////command ``` -Example command topic to set the device state: +### Example command topic to set the device state: ``` tuya////command - - Example MQTT message payload for basic command (assumes DPS 1 is "on/off" control): - "ON" +``` +### Example MQTT message payload for basic command (assumes DPS 1 is "on/off" control): +``` + "ON" "OFF" "on" "off" @@ -75,15 +76,16 @@ Example command topic to set the device state: "0" "toggle" "TOGGLE" - - Example MQTT message payload for advanced commands (set any DPS value): +``` +### Example MQTT message payload for advanced commands (set any DPS value): +``` "{ \"dps\": 1, \"set\": true }" "{ \"dps\": 7, \"set\": true }" "{ \"multiple\": true, \"data\": { \"1\": true, \"7\": true } }" "{ \"schema\": true }" "{ \"multiple\": true, \"data\": { \"1\": true, \"2\": \"scene_4\" } }" "{ \"multiple\": true, \"data\": { \"1\": true, \"2\": \"scene\", \"6\": \"c479000025ffc3\" } }" - +``` Command topic for color change of lightbulb tuya////color From 9944f326c5f2397a67c33d415454620b19c13b8a Mon Sep 17 00:00:00 2001 From: tsightler Date: Thu, 17 Sep 2020 21:57:38 -0400 Subject: [PATCH 19/20] Update README.md --- README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 0f330f5..c5aecbb 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Set DEBUG=-* & node c:/openhab2/userdata/etc/scripts/tuya-mqtt.js ``` tuya////command ``` -### Example MQTT message payload for basic command (assumes DPS 1 is "on/off" control): +### Example MQTT message payload for basic commands (default controls DPS[1] value, assumes true/false state control): ``` "ON" "OFF" @@ -86,7 +86,8 @@ Set DEBUG=-* & node c:/openhab2/userdata/etc/scripts/tuya-mqtt.js "{ \"multiple\": true, \"data\": { \"1\": true, \"2\": \"scene_4\" } }" "{ \"multiple\": true, \"data\": { \"1\": true, \"2\": \"scene\", \"6\": \"c479000025ffc3\" } }" ``` -Command topic for color change of lightbulb +### Example command topic for color change of lightbulb +``` tuya////color Example MQTT message payload: @@ -94,23 +95,25 @@ Command topic for color change of lightbulb 0,0,89 ``` -### MQTT State Topic's (get device data) -``` -Current device state (allways DPS[1]-Value): +### Example state topics (get device data) +### Get current device state (always DPS[1] value): tuya////state -Device DPS-Values: - // returns JSON.stringify(dps) values, use with care, does not always contain all dps values +### Get all available device DPS values +Returns JSON.stringify(dps) values, use with care, does not always contain all dps values +``` tuya////dps +``` - // return single dps data value +### Get any single DPS data value +``` tuya////dps/ ``` ## Issues Not all Tuya protocols are supported. For example, some devices use protocol 3.2 which currently remains unsupported by the TuyAPI project due to lack of enough information to reverse engineer the protcol. If you are unable to control your devices with tuya-mqtt please verify that you can query and control them with tuya-cli first. If tuya-cli works, then this script should also work, if it doesn't then this script will not work either. -## Integration with other tools +## Integration with other Home Automation tools openHAB examples are (here)[docs/openHAB.md]. ## Contributors From 989834821ae3ca92550df40a1bdb519d0931dc18 Mon Sep 17 00:00:00 2001 From: tsightler Date: Thu, 17 Sep 2020 21:58:17 -0400 Subject: [PATCH 20/20] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c5aecbb..8f17e40 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ Returns JSON.stringify(dps) values, use with care, does not always contain all d Not all Tuya protocols are supported. For example, some devices use protocol 3.2 which currently remains unsupported by the TuyAPI project due to lack of enough information to reverse engineer the protcol. If you are unable to control your devices with tuya-mqtt please verify that you can query and control them with tuya-cli first. If tuya-cli works, then this script should also work, if it doesn't then this script will not work either. ## Integration with other Home Automation tools -openHAB examples are (here)[docs/openHAB.md]. +openHAB examples are [here](docs/openHAB.md). ## Contributors - [TheAgentK](https://github.com/TheAgentK)