Tuya Switch Control (Jinvoo, Smart Life)

If you have a link I can stick it in the OP.

Done. You can now just setup the devices normally using your Tuya/Smart life app and stick those details into key.json and use that control the devices without needing to MITM the local key for each device.

Hi I’m having some difficulty making rules for this and getting it to work. I have a power strip (with four dps)… and I can run this code in ssh: /usr/bin/node /etc/openhab2/scripts/njstuya.js -ip 192.168.2.243 -id xxxxxxx -key xxxx -set "{ \"dps\": 2, \"set\": true }" with the x’s as my id and key. And it works. However when I try to run the executeCommandLine I can’t seem to get it to work. All examples I’ve found use just “ON” or “OFF” which doesn’t work for my strip. Any advice or suggestions? Thanks!

I know the formatting of the set/get value is quite temperamental. Also if you are using windows I think the execcommand needs some special formatting for spaces. I actually changed the docs and formatting of the set/get commands so it would work on windows through the command line, but it seems like that might be the cause of the issue. I only tested the new formatting using the terminal/console on windows and linux and like you found it works fine. If I send the command '{"dps":2, "set":true}' to LivingRoomHeaterCommand it works. So try that format out. Since you are on linux that should work fine.

/usr/bin/node /etc/openhab2/scripts/njstuya.js -ip 192.168.2.243 -id xxxxxxx -key xxxx -set '{"dps":2, "set":true}'

Ah I see. No "\" in the command and difference spacing.

I’m trying to implement this simple code:
rule “TEST3 rule”
when
Item TEST3 changed
then

executeCommandLine("/usr/bin/node /etc/openhab2/scripts/njstuya.js -ip 192.168.2.243 -id xxxxxxx -key xxxx -set '{"dps":2, "set":true}'")
end

But when I toggle “TEST3” switch, nothing seems to be happening. Running on a Raspberry Pi too. Any idea on the spacing for this command? I’ll try out the living room rules, though it seems a bit overkill for the simple on/off I’m going for. Thanks so much for the help thus far!

If you are trying to add the command directly to the command string then it gets a bit more complicated since you need to scape everything since there is an outer set of quotes that need escaping. The spaces don’t matter. I don’t have any devices that require dps settings and just added support at the request of others. So I only really tested it as a command line tool and haven’t ever tested against a rule before. The rules are a bit a of a mess but I have was working on getting cloud support and considering just making a proper binding. The previous control script was very unreliable so lots of the crap in the rules was aimed at that, but I should update them with nice clean rules.

I would recommend that you pass the argument to the rule, it helps having to escape everything. So if you pass -set '{"dps":1, "set":true}' to item testTuya it should work fine.

rule "Test dps"
when
    Item tesTuya received command
then
    var setCommand="node /etc/openhab2/scripts/node_modules/njstuya -ip 10.xx -id xx -key xx " + receivedCommand.toString
    var resp = executeCommandLine(setCommand, 5000)
    if(db) { logInfo("Tuya", "New broadcast {} resp {}", setCommand, resp)  }
end

I would also suggest you pass the item ip, key, id as well or have it as some kind of variable than fixed into separate rules.

But if you want to test it directly or hardcoded then if you escape everything any of the following should work.
As a variable

    var setCommand = "'{\"dps\":1,\"set\":true}'"
    var resp = executeCommandLine("node /etc/openhab2/scripts/node_modules/njstuya -ip 192.168.xx -id xx -key xx -set " + setCommand, 5000)

Or hardcoded

    executeCommandLine("node /etc/openhab2/scripts/node_modules/njstuya -ip 192.168.xx -id xx -key xx -set " + '\'{"dps": 1, "set":true}\'', 5000)
    executeCommandLine("node /etc/openhab2/scripts/node_modules/njstuya -ip 192.168.xx -id xx -key xx -set '{\"dps\": 1, \"set\":true}'", 5000)

Edit: I’ve redone the items and rules.
Items

//Tuya items and thermo
//  Complete ip/id/key

