I see you haven’t replied here in a while, but looks like you are still doing updates to the code on your GitHub. Hope all is well with you!
I have been researching current state of Tuya stuff on OpenHAB, as I have a few plugs I would like to integrate. Since you seem to have been around it for quite a while (and have what seems to be one of the few remaining working implementations) I wanted to get your thoughts. So far I think I found:
This project, which is a node.js wrapper around tuyapi, and can be used directly from OpenHAB (via command line exec binding)? That last bit maybe I’m not sure on.
tuyapi/cli which you suggest as a possible alternative. Haven’t looked into that in detail yet.
The Tuya MQTT binding by AgentK.
Some other implementations that have outright been cancelled by developer (Wim Visser’s) or seem to me much less actively maintained (tuyasvr). I’ve already discounted these.
So I guess it’s whether I want to do command line exec stuff or MQTT sounds like are the two main options? The AgentK MQTT option seems very popular but I am failing to see why MQTT needs to be inserted in between OpenHAB and Tuya when the tuyapi is already web based?
Any other thoughts you might have would certainly be appreciated, and likely save me quite some time experimenting…
What implementation you go for kind of depends on your setup. If you already have MQTT set up for other devices then it makes sense to just use that. MQTT is a nice way to control devices once you have it set up. Last time I looked at the AgentK MQTT code was years ago and some things were hard coded which meant if you had strange devices or setups it wouldn’t work. I did do a pull request to fix the issue but I don’t know the current state of things.
njstuya has two modes. The local mode uses tuyapi which provides very raw access to the tuya devices and my njstuya script lets you use do pretty much anything using the command line (which you can access using the exec binding).
But I couldn’t manage to get the key from the last tuya device I brought and am controlling that using using the cloud mode. In cloud mode njstuya uses my cloudtuya script which just requires the app username/pass and some things like region, etc. If you have already added the devices to the smart life app, then you can get njstuya in cloud mode installed and running on the command line with two commands if you want to quickly test it out. (assuming you have npm installed)
And yes, I am aware of the two modes of njstuya. Part of what attracted me in fact! However if I cannot keep it all completely local, then I am not going to use the devices at all, period, end of story!
Story Time
I actually bought them quite a while ago with intention of flashing Tasmota, ESPEasy, or whatever but turns out I got some that are not ESP8266 based. At first I gave up and left them in a box for some months, and went on to other things. But the other day I decided to have another look and I actually tore one down, confirming it was in fact TW-02 (WinnerMicro W600) based, which it is apparently possible to flash MicroPython on, but unfortunately I managed to desolder the pads on the main board in the process. It’s alright though, I was willing to make a sacrifice on the altar of science.
So, now my thought is to use tuyapi with some wrapper (either yours or MQTT one I guess) and then just make some router settings to contain them on a different subnet or otherwise isolate them with no Internet access. So I been looking into that lately.
Are you aware that currently, the primary recommended way (from tuyapi project) to acquire the credentials is actually simply to create a developer account on iot.tuya.com and then use tuyapi-cli to dump the needed credentials, including the all important device key? My understanding is this is due to more encryption lately which is making the traditional MiTM way pretty much impossible. Perhaps this is the problem you ran into with your newer device, and if so, maybe doing it this way might work for you?
If my understanding is correct, I think I can do it this way, and then not need any account other than the developer account, and then that even only for initial signup. I made a post over at tuyapi to try and confirm this approach, but no answer as of yet.
Follow on question (over there) I suppose would be how difficult is it to obtain said developer account? Maybe instead of waiting, I should just try…
Just a little fyi … I wrote the tuyasvr thing quite a while back - I’m using it every day - more so during the little heatwave we’re having in the UK right now - “Alexa - turn on the fan in the office”
However I have been slowly moving all of my custom bits over to containerised MQTT solutions … and tuyasvr is last on my list - I plan to redo it as an MQTT client in the next week or so.
It works pretty reliably as-is. In all honesty, my write-up of how to perform the MITM stuff to get the keys is significantly longer than the code itself!
I’ve been putting more stuff on github recently (I did an MQTT client for APC UPS the other week) - so I will push things and drop a note here when done if you’re interested.
If you update a new version and re-publish tuyasvr, I would think that should deserve it’s own thread! Feel free to ping me or drop a note with a link in here though.
Just a couple questions, if you don’t mind:
I know you said reliable, but really how reliable has your solution been for you? (I hear different stuff all over the map about this in general, especially with Tuya stuff, so don’t afraid to be honest )
Why implement your own instead of contributing to (or otherwise combining forces with) or even simply just using AgentK MQTT based solution?
In a similar vein to #2 but in different area, were you aware of Network UPS Tools (NUT) project? I just took a quick look, and maybe they don’t support MQTT which is why you decide to write your own?
Or maybe like me some times you just prefer to go your own way.
Well - it just works, so I think it’s pretty reliable … in all honesty, I didn’t stick the code on gh as a “solution”, but more of an example of what can work - and that code really was just designed for me to control my tuya sockets.
There were a few solutions out there, but nothing quite right for me at the time (I dont remember why) - and it wasn’t that tricky to just write a small node app that did exactly what I wanted. … As far as AgentK … it sounds dead to me - and im a control freak - so I’ll be doing my own thing - exactly the way I want it to work. The difference being that this time, I’m a little more experienced with making solutions “configurable”, so my mqtt solution (tentatively named tuya-mqtt) should be easy enough for others to use as well.
… I looked at the NUT binding, but I’ve got 2 APC UPSs, and thought I could do a “cleaner” MQTT solution - i.e. having less moving parts to worry about - frankly - If I could convert my whole life to an MQTT-based pub/sub model I would be very happy
… I’m not going to try building things tonight, but it’s definitely on my to-do list for this weekend, so look out
No rush, man. Whenever you feel like working on it!
You didn’t mention it, but maybe helpful to you, instead of writing in detail how to do MitM, did you see where I noted that nowadays even tuyapiofficial docs now simply recommend to create a developer account at iot.tuya.com? What do you think about that way of doing it?
This is all new to me, so I had actually lodged an issue over at tuyapi asking further detail on that very point, but so far no responses.
Glad your not in a rush - I’m drinking wine and playing with a docker snapshot of OH v.3 right now - the new UI is super-slick
… I’ve just checked - I’ve still got a tuya dev account - I can’t remember why, but that approach wasn’t for me - read: I found the PITA MITM approach easier … but that was a while ago…
So next step for me will be working on that, then maybe try your (and/or, other) bindings/methods, assuming I can get the devices registered under the new developer account…
additional this is the docker log - mayber this helps also
(node:1) 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(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 30)
(node:1) UnhandledPromiseRejectionWarning: TypeError: Cannot read property '1' of undefined
at TuyaDevice.<anonymous> (/app/index.js:48:25)
at TuyaDevice.emit (events.js:315:20)
at TuyaDevice._packetHandler (/app/node_modules/tuyapi/index.js:443:10)
at /app/node_modules/tuyapi/index.js:342:43
at Array.forEach (<anonymous>)
at Socket.<anonymous> (/app/node_modules/tuyapi/index.js:338:19)
at Socket.emit (events.js:315:20)
at addChunk (_stream_readable.js:297:12)
at readableAddChunk (_stream_readable.js:273:9)
at Socket.Readable.push (_stream_readable.js:214:10)
(node:1) 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(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 31)
Hello,
Sorry you’re having problems - and thanks for your debug info.
I suspect that your tuya device is responding differently to mine.
The error message that you posted does suggest that there is hope however.
The interesting line of the error is:
at TuyaDevice.<anonymous> (/app/index.js:48:25)
… if you look at this line here you can see that we have recieved a message back from your tuya device, but it’s not formed the way we expect.
… can I suggest that you rebuild your docker image, but un-comment line 47 and run things again - this won’t fix anything, but should provide a little more debugging information that might help us to track down that pesky bug
(node:1) 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(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 5)
(node:1) UnhandledPromiseRejectionWarning: Error: No connection has been made to the device.
at TuyaDevice._send (/app/node_modules/tuyapi/index.js:234:13)
at /app/node_modules/tuyapi/index.js:214:14
at new Promise (<anonymous>)
at TuyaDevice.set (/app/node_modules/tuyapi/index.js:211:12)
at MqttClient.<anonymous> (/app/index.js:67:24)
at MqttClient.emit (events.js:315:20)
at MqttClient._handlePublish (/app/node_modules/mqtt/lib/client.js:1271:12)
at MqttClient._handlePacket (/app/node_modules/mqtt/lib/client.js:410:12)
at work (/app/node_modules/mqtt/lib/client.js:321:12)
at Writable.writable._write (/app/node_modules/mqtt/lib/client.js:335:5)
(node:1) 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(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 6)
(node:1) 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(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see Command-line API | Node.js v25.2.1 Documentation). (rejection id: 7)
(node:1) UnhandledPromiseRejectionWarning: TypeError: Cannot read property ‘1’ of undefined
at TuyaDevice.<anonymous> (/app/index.js:48:25)
at TuyaDevice.emit (events.js:315:20)
at TuyaDevice._packetHandler (/app/node_modules/tuyapi/index.js:443:10)
at /app/node_modules/tuyapi/index.js:342:43
at Array.forEach (<anonymous>)
at Socket.<anonymous> (/app/node_modules/tuyapi/index.js:338:19)
at Socket.emit (events.js:315:20)
at addChunk (_stream_readable.js:297:12)
at readableAddChunk (_stream_readable.js:273:9)
at Socket.Readable.push (_stream_readable.js:214:10)
And if i switch with hardware switch i got this:
(node:1) 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(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see Command-line API | Node.js v25.2.1 Documentation). (rejection id: 22)
(node:1) UnhandledPromiseRejectionWarning: TypeError: Cannot read property ‘1’ of undefined
at TuyaDevice.<anonymous> (/app/index.js:48:25)
at TuyaDevice.emit (events.js:315:20)
at TuyaDevice._packetHandler (/app/node_modules/tuyapi/index.js:443:10)
at /app/node_modules/tuyapi/index.js:342:43
at Array.forEach (<anonymous>)
at Socket.<anonymous> (/app/node_modules/tuyapi/index.js:338:19)
at Socket.emit (events.js:315:20)
at addChunk (_stream_readable.js:297:12)
at readableAddChunk (_stream_readable.js:273:9)
at Socket.Readable.push (_stream_readable.js:214:10)
(node:1) 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(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see Command-line API | Node.js v25.2.1 Documentation). (rejection id: 23)
… This suggests 2 reasons for the “garbled” responses from your device:
1/ Your device key might be incorrect, or
2/ You need to specify the “version” of the API to use
… so you should check to make sure that you’ve set up the device id + keys correctly …
… and you can also try setting the “version” information when the device is intialised. To do that, you need to modify the index.js file again, updating lines 14-19 to include the version specification, like this:
var device = new TuyaDevice({
ip: element.deviceIp,
id: element.deviceId,
key: element.deviceKey,
version: 3.3,
persistentConnection: true
});