codetheweb / tuyapi

🌧 An easy-to-use API for devices that use Tuya's cloud services. Documentation: https://codetheweb.github.io/tuyapi.
MIT License
2.04k stars 332 forks source link

Controlling Tuya devices with cloud API instead of controlling them locally #20

Open michmike opened 6 years ago

michmike commented 6 years ago

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.

michmike commented 6 years ago

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

codetheweb commented 6 years ago

I believe the MD5 hash is the same as what's implemented in TuyAPI (see here), although I could be wrong.

bahorn commented 6 years ago

In the cloud API sign is generated by:

There is a special case for the parameter postData, which is done by:

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"]

michmike commented 6 years ago

@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.

michmike commented 6 years ago

Also one thing i was not aware of is that the postData is part of the MD5 hash to generate the sign. thank you

dominicklee commented 6 years ago

@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.
Ericmas001 commented 6 years ago

@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:

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 !

bahorn commented 6 years ago

@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:

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]

dominicklee commented 6 years ago

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.

Ericmas001 commented 6 years ago

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 ?

codetheweb commented 6 years ago

@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.

dominicklee commented 6 years ago

@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.

dominicklee commented 6 years ago

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.

bobalob commented 6 years ago

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

Ericmas001 commented 6 years ago

@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 ...

bahorn commented 6 years ago

@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/"? 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.

dominicklee commented 6 years ago

@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 Tuya screenshot

dominicklee commented 6 years ago

@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.

bahorn commented 6 years ago

@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

bobalob commented 6 years ago

@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.

bobalob commented 6 years ago

@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.

dominicklee commented 6 years ago

@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.

michmike commented 6 years ago

@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

dlashua commented 6 years ago

@dominicklee I'd love to see your progress. Any chance you have a fork hosted somewhere with these changes?

dlashua commented 6 years ago

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.

codetheweb commented 6 years ago

@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.)

dlashua commented 6 years ago

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.

BillSobel commented 6 years ago

@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?

shoeper commented 6 years ago

Has someone made some progress on this, since? I'd like to control a power switch via script.

codetheweb commented 6 years ago

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.

JohnRambo93 commented 5 years ago

Hi everyone, I'm fairly new to this and I am currently doing a project for university. I've tried to get the access ID and the ACCESS KEY for the cloud API from Tuya but was unable to. I was wondering if there is anyway to by pass the access ID and access key and just control the devices locally (i.e. no cloud access needed)?

BillSobel commented 5 years ago

You need the secret key for the device to manage it locally, you get that by registering the device with the cloud api. So if you can intercept the traffic and see the key you can access the devices locally, otherwise you do need the cloud access keys. Did you sign up and request them? I found they were fairly easy to get.

On Mon, Jul 23, 2018 at 2:53 AM, JohnRambo93 notifications@github.com wrote:

Hi everyone, I'm fairly new to this and I am currently doing a project for university. I've tried to get the access ID and the ACCESS KEY for the cloud API from Tuya but was unable to. I was wondering if there is anyway to by pass the access ID and access key and just control the devices locally (i.e. no cloud access needed)?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/codetheweb/tuyapi/issues/20#issuecomment-406971464, or mute the thread https://github.com/notifications/unsubscribe-auth/ADLj6K04kyVNuEEDTkwalYjOfTna5fqxks5uJYDmgaJpZM4Rsiok .

llee2015 commented 5 years ago

Hi all:

I'm new to Tuya. I'm trying to use Mobile API to control a device. I did get Cloud API's accessKey and accessSecret. but it does not seem to work with Mobile API. Here are my questions

  1. Can API key/secret of Cloud API used in Mobile API (App API)? If not, how do I get the key/secret of Mobile API?
  2. I tried calling tuya.m.user.email.password.register and tuya.m.user.email.password.login. It seems the user account generated by Cloud API is not the same as the SmartLife app account. Can I use Mobile API to access the user account of SmartLife app?

I've already spent a few days but could not get it going anywhere. Any help/hint is appreciated.

BillSobel commented 5 years ago
  1. In the Tuya site create a mobile app. Mobile keys are provisioned there
  2. Tuya isolates devices by keys. They do not give out the keys to their app. So all your devices will be available in your keyspace but NOT in the official Tuya app. You can pay them 1500 to publish your own copy of the app with your keys. Best Bill

Sent from my iPhone please excuse any typos.

On Jul 26, 2018, at 7:16 AM, llee2015 notifications@github.com wrote:

Hi all:

I'm new to Tuya. I'm trying to use Mobile API to control a device. I did get Cloud API's accessKey and accessSecret. but it does not seem to work with Mobile API. Here are my questions

Can API key/secret of Cloud API used in Mobile API (App API)? If not, how do I get the key/secret of Mobile API? I tried calling tuya.m.user.email.password.register and tuya.m.user.email.password.login. It seems the user account generated by Cloud API is not the same as the SmartLife app account. Can I use Mobile API to access the user account of SmartLife app? I've already spent a few days but could not get it going anywhere. Any help/hint is appreciated.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.

llee2015 commented 5 years ago

@BillSobel Thanks for clarification. I managed to get appKey and appSecret to make some calls but having trouble in adding devices. More questions

  1. If a device has been activated by other app like SmartLife, can it still be activated in my keyspace?
  2. How to add/activate the device? I can think of two ways a. Device API has a device activation function but it requires some authKey(?) for signing. b. Build a mobile app from sdk with appKey/appSecret then activate the device. Is there another way?
BillSobel commented 5 years ago

Hi.