// Tuya Group Devices 
Group gTuyaActions "Tuya Action to run"
Group:Switch gTuyaStates "Tuya devices"
// Tuya devices
Switch LivingRoomHeater	"Heater state"  (gTuyaStates, gSwitch)  [ "Switchable" ]
Switch BedroomHeater	"Heater state"  (gTuyaStates, gSwitch)  [ "Switchable" ]
Number KitchenThermostatCurrentTemp "Kitchen Thermostat Current Temperature" (gKitchenThermostat)  //[ "CurrentTemperature" ]
Number KitchenThermostatTargetTemp "Kitchen Thermostat Target Temperature" (gKitchenThermostat) {autoupdate="true"}//[ "TargetTemperature" ]
// Send current time to refresh to be able to query when previous refresh was
String TuyaRefresh "Refresh"

// Tuya Devices with sensistive data edit before sharing. 
String LivingRoomHeaterCommand "-ip 10.x.x.x -id xx -key xx" (gTuyaActions)
String BedroomHeaterCommand "-ip 10.x.x.x -id xx -key xx" (gTuyaActions)

Rules

var _DEBUG = false
var db = false
var Timer statusTimer = null
var Timer errorTimer = null

rule "Execute Tuya Action"
when
    Member of gTuyaActions received command
then
    if(db) logInfo("TUYA", "Group trigger {} command", triggeringItem, receivedCommand)

    var setCommand="node /etc/openhab2/scripts/node_modules/njstuya " + triggeringItem.label + " " + receivedCommand.toString
    var tuyaDevice = triggeringItem.name.replace('Command','')
    
    // Runs command vis njstuya
    var resp = executeCommandLine(setCommand, 5000)
    
    if(resp === null)   resp = "Error null"
    if(resp.contains("Error") || resp.contains("Warning") || resp.length < 2){
        logInfo("Tuya", "Error in response [{}] ", resp)
        postUpdate(tuyaDevice, "UNDEF")
        
        // refresh on Error only once after good state. 
        if (errorTimer === null) {
            errorTimer = createTimer(now.plusMillis(1500),  [ | 
                triggeringItem.sendCommand("STATUS")
                errorTimer.cancel()
                errorTimer = null
            ])
        }
    }
    else {
        if(db) logInfo("Tuya", "Updating state [{}] ", resp)   
        postUpdate(tuyaDevice, resp)
        // Reset errorTimer to null if state is good
        if (errorTimer !== null) {
            errorTimer.cancel()
            errorTimer = null
        } 
    }
    if(db) { logInfo("Tuya", "New broadcast {} resp {}", setCommand, resp)  }
end

rule "Tuya Refresh"
when
    Item TuyaRefresh received command
    or
    Time cron "13 0/2 * * * ?"
then
    gTuyaActions.allMembers.forEach[ device | sendCommand(device, "status")]
end


rule "Update Thermostat Value"
when
    Item HueKitchenTemp changed
then
    if(db) { logInfo("Thermostat", "update kitchen temp {}", HueKitchenTemp)}
    KitchenThermostatCurrentTemp.postUpdate(Float::parseFloat(String::format("%s",HueKitchenTemp.state).replace(' ','')))
end

rule "Control Temp"
when
    Item KitchenThermostatCurrentTemp changed
    or 
    Item KitchenThermostatTargetTemp changed
then
    logInfo("Thermostat", "Set {} change temperature from {} to {}", LivingRoomHeater ,KitchenThermostatCurrentTemp.state, KitchenThermostatTargetTemp.state)
    var isCold =  (KitchenThermostatCurrentTemp.state as Number) < (KitchenThermostatTargetTemp.state as Number)
    var setCommand = if(isCold) 'ON' else 'OFF'


    //When using slider you can get lots of changes, this should prevent lots of commands in a row
    if(!LivingRoomHeater.state.toString.contains(setCommand) && statusTimer === null) {
        if(db) { logInfo("Thermostat", " logic {} state {} cmd{}", LivingRoomHeater.state.toString.contains(setCommand), LivingRoomHeater.state.toString, setCommand)}
        if(db) { logInfo("Thermostat", " is cold {} set command {}", isCold, setCommand)}
        LivingRoomHeaterCommand.sendCommand(setCommand)


        // Update state
        if (statusTimer !== null) {
            statusTimer.cancel()
            statusTimer = null
        } 

        statusTimer = createTimer(now.plusMillis(1500),  [ | 
            LivingRoomHeaterCommand.sendCommand("STATUS")
            statusTimer.cancel()
            statusTimer = null
        ])
    } else if (statusTimer !== null) {
        statusTimer.cancel()
        statusTimer = null
    } 

