tuyapi
tuyapi copied to clipboard
Controlling Tuya devices with cloud API instead of controlling them locally
hi there,
First thank you for your valuable contributions to the tuya library. I realize this is a long shot, but i am wondering if anyone had success in calling the tuya cloud API? i used fiddler and i was able to decipher a lot of the information on the calls, but there is one thing missing.
How is tuya calculating the MD5 hash? I was not able to replicate their "sign" parameter to the URL and the details on this as slim. Most of the info is located at https://docs.tuya.com/en/cloudapi/cloud_access.html#access-mode (search for accesskey) but i could not get it to work following the example. (I had to order my parameters, i used localKey as the accessKey, and then i did a utf-8 encoded MD5 hash). For the time, i got it in seconds using Unix epoch time.
Once i complete my work, i will share the PowerShell script that can be replicated into standard http/json requests.
You have to apply to https://developer.tuya.com/user/cloud to get an accesskey. i will update this thread once i have more details. but if anyone has additional ideas, please let me know
I believe the MD5 hash is the same as what's implemented in TuyAPI (see here), although I could be wrong.
In the cloud API sign
is generated by:
- Sorting all the parameters it cares about[1] into alphabetical order by their key names, ignoring null valued ones.
- Joining them in the form:
value1=test||value2=test||...||valuen=test
- Appending the appSecret to the string (so it becomes
value1=test||value2=test||...||appSecret
- MD5 this string.
There is a special case for the parameter postData
, which is done by:
- MD5'ing the JSON form data
- rearrange the ASCII encoded MD5 hash in this order:
bytes[8:16] + bytes[0:8] + bytes[24:32] + bytes[16:24]
.
Just before you do the final MD5 of the whole string, it should look like this:
a=<action>||...||postData=<hash>||...||appSecret
[1] ["a", "v", "lat", "lon", "lang", "deviceId", "imei", "imsi", "appVersion", "ttid", "isH5", "h5Token", "os", "clientId", "postData", "time", "n4h5", "sid", "sp"]
@bahorn thank you for the reply. i will try this as soon as i get the accesskey from tuya. This is funny though. their instructions at https://docs.tuya.com/en/cloudapi/cloud_access.html#access-mode are incorrect. they do tell you to sort, but they use only one | and the access key is in the front and not the back of the string.
Also one thing i was not aware of is that the postData is part of the MD5 hash to generate the sign. thank you
@bahorn May I ask where did you get this information for the cloud API sign
?
I just got my accessKey and keySecret from Tuya and I have tried both your method and the instructions on Tuya, that @michmike said. Using PostMan, I tried doing API requests and end up getting the following response after multiple tries:
{
"t": 1517217462399,
"success": false,
"errorCode": "SING_VALIDATE_FALED_4",
"status": "error",
"errorMsg": "Parameter or Data Error"
}
If I wish to send postData, for example to turn on my application, could you perhaps share a screenshot or collection in PostMan of what needs to be sent? I've spent hours on this to no avail.
@dominicklee I just recorded on my old phone (android old enough so i could have a root certificate for fiddler) so i got all the information about resquest&post-data + responses.
I have recorded the following scenarios with the official SmartLife app:
- Connecting with auto-connect (saved password in app-data) & going all the way to log-out
- Connecting with mobile-no + password & going all the way to log-out
- Connecting with mobile-no + sms-code & going all the way to log-out
- From main menu, removing a device
- From main menu, adding a device
I will need a little time to format everything (and scramble some personnal info before making it public) but i will make all the info available, maybe tonight (GMT -5) if I have time
The only thing i'm not able to record is the action on the device. If I click to turn it on or off, nothings happens in fiddler, so no HTTP request seams to be made to the server, it must be something else. I checked for UDP on the network and it's not that either. So that's the part I did not figure out !
@dominicklee I stripped down my personal Python implementation that should clear up the issues. https://gist.github.com/bahorn/9bebbbf37c2167f7057aea0244ff2d92
@Ericmas001 Devices are controlled by MQTT. Use Wireshark to log it, you can set a filter for just MQTT.
Just to explain more of how this works, when you attempt to login you are given:
- a token
- A RSA public key
- An exponent for this key.
You construct the RSA key given and use it to encrypt the MD5(all 128 bits encoded as hex) of your password. This is then padded and then sent on to the server (along with the token). If the login details are correct, you get returned a session ID for use in other mobile requests. Worth noting is the "ecode" and the "p10001" thing that are returned along with the session ID, which you need to login to the MQTT server.
After you login, you should make a call to "tuya.m.device.my.list" to get a list of devices, their uuids and local keys. With this, you can then connect to their MQTT server to issue commands.
MQTT login details are of the form:
Username: pThing+"_"+appKey+"_mb_"+sessionID+md5(md5(appKey)+ecode)[8:24]
Password: MD5(MD5(appSecret)+ecode)[8:24]
I would like to update that I have tried the methods as mentioned by @bahorn and sadly I have received "PERMISSION_DENIED" with the access keys that I have been given. I don't exactly understand why this does not work. Perhaps Tuya is limiting the cloud API access to the respective companies who ordered custom devices.
If anyone got the Tuya cloud REST API working on a no-name (generic) Tuya device, please let me know.
https://github.com/Ericmas001/Tuya-Api-Tools/wiki/Requests
I scrambled some info and applied a little formatting to my fiddler outputs (I did not do it manually, of course a little script helped me 😃) So you can see all the Requests/Responses that were sent.
There is more information than needed, but it's never too much when you try to understand something 😃
@bahorn The appKey and appSecret, can they be seen on those request or taken from existing app, or I really need to apply for one at the Tuya API Team ?
@Ericmas001 wow, that looks like it was a lot of work - thank you. The API keys I thought were sent in HTTP/HTTPS requests to Tuya's servers, but from your requests it looks like that isn't the case.
@Ericmas001 the clientId is actually the accessKey which Tuya app uses. But you will not be able use any Tuya Cloud API without knowing the keySecret, which is technically in the md5 hashed sign.
Unless you can decode the sign, which is virtually impossible, you won't be able to get the keySecret.
And as said, I rightfully requested for a set of API credentials from Tuya and tried them. They apparently do not give me permission to do anything on any generic devices.
Also, one more things you guys should understand is that Tuya devices can be controlled via both MQTT and HTTPS API. However, their app chose to use MQTT for some reason, which explains why actions could not be recorded in an HTTP sniffer.
I've done a bit of wiresharking between my phone, device and tuya cloud using the eFamilyCloud app and I can successfully decode all of the MQTT messages from the device using my device key.
The MD5 example on the Tuya site is not 100% accurate and needs some modification to get the MD5 hash that the site shows out the other end. I think their formatting in the code boxes is a bit messed up.
I can login to the cloud and subscribe to MQTT queues using the mqttfx app and if I send a control action from my phone, I can see it appear in the subscription queue and then decode the message using a PowerShell script I wrote. Wireshark shows your username and password in the MQTT connect message in plain text.
I've tried reversing this and constructing a message to send to the MQTT queue using the same method but unfortunately it just gets dropped by the server. I'm pretty sure that I'm signing the message in the same way that the eFamilyCloud app does so I had got to the point where I thought there must be some set up done over HTTPS before the MQTT conversation starts. My decode script decodes both genuine MQTT data and my own constructed data in the same way.
I've gone as far as exporting a conversation between my phone and the cloud (which successfully controls the device) and then another conversation between mqttfx and the cloud with my encoded data (which doesn't work) and the MQTT conversation looks identical (bar the data and IPs etc.)
It's possible that I'm not encoding the 'data' json part of the message correctly.
When I get a few minutes, I'll tidy up the encode/decode PowerShell scripts and publish them.
Turns out I had a few minutes, here's where I got with the encode/decode
https://github.com/bobalob/PS-Tuya-Tools
@bobalob I'm a MQTT total noob, so maybe this makes no sence, but does MQTT have some kind of headers, like HttpHeaders, that could be different, something like a userAgent that would be blocked or i don't know ...
@Ericmas001 You can extract them from the many apps, which was discussed early on in #5. In the app I looked at I found the signing process was dumped in the android logs, which includes the AppSecret and AppKey. (Look for "SignRequest" or something on that line.)
I was able to send messages directly to their MQTT server using the paho-mqtt Python library. I just logged in using details I took from packet captures (I only later figured out how usernames/passwords were generated). The app actually supports MQTT over TLS but never uses it for some reason.
Worth noting that you can actually replay messages sent over MQTT as the time is actually ignored (or at least by the devices I used).
@bobalob Are you sending it to the topic "smart/mb/out/<device ID>"? You code for producing messages seems correct but I don't have a Windows box on me right to verify. The first commented out template is what I've used to control devices.
@dominicklee I actually didn't know the Cloud/App API supported that. I thought it would fall back to it when I forced everything though a HTTP proxy but it never did so I assumed they didn't support it.
I tested the tuya.m.device.dp.publish
action yesterday and was able to turn the light on/off.
Just wondering, did you modify my code to use any of the cloud actions listed on https://docs.tuya.com/en/cloudapi/cloudAPI/index.html I only ever got an "PERMISSION_DENIED" when attempting to use an action that wasn't a mobile one (where my API key was from). I assume cloud Keys can't access mobile ones as well.
@bahorn I'm impressed that you were able to use tuya.m.device.dp.publish
to turn lights on/off. I actually requested for cloud keys but for some reason I could not get those to work with the cloudAPI in doing the dp.device publish. See image for details
@bahorn You have successfully got the app access working it seems. However, the cloud access for API is not exactly the same, and that is what I am trying to figure out.
@dominicklee I just made some progress on getting the cloud API working literally 30 seconds ago. Turns out they actually were doing something different. (The secret was being added to the start of signing string?!). Each endpoint is specific to the type of key though.
Just got a call to both tuya.p.weather.city.info.list
and tuya.cloud.device.get
working.
Code: https://gist.github.com/bahorn/160b4143badd1b6fae61cec629fce339
@bahorn Yes, I was publishing on the "smart/mb/out/devId" while subscribed to smart/mb/in and another topic pXXXXXX/mb/euXXXXXXXX. I wasn't aware you could replay a message from the phone. I did try that but that also got dropped. Perhaps the application I'm using is doing something weird or I'm missing something.
@Ericmas001 there are headers in the packet for various MQTT controls like message type, flags, QoS, retain etc. I'm setting those all the same as the app does.
@bahorn and @Ericmas001 Thank you guys for your helpful inputs! I have been able to to get Tuya mobile API working using your code examples and hints. Although I still haven't been able to control devices with the Tuya Cloud API, I feel the mobile API would do just as well in terms of controlling devices.
To confirm and clear up any confusion, the mobile API is signed as:
a=tuya.m.device.dp.publish||clientId=<accessKey>||lang=en||os=Android||postData=<your request JSON md5>||sid=<needed for dp actions>||time=<unix time in seconds>||v=1.0||<keySecret>
For mobile, you will need to be logged in (with an SID provided) to perform actions.
While the Tuya Cloud API is signed as:
<keySecret>a=tuya.cloud.device.dp.publish|clientId=<accessKey>|lang=en|os=Linux|postData=<your request JSON md5>|time=<unix time in seconds>|v=1.0
Both API credentials are different. However, even if you request the cloud API credentials for Tuya, they will only allow you to access only the devices you manufacture with them. If you are able to get the API keys for another working app, that may work but it goes beyond the scope of this issue.
@bahorn have you been able to use the mobile or cloud API to control the devices as well?
I have yet to try what @dominicklee mentioned where you can use the API versus MQTT to control the power plug
@dominicklee I'd love to see your progress. Any chance you have a fork hosted somewhere with these changes?
I like the idea of this library using LOCAL control over Cloud control. Of course, having both options would be ideal, giving the user choice, and providing failover if one isn't available.
Another nice aspect of getting at least SOME of the Cloud API worked out... the Cloud API returns deviceID and localKey for all devices. Having this piece in place would keep people from having to wireshark their keys and would act as a "discovery" mode of sorts.
@dlashua right, it would be ideal if a user could just sign in with the same username & password and be able to control all their devices.
I'm planning to add cloud control of devices as optional functionality if this ever works. (Meaning, TuyAPI will default to controlling stuff locally unless told otherwise by the user.)
That's perfect! I applied for the Cloud API Key 3 days ago so that I could help get this underway, but I've yet to get info from them and my application still shows as "under review".
It's a shame they didn't make this a little bit easier. Tuya products are EVERYWHERE, they work quite well (as long as you use their permissions laiden apps), and are fairly inexpensive. And all that's "missing" is some documentation, and a way to easily access localKey.
There are several big downsides to the cloud approach as Tuya presents it. As best as I can tell, you'll need to perform a "tuya.cloud.user.sync" for each user that wishes to use the cloud through your key. Additionally, at that point, that cloud account will have access (without a password, from that point on) to that user's devices. So, this means 1) you can't put the Cloud API creds directly in the library or else everyone will have access to everyone else's devices, 2) because of this, a intermediate API will need to be developed for the library to hit, 3) this API will have to be hosted somewhere ($$$) and publically available, 4) users will have to trust this cloud service with their credentials.
Another option will be to require that every user seek their own Cloud API credentials, but, as you can see from my experience, this doesn't cater to the "I want it now" mentality, as I've been waiting three days with no response.
@dominicklee You mentioned "they will only allow you to access only the devices you manufacture with them" From what I can tell (and I am coming late to the game) you can only access devices that you register with them. So you could register a device from any manufacturer on your account and get the keys. What this means is if you want to share one of their apps with your implementation (say the Smart Life app) you would actually need those keys. But if you are ok registering the devices yourself, it should work with any device you purchase, but other apps (again such as Smart Life) would not see or have access to those devices. Does that make sense and match what you've seen so far?
Has someone made some progress on this, since? I'd like to control a power switch via script.
Yep, it's now possible with my new tuyapi/cloud package. Look at the Tuya docs for endpoints specific to controlling devices.
Although this should work, I haven't actually tried it, so let me know what happens @shoeper.