Yes, you can activate a device on your key space, but the device will be deregistered in the other key space (e.g. the device can only be managed by one silo at a time, but can move through silos). I implanted both AP and UDP broadcast registration, the UDP broadcast registration was ported to this project, so you can provide the credentials and it should work.

Best Bill

On Fri, Jul 27, 2018 at 3:39 AM, llee2015 notifications@github.com wrote:

@BillSobel https://github.com/BillSobel Thanks for clarification. I managed to get appKey and appSecret to make some calls but having trouble in adding devices. More questions

  1. If a device has been activated by other app like SmartLife, can it still be activated in my keyspace?
  2. How to add/activate the device? I can think of two ways a. Device API has a device activation function but it requires some authKey(?) for signing. b. Build a mobile app with appKey/appSecret then activate the device. Is there another way?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/codetheweb/tuyapi/issues/20#issuecomment-408352635, or mute the thread https://github.com/notifications/unsubscribe-auth/ADLj6HIaOLp8jFDW6W4LvxLyGkjoc2ojks5uKtG0gaJpZM4Rsiok .

codetheweb commented 5 years ago

@llee2015 check out @tuyapi/link. The built-in token generator function is currently not working; so you'd have to use the .manual() class instead of the .wizard() class. If you can figure out how to generate tokens (it stopped working for me a few weeks ago), it'd be great if you could post the code for doing so.

llee2015 commented 5 years ago

@codetheweb Thanks. So Tuya changed their protocols recently? I'll try registering the device with Mobile app first then look into the UDP way. Will share if I figure out anything useful.

BillSobel commented 5 years ago

I don't believe the protocols have changed, my version of the code continues to get a token just fine, so its a bug in the current framework (I strongly suspect)

On Sat, Jul 28, 2018 at 12:02 AM, llee2015 notifications@github.com wrote:

@codetheweb https://github.com/codetheweb Thanks. So Tuya changed their protocols recently? I'll try registering the device with Mobile app first then look into the UDP way. Will share if I figure out anything useful.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/codetheweb/tuyapi/issues/20#issuecomment-408587991, or mute the thread https://github.com/notifications/unsubscribe-auth/ADLj6Kn3A524gby5xKdsBfkB0IiluYs9ks5uLAyJgaJpZM4Rsiok .

janakigarimella commented 5 years ago

Hi everyone, Any one are able to get the correct response of the apis using postman tool ??? If not using postman tool direct api. I have used the direct api of tuya apis following the code of <[https://github.com/TuyaInc/TuyaDemo/tree/master/cloud-sign/src/main/java] Using this link I got the sign value and I pasted that sign value directly in the postman tool as below s3

I cant understand why I'm getting "errorCode": "SING_VALIDATE_FALED_4" error like this can any one please help me out. In which case do I get like this

I have given the password by converting to MD5 encryption for example if give password as qdrttt12: 96fbfb9539dc68401908d4bdb2d36721.

Can any one tell me where am I giving wrong details.

Are the apis are get the response using MQTT. If Yes please tell me.

llee2015 commented 5 years ago

@janakigarimella the json payload should be { "postData": { "countryCode":"...", "email":"...", "passwd":"" } }

janakigarimella commented 5 years ago

@llee2015 .Thanks for your help but again getting the same response after giving the values of postdata in the postman tool as suggested.Please look at the screen shot is there any thing else missing.

{ "postData": { "countryCode":"91", "email":"....@gmail.com", "passwd":"96fbfb9539dc68401908d4bdb2d36721" } }

given in the postman tool.

client id given from the APP KEY from Edit .

Response: { "t": 1533111254008, "success": false, "errorCode": "SING_VALIDATE_FALED_4", "status": "error", "errorMsg": "Parameter or Data Error" }

llee2015 commented 5 years ago

@janakigarimella Sorry i made a mistake in the previous response.

  1. The POST payload should be postData={"countryCode:"...", "email": "...", passwd:"..."}

  2. HTTP command content-type has to be "application/x-www-form-urlencoded"

brannon-bowden commented 5 years ago

Does anyone happen to know how long an SID will last? Does it expire? Thanks!

janakigarimella commented 5 years ago

Using android sdk of tuya how they are controlling devices in outside network. Which line compares the inside and outside network and where this line exists. I have created a sample project to their devices.When I goto outside network it shows like Network error and command is not sent sucessfully.

codetheweb commented 5 years ago

@Sytanek don't have any hard numbers for you, but an issued SID lasts for at least ~5 minutes. I'm guessing it works for much longer than that; I just haven't tried an operation that runs for more than 5 minutes yet. 😃

@janakigarimella are you asking how the official Tuya app controls devices when the controlling phone isn't on the same network? If so, the phone calls Tuya's API to control devices from the cloud instead of sending packets locally.

brannon-bowden commented 5 years ago

@codetheweb thanks! Hrmm, just noticed my official smart life app just an update and also required two factor to log in....and your test script is returning an error to update my app to a newer version. Wonder if they swapped keys, or if the API is changing...

codetheweb commented 5 years ago

@Sytanek I wouldn't be surprised if the API's changed, they seem to have changed it in the past with no warning (can't actually confirm, as I was originally using their API in non-intended ways).

What test script are you talking about, and what's the specific error code?

dominicklee commented 5 years ago

@codetheweb @Sytanek Hey guys, its been a while since I touched those devices. But for your information, I just tested mine and it seems their API is still very much working. So even if updates were made, their old documentation still stands well.

dominicklee commented 5 years ago

At least with the devices I have, they are all functional. Now, if you are suggesting that their firmware is updated, I haven't tried updating the firmware on any of my devices. I will try it and let you know.