end

Hi @unparagoned
I was able using your script to control my smart plug which has 2 outlets (dps1 and dps2). Thank you!

However, without switching (just idling), approximately every minute I observed the following ERROR messages in the log:

2019-03-29 22:04:42.924 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: ‘(node:20295) UnhandledPromiseRejectionWarning: SyntaxError: Unexpected end of JSON input’

2019-03-29 22:04:42.942 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: ’ at JSON.parse ()’

2019-03-29 22:04:42.946 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: ’ at TuyaDevice.runCommand (/etc/openhab2/scripts/node_modules/njstuya/njstuya.js:231:54)’

2019-03-29 22:04:42.949 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: ’ at emitTwo (events.js:131:20)’

2019-03-29 22:04:42.953 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: ’ at TuyaDevice.emit (events.js:214:7)’

2019-03-29 22:04:42.956 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: ’ at Socket.client.on.data (/etc/openhab2/scripts/node_modules/tuyapi/index.js:425:16)’

2019-03-29 22:04:42.961 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: ’ at emitOne (events.js:116:13)’

2019-03-29 22:04:42.965 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: ’ at Socket.emit (events.js:211:7)’

2019-03-29 22:04:42.969 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: ’ at addChunk (_stream_readable.js:263:12)’

2019-03-29 22:04:42.972 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: ’ at readableAddChunk (_stream_readable.js:250:11)’

2019-03-29 22:04:42.976 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: ’ at Socket.Readable.push (_stream_readable.js:208:10)’

2019-03-29 22:04:42.979 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: ‘(node:20295) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)’

2019-03-29 22:04:42.983 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: ‘(node:20295) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.’

2019-03-29 22:04:42.986 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: ‘(node:20295) UnhandledPromiseRejectionWarning: undefined’

2019-03-29 22:04:42.991 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: ‘(node:20295) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)’

2019-03-29 22:04:43.002 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Transformed response is '(node:20295) UnhandledPromiseRejectionWarning: SyntaxError: Unexpected end of JSON input

at JSON.parse (<anonymous>)

at TuyaDevice.runCommand (/etc/openhab2/scripts/node_modules/njstuya/njstuya.js:231:54)

at emitTwo (events.js:131:20)

at TuyaDevice.emit (events.js:214:7)

at Socket.client.on.data (/etc/openhab2/scripts/node_modules/tuyapi/index.js:425:16)

at emitOne (events.js:116:13)

at Socket.emit (events.js:211:7)

at addChunk (_stream_readable.js:263:12)

at readableAddChunk (_stream_readable.js:250:11)

at Socket.Readable.push (_stream_readable.js:208:10)

(node:20295) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)

(node:20295) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

(node:20295) UnhandledPromiseRejectionWarning: undefined

(node:20295) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)

(node:20295) UnhandledPromiseRejectionWarning: SyntaxError: Unexpected end of JSON input

at JSON.parse (<anonymous>)

at TuyaDevice.runCommand (/etc/openhab2/scripts/node_modules/njstuya/njstuya.js:231:54)

at emitTwo (events.js:131:20)

at TuyaDevice.emit (events.js:214:7)

at Socket.client.on.data (/etc/openhab2/scripts/node_modules/tuyapi/index.js:425:16)

at emitOne (events.js:116:13)

at Socket.emit (events.js:211:7)

at addChunk (_stream_readable.js:263:12)

at readableAddChunk (_stream_readable.js:250:11)

at Socket.Readable.push (_stream_readable.js:208:10)

(node:20295) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)

(node:20295) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

(node:20295) UnhandledPromiseRejectionWarning: undefined

(node:20295) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)’

Any idea?

However, without switching (just idling), approximately every minute I observed the following ERROR messages in the log:

I don’t understand how you can be getting an error if you are just idling. My script only starts up and runs when you run a command. If you are using some of my rules then there should be a cron job that runs every now and then to get the state or update the state.

It looks like you are running a -Set command but the arguments is not properly formatted. So have a look at any cron jobs you have running and see what commands they are running

Also try and enable warnings and errors in any rules so we can see what command is being sent., change the debug lines in the rules to true.

@unparagoned

I looked at my rules folder and renamed all recently changed files’ extension from xxx.rules to xxx.yyyrules and restarted the openhab2.service. That did not help.

I looked at /var/spool/cron/crontabs/ (I assume that this is where cron jobs are stored) but the folder is empty.

I also renamed your script folder from node_modules to _node_modules just to see what error messages I got and I saw these but I have no idea what they meant

2019-03-30 09:58:52.382 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: 'module.js:550'
2019-03-30 09:58:52.394 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: '    throw err;'
2019-03-30 09:58:52.397 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: '    ^'
2019-03-30 09:58:52.400 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: ''
2019-03-30 09:58:52.404 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: 'Error: Cannot find module '/etc/openhab2/scripts/node_modules/njstuya''
2019-03-30 09:58:52.406 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: '    at Function.Module._resolveFilename (module.js:548:15)'
2019-03-30 09:58:52.410 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: '    at Function.Module._load (module.js:475:25)'
2019-03-30 09:58:52.412 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: '    at Function.Module.runMain (module.js:694:10)'
2019-03-30 09:58:52.416 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: '    at startup (bootstrap_node.js:204:16)'
2019-03-30 09:58:52.418 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Exec [ERROR]: '    at bootstrap_node.js:625:3'
2019-03-30 09:58:52.426 [DEBUG] [hab.binding.exec.handler.ExecHandler] - Transformed response is 'module.js:550
    throw err;
    ^
Error: Cannot find module '/etc/openhab2/scripts/node_modules/njstuya'
    at Function.Module._resolveFilename (module.js:548:15)
    at Function.Module._load (module.js:475:25)
    at Function.Module.runMain (module.js:694:10)
    at startup (bootstrap_node.js:204:16)
    at bootstrap_node.js:625:3
module.js:550
    throw err;
    ^
Error: Cannot find module '/etc/openhab2/scripts/node_modules/njstuya'
    at Function.Module._resolveFilename (module.js:548:15)
    at Function.Module._load (module.js:475:25)
    at Function.Module.runMain (module.js:694:10)
    at startup (bootstrap_node.js:204:16)
    at bootstrap_node.js:625:3'

You must have a rule in /etc/openhab2/rules that runs the exec binding which is trying to call njstuya. If you just install njstuya by itself it doesn’t do anything, so you can’t be getting errors. You must have something that is trying to execute it. The errors above are just because you renamed node_modules to _node_modules. So whatever rule you have in openhab trying to run the node script fails, since it’s looking in the wrong place.

GadgetAngel
Your formatting may be off a bit.

Here is what I am talking about, I ran these two commands back to back. I do not understand why it states “undefined” and then I run it again and it does what it should had done the first time

PS C:\openhab2\userdata\etc\scripts> node  c:/openhab2/userdata/etc/scripts/node_modules/njstuya/njstuya.js -ip 192.168.0.30 -id 5846055184f3eb20bfaf -key afb9b013346054e6 -set '{ \"dps\": 2, \"set\": \"white\" }' DEBUG=*
(node:12400) UnhandledPromiseRejectionWarning: undefined
(node:12400) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:12400) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
PS C:\openhab2\userdata\etc\scripts> node  c:/openhab2/userdata/etc/scripts/node_modules/njstuya/njstuya.js -ip 192.168.0.30 -id asdasd -key asdasda -set '{ \"dps\": 2, \"set\": \"white\" }' DEBUG=*
OFF
PS C:\openhab2\userdata\etc\scripts>

I tried stepping thru your code but you are more familiar with the code then me, since their is alot of timing stuff going on. It take me to long to figure out what variables I need to look at.

But if you could figure out why the above happens, I would appreciate it.

I am leaning toward using your code but I have to know if it executed correctly. I hate the idea of having to send a command twice just to make sure the command goes thru to the bulb

BTW it reported that the bulb is OFF when it was ON

Try

c:/openhab2/userdata/etc/scripts/node_modules/njstuya/njstuya.js -ip 192.168.0.30 -id asdasd -key asdasda -set '{ "dps": 2, "set": white }'
or
c:/openhab2/userdata/etc/scripts/node_modules/njstuya/njstuya.js -ip 192.168.0.30 -id asdasd -key asdasda -set "{ \"dps\": 2, \"set\": white }"
or
c:/openhab2/userdata/etc/scripts/node_modules/njstuya/njstuya.js -ip 192.168.0.30 -id asdasd -key asdasda -set '{ \"dps\": 2, \"set\": white }'

Or something like that.

Also I’m not sure my script properly deals or even know if it’s possible to easily deal with devices that use the dps options. There simply are so many types and variations when devices use multiple dps, and can be color, a fan, etc. You need to query the dps yourself and then convert that to a state. My script probably just works for switches.

here is the output from the commands you suggested:

C:\Windows\system32>"c:\Program Files\nodejs\node" c:\openHAB2\userdata\etc\scripts\node_modules\njstuya\njstuya.js -ip 192.168.0.30 -id 5846055184f3eb20bfaf -key afb9b013346054e6 -set '{ "dps": 2, "set": white }'
(node:2972) UnhandledPromiseRejectionWarning: undefined
(node:2972) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:2972) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

C:\Windows\system32>
C:\Windows\system32>
C:\Windows\system32>"c:\Program Files\nodejs\node" c:\openHAB2\userdata\etc\scripts\node_modules\njstuya\njstuya.js -ip 192.168.0.30 -id 5846055184f3eb20bfaf -key afb9b013346054e6 -set "{ \"dps\": 2, \"set\": white }"
(node:1772) UnhandledPromiseRejectionWarning: SyntaxError: Unexpected token w in JSON at position 19
    at JSON.parse (<anonymous>)
    at TuyaDevice.runCommand (c:\openHAB2\userdata\etc\scripts\node_modules\njstuya\njstuya.js:231:54)
    at TuyaDevice.emit (events.js:194:15)
    at Socket.client.on.data (c:\openHAB2\userdata\etc\scripts\node_modules\tuyapi\index.js:425:16)
    at Socket.emit (events.js:189:13)
    at addChunk (_stream_readable.js:284:12)
    at readableAddChunk (_stream_readable.js:265:11)
    at Socket.Readable.push (_stream_readable.js:220:10)
    at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:94:17)
(node:1772) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:1772) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:1772) UnhandledPromiseRejectionWarning: undefined
(node:1772) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)

C:\Windows\system32>
C:\Windows\system32>
C:\Windows\system32>"c:\Program Files\nodejs\node" c:\openHAB2\userdata\etc\scripts\node_modules\njstuya\njstuya.js -ip 192.168.0.30 -id 5846055184f3eb20bfaf -key afb9b013346054e6 -set '{ \"dps\": 2, \"set\": white }'
(node:6284) UnhandledPromiseRejectionWarning: SyntaxError: Unexpected end of JSON input
    at JSON.parse (<anonymous>)
    at TuyaDevice.runCommand (c:\openHAB2\userdata\etc\scripts\node_modules\njstuya\njstuya.js:231:54)
    at TuyaDevice.emit (events.js:194:15)
    at Socket.client.on.data (c:\openHAB2\userdata\etc\scripts\node_modules\tuyapi\index.js:425:16)
    at Socket.emit (events.js:189:13)
    at addChunk (_stream_readable.js:284:12)
    at readableAddChunk (_stream_readable.js:265:11)
    at Socket.Readable.push (_stream_readable.js:220:10)
    at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:94:17)
(node:6284) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:6284) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:6284) UnhandledPromiseRejectionWarning: undefined
(node:6284) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)

C:\Windows\system32>

Here is What DOES WORK:

C:\Windows\system32>"c:\Program Files\nodejs\node" c:\openHAB2\userdata\etc\scripts\node_modules\njstuya\njstuya.js -ip 192.168.0.30 -id 5846055184f3eb20bfaf -key afb9b013346054e6 -set "{ \"dps\": 2, \"set\": \"white\" }"
OFF

C:\Windows\system32>

Because I have to specify the full path for node.exe and it resides in “Program Files” I have to use double quotes in the path. So I have no choice on windows, I MUST use double quotes. Since I am stuck with double quotes, I must use double quotes around the -set string. Mixing the quotes NEVER works for me because I have to use a pair for the path definition (needed for executeCommandLine call in rules file).

HERE is my trying to access node.exe two different ways on windows, you can see from the output that cmd.exe can not use single quotes in the path definition:

C:\Windows\system32>'c:\Program Files\nodejs\node'
The filename, directory name, or volume label syntax is incorrect.

C:\Windows\system32>"c:\Program Files\nodejs\node"
>
(To exit, press ^C again or type .exit)
>

C:\Windows\system32>

Hey, unparagoned:

I would like to add some code to your script. How do we go about doing that?

What I have added will not effect anyone else because my new command switch calls a new function.

I added a new command line switch to your njstuya.js file which allows the user to set multiple dps values in one command.

The new command switch -multi uses a function, I wrote (modeled after your setState function) to call tuyapi but allows the multi dps set to occur. The -multi command has the following format:

-multi { “dps index as number”: dpsvalue, “dps index as a number”: dpsvalue }

The first key is the dps index expressed as a character, then the colon, then the value (for that dps index) followed by the comma

so the -multi command line switch can be used to set one dps or multiple dps’ values

MULTIPLE DPS values set (dps 1,2,3,4,5,6)
-multi { "1": true, "2": "string", "3": 255, "4": 255, "5": "string", "6": "string" }

ONE DPS value set (dps 1)
-multi { "1": true }

I will get your project from github, but I am not familiar with github enough to know how to get the changes I added back to you. I use WebStorm for my projects and it does have an interface with github.

Here is an example of it running:

C:\Windows\system32>"c:\Program Files\nodejs\node" c:\Users\joann\WebstormProjects\njsTuya\njstuya.js -ip 192.168.0.30 -id 5846055184f3eb20bfaf -key afb9b013346054e6 -multi "{ \"1\": false }"
OFF

C:\Windows\system32>"c:\Program Files\nodejs\node" c:\Users\joann\WebstormProjects\njsTuya\njstuya.js -ip 192.168.0.30 -id 5846055184f3eb20bfaf -key afb9b013346054e6 -multi "{ \"1\": true, \"2\": \"scene_2\" }"
ON

C:\Windows\system32>"c:\Program Files\nodejs\node" c:\Users\joann\WebstormProjects\njsTuya\njstuya.js -ip 192.168.0.30 -id 5846055184f3eb20bfaf -key afb9b013346054e6 -multi "{ \"2\": \"white\", \"3\": 255, \"4\": 255 }"
OFF

C:\Windows\system32>"c:\Program Files\nodejs\node" c:\Users\joann\WebstormProjects\njsTuya\njstuya.js -ip 192.168.0.30 -id 5846055184f3eb20bfaf -key afb9b013346054e6 -multi "{ \"2\": \"colour\", \"5\": \"00ff040079ffff\" }"
OFF

C:\Windows\system32>"c:\Program Files\nodejs\node" c:\Users\joann\WebstormProjects\njsTuya\njstuya.js -ip 192.168.0.30 -id 5846055184f3eb20bfaf -key afb9b013346054e6 -multi "{ \"1\": false }"
OFF

C:\Windows\system32>"c:\Program Files\nodejs\node" c:\Users\joann\WebstormProjects\njsTuya\njstuya.js -ip 192.168.0.30 -id 5846055184f3eb20bfaf -key afb9b013346054e6 -multi "{ \"1\": true, \"2\": \"white\", \"3\": 138, \"4\": 255 }"
ON

C:\Windows\system32>"c:\Program Files\nodejs\node" c:\Users\joann\WebstormProjects\njsTuya\njstuya.js -ip 192.168.0.30 -id 5846055184f3eb20bfaf -key afb9b013346054e6 -multi "{ \"1\": true, \"2\": \"white\", \"3\": 27, \"4\": 255 }"
ON

Created a PULL request let me know if you have any questions.

let me know what you want to do.

Windows is just a complete nightmare when it comes to trying to format the JSON strings, single and double quotes. One will work on the command line then stop if you use that format in the exec command from a rule. Really I should just say no windows support, it’s silly and stupid. It’s probably cheaper in terms of my time to buy everyone who has a problem with windows a raspberrypi.

Also thanks for your pull request but can you get it to work as is by just passing passing the correctly formatted set command? Something like below but with the quotes fixed to work on windows?

-set '{ "multiple": true, "data": {  "2": scene, "6": bd76000168ffff } } }'

I already own (4) raspberrypi. I use them to create my own voice activated devices in my house. Right now I am working on a project to automate my adjustable bed so I can use Alexa as my bed remote. :smile:

WELL I FELL really STUPID::zipper_mouth_face:

:astonished: Your CODE already does the function of setting multiple dps values.:hugs::star_struck:

Please disregard my pull request. IT IS NOT NEEDED!

To get njstuya.js to do multiple dps sets in one command use the following format for -set

To change the light to on and set color to yellow:

-set '{ \"multiple\": true, \"data\": { \"1\": true, \"2\": \"colour\", \"5\": \"ffc400002effff\" } }'

or 

to set the plug on and the usb port on that plug on:

-set '{ \"multiple\": true, \"data\": { \"1\": true, \"7\": true } }'

So the string you suggested will not work, but it was close. I get the impression you do not own a tuya light bulb :

This format WILL WORK

-set '{ "multiple": true, "data": { "2": "scene", "6": "bd76000168ffff" } }'

So any user using this format: beware of which dps values take strings, booleans or numbers.

Here is an example that sets the brightness and color temp. of my tuya bulb

-set '{ "multiple": true, "data": { "1": true, "2": "white", "3": 183, "4": 210 } }'

I am sorry for distributing you and I am sorry for making the pull request.:no_mouth: I now need to see if I can do this with the MQTT version (tuya-mqtt.exe).

THANK YOU, for pointing out this NEW FEATURE! to your amazing code!!!:sunglasses:

The mqtt version should do what you want in an automated fashion. So you just tell it you want it to be white and it should sort out our the dps stuff in the background.

Hi @unparagoned,
I could not locate the issue. I reverted back to a backup OH image BEFORE my tuya adventure and started all over again. However, this time I encountered many errors during the installation and could not get your script to work at all. The first set of errors were these messages

[19:23:53] openhabian@openHABianPi:/etc/openhab2/scripts$ sudo npm install unparagoned/njsTuya
[sudo] password for openhabian:

npm ERR! code 128
npm ERR! Command failed: /usr/bin/git submodule update -q --init --recursive
npm ERR! fatal: Could not change back to '/root/.npm/_cacache/tmp/git-clone-c7f4ed9e': Permission denied
npm ERR!

npm ERR! A complete log of this run can be found in:
npm ERR!     /root/.npm/_logs/2019-04-03T23_24_18_072Z-debug.log

The install instruction did not call for root but somehow the error messages seemed to indicate so. I’m not a programmer and still learning. Most of these (e.g. node , npm,…) are still foreign to me.

What would be a proper response to such errors? Thanks.

That looks like a permissions error. In theory you shouldn’t need sudo but it is required on some systems due to to permission issues. So try without sudo.

npm install unparagoned/njsTuya

Otherwise try

sudo -i -u openhabian npm install unparagoned/njsTuya

Or if that doesn’t work you should just delete or fix the permissions of /root/.npm/_cacache. You probably have user permissions rather than root or vise versa.

sudo chown openhabian:root /root/.npm/_cacache
or 
sudo chown root:openhabian /root/.npm/_cacache

@unparagoned The problem is that @AgentK uses the dps topic to send back information on the state of the device. I tried using it but the code ends up in a loop, because it tries to send and receive on the same topic. In fact what happened was that I set the bulb to 1 and my command was just overwritten by the current state of the bulb which was OFF so the command sent to the device ended up being OFF.

I sent @AgentK a pull request. I got two different JSON formats working all under the command topic. I also got ON, OFF to work like it should.

The balls now in his court if he wants to incorporate my additions or not.

but I made sure the two JSON formats work like they do in your script:

To set one dps value

{ "dps": 1, "set": true }

To set multiple dps values

{ "multiple": true, "data": { "1": true, "7": true } }

I changed my update to go with more your suggestion of using the topic or switch that was already available in the code.

Another NOTE; I forgot to mention that @AgentK does a conversion of all the characters placed in the message area with the following code.

He first converts it to a string than to lowercase but then he does this:

return cmessge == 1  ? 'on' : "off";

So basically anything you put into the message field gets converted to “off”, EVEN the “on” string gets converted to “off”. The only value that gets converted to ‘on’ is a “1”!!!

If you place the “on” next to the command topic as follows:

command topic in current version of tuya-mqtt

tuya/<tuyaAPI-type>/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/command/on 

@AgentK will send the string on as straight string (not a JSON string). I know you added the dpsJ topic, but I saw no need to keep it around since I needed to fix the “ON”,“on”,“OFF”,“off” problem anyways.

So my update fixes more than just adding the JSON strings.

BTW @unparagoned, I am sure you are aware of the following command:

process.on('unhandledRejection', err3 => {
    debug("unhandledRejection listener caught the Error, the Message is: ", err3.message);
});

That line will suppress this output from going to the log and stderr but still let it show up in the DEBUG=* LOGS :

[19:24:13] openhabian@openHABianPi:/etc/openhab2/scripts$ node node_modules/njstuya
(node:4131) UnhandledPromiseRejectionWarning: undefined
(node:4131) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:4131) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Error: find() timed out. Is the device powered on and the ID or IP correct?
(node:4131) UnhandledPromiseRejectionWarning: Error: find() timed out. Is the device powered on and the ID or IP correct?
(node:4131) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)

I honestly, believe its due to network traffic on the Wifi that this shows up some times. Every time it happens I just rerun the command again and it goes through!!

When I get time, I plan to test this theory with the use of a spare Wifi router and extra tuya bulbs and tuya plugs with my windows openhab2 server. The tuya plug and bulb and my machine will be the only things on this Wifi network and I will see if the problem goes away.

@unparagoned, I followed your instructions and was able to install your script without the npm errors. However, there were npm warnings

[19:14:51] openhabian@openHABianPi:/etc/openhab2/scripts$ npm install unparagoned/njsTuya
npm WARN saveError ENOENT: no such file or directory, open '/etc/openhab2/scripts/package.json'
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN enoent ENOENT: no such file or directory, open '/etc/openhab2/scripts/package.json'
npm WARN scripts No description
npm WARN scripts No repository field.
npm WARN scripts No README data
npm WARN scripts No license field.

+ njstuya@2.2.3
added 62 packages from 76 contributors and audited 81 packages in 55.797s
found 0 vulnerabilities

Based on your comment here,
https://github.com/unparagoned/njsTuya/issues/3#issuecomment-395934533, those should be normal responses?

But when I tested my install,

node node_modules/njstuya

I got the following errors:

[19:24:13] openhabian@openHABianPi:/etc/openhab2/scripts$ node node_modules/njstuya
(node:4131) UnhandledPromiseRejectionWarning: undefined
(node:4131) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:4131) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Error: find() timed out. Is the device powered on and the ID or IP correct?
(node:4131) UnhandledPromiseRejectionWarning: Error: find() timed out. Is the device powered on and the ID or IP correct?
(node:4131) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)

So I am stuck again :disappointed_relieved:. Please give me another hint. Thank you.