codetheweb / tuyapi

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

v6.0.0 Discussion #169

Open codetheweb opened 5 years ago

codetheweb commented 5 years ago

This issue will hopefully contain a more detailed plan and checklist in the future, but for now here's what we have

Not sure how you want to split up the work @kueblc. I can also work on @tuyapi/stub or similar.

kueblc commented 5 years ago

Been a busy week, will follow up

py60800 commented 5 years ago

Hi,

I don't know if this the right place to post the information but I want to let you know that I have developped a tool to dump communication with tuyadevices. It can be found here: https://github.com/py60800/tuyadump.

It is based upon gopacket, it can decrypt tuya commands (if provided with the keys!!).

By the way, you will find here a golang port of the API https://github.com/py60800/tuya. Still experimental...

codetheweb commented 5 years ago

That's cool @py60800, I'll add both projects to the README. :)

kueblc commented 5 years ago

Update; I've been focused on evaluating and reverse engineering the recent activity from Tuya, seems they are starting to take an active stance against local control and firmware freedom. :frowning_face:

Still a lot of work to be done, and I've been trying to get my hands on every firmware sample I can to speed up the process. If you come across any firmware bins or network captures of these recent updates please let me know!

p3g4asus commented 5 years ago

To give my contribute, If anyone is interested, I developed a python library that supports gocomma r9 that is a tuya smart remote. I developed also home-assistant remote component that supports it. I would like to thank you @codetheweb for all your hard work with Tuya devices and for your excellent tutorial on getting id and key out of the device.

kueblc commented 5 years ago

Some thoughts I've gathered over the past year or so:

  1. Only need to get() on connect and reconnect, should never need to call this manually; proactive updates should keep everything up to date. Store this in this.dps.
  2. If the above is implemented, we can make the user facing get() simply reference this.dps and move the current get() code into a private function, something like _update() or _dpQuery(). Or don't have get() at all and just expose this.dps, firing events to notify of updates. Or get() could be the "schema-tized" version along with direct access via this.dps.
  3. MessageParser.parse() should return the return code. If non-zero, fire an error event, but attempt to process the rest of the queue.
  4. Heartbeat should timeout if we don't get a response in a reasonable time. Throw an error or disconnect event or both.
  5. Device discovery should be its own file/class. If we then assume TuyaDevice is always being called with the required ip id and key we can then remove some safety checks.
  6. Perhaps encrypt should only take a string and only return a Buffer. If you need base64 encoding just call toString('base64') on the returned Buffer. This reduces logic in encrypt while improving readability where it is used, as it becomes abundantly clear when it is in base64 format.
  7. Similarly with decrypt, it should only return a string. JSON decoding only needs to take place once, after decrypting. No need to attempt to JSON decode twice.
  8. MessageParser could be delegated to only process the TuyaFrame, and leave payload processing to another version specific class (3.1, 3.3 ...). The TuyaFrame format is unchanging between versions. This will make a cleaner approach to UDP processing possible too, as we don't pull in unrelated code just for device discovery.
  9. Our md5 convenience function should just return the full MD5. We can then implement version specific uses of it in our version specific payload processing, ie taking the substr for 3.1 signatures.
  10. Essentially, TuyaFrame and the encryption utilities are constant, the only thing changing between versions is payload handling. So we can insert an extra class between these two to deal with the version specific stuff.
  11. MessageParser.parse() currently checks for the frame suffix at the end of the buffer only, even if there are multiple packets. It should check for the suffix in each packet. This is a small bug but a bug nonetheless.
  12. If we implement schemas, I think we should be giving developers the option to interact with the device at a low level, much as it is now. Have the "schema-tized" TuyaDevice object be a subclass of the existing TuyaDevice.
  13. set() is unnecessarily complex, we should just pass the dps directly, as if the multiple option is specified. Read: set({1: true, 2: 55}) vs set({set: '1', dps: true}); set({set: '2', dps: 55})
  14. Expose more device commands. Looking at MessageParsers CommandTypes there are a lot of features we can bring to developers. Even commands we don't (yet) know how to use should be accessible through some combination of TuyaDevice.send.
  15. If we're breaking API anyway, we could take this opportunity to rename some things for consistency and brevity. For example commandByte could become simply command (technically not a byte anyway, it takes up a full 32 bits in the TuyaFrame even if they only use the lowest byte right now, they could easily change that in future releases).
  16. Related to the above, we may want our class names to match file names. Also consider moving TuyaDevice into lib and have index.js only there to expose all the available interfaces.
kueblc commented 5 years ago

A lot of my suggestions amount to removing and splitting code; this should ease the testing and maintenance burden. For testing purposes I think we should gather real world samples of communication data, rather than relying on our library to convert in one direction and back in the other.

codetheweb commented 5 years ago

Really like all of those suggestions @kueblc, thanks for taking the time to think through this.

One other thing I was thinking about the other day was creating a second package that layers on top of TuyAPI, providing a common interface for device categories like lightbulbs and outlets. So for example, instead of encoding the color of a lightbulb whatever weird way Tuya devices expect, the user could just run await device.setColor('#ff00ff') or whatever. I don't think it should become part of the base TuyAPI package, but potentially could be very useful as a helper package.

Unfortunately, I've been pretty busy this summer and don't expect to really be able to dedicate a good chunk of time to implement some of this stuff until fall when I'm back in college. So if anyone else visits this issue and the last comment is a few months old, please have patience :).

codetheweb commented 5 years ago

Expanding on the above, I was thinking about this yesterday: we could move TuyAPI-related functionality into a new package, @tuyapi/driver. This package would be focused on only providing a low-level interface to devices. A second package, called @tuyapi/devices, would then provide a high-level interface (with helper functions for each device type).

Moving this package to be under the @tuyapi umbrella would have a few positives:

The main downside is that I don't see it being easy to get people to migrate to one of the new packages.

Just wanted to jot down some thoughts, take them or leave them. :)

Also: I've seen a few projects recently that hit API endpoints Tuya created specifically for their Home Assistant plugin. I've been meaning to try them out and see if we can get the localKey from one of them, as the AnyProxy solution doesn't seem to be working. Update: doesn't seem to work after a quick inspection, the Home Assistant API only returns { online: true, state: true }.

fondberg commented 5 years ago

Is the anyproxy solution not working? I was just about to try tuyapi out because my latest plugs couldn't be flashed using tuya-convert

codetheweb commented 5 years ago

@fondberg people have had mixed results with AnyProxy from what I've heard.

You can always just sniff the key manually instead.

codetheweb commented 5 years ago

I've been working on a rough prototype for the next major version of TuyAPI over the last few weeks, and I think it's ready for some testing and review.

I made a new repo for it at @tuyapi/driver. I'm not completely committed to moving the repo source at this point, and this repo may get merged or something into this one once it's ready.

A few things:

Here's an example script that logs all received packets while rapidly toggling the first property of a device (save as dev.js so it's ignored by Git, build Typescript files first with npm run build):

const {Device} = require('./dist');

const device = new Device({ip: '', key: '', id: ''});

device.connect();

device.on('connect', async () => {
  device.update();
});

device.on('data', frame => console.log(frame));

device.on('state-change', async () => {
  console.log(device.get());

  await device.set({1: !device.get()['1']});
});

I've only tested it with v3.1 devices so far, as I still haven't gotten a v3.3 device. v3.3 should work, in theory.

kueblc commented 5 years ago

Awesome, looking forward to checking it out, though I'm not familiar with Typescript so like you I may not be able to recommend the "right" way to do things.

codetheweb commented 5 years ago

Has anyone had a chance to take a look at the new package?

I'd love to ship it before the end of the year if possible.

jezzaaa commented 5 years ago

I don't know if this the right place to post the information but I want to let you know that I have developped a tool to dump communication with tuyadevices. It can be found here: https://github.com/py60800/tuyadump.

@py60800 : I'm fascinated by this. What would it take to add support for v3.3?

py60800 commented 5 years ago

What are the change between protocol 3.3 and 3.1 ?

If there is any change in the way encryption is used, it may take some time (they may have fixed some errors in the implementation).

I do not have any device using this protocol but I could try to add support if I am provided with samples of communication (tcpdump).

codetheweb commented 5 years ago

@jezzaaa @py60800 if you want to continue this conversation please move it to an issue on @py60800's repo.

codetheweb commented 5 years ago

@kueblc I've been thinking about your suggestion to run end-to-end tests against real data, but I'm having a hard time thinking through how it would work. i.e. if your source is a PCAP file, when do you send the packets coming from the simulated device? How do you check if the packets match without always doing a byte-by-byte compare (sometimes packets have timestamps in the data)?

I really would like to test against captured traffic, just not sure how it would work.

kueblc commented 5 years ago

I think that my comment was more about cases like this where we are testing the parser against the encoder. (The idea being, if the parser and encoder are equally wrong, we wouldn't know.) Tests like these should be replaced with tests like this where the cipher is tested against a known fixed result.

We could implement this by adding PCAPs and a pcap parser/relay, but I think it would be easiest to implement by just adding more data samples embedded as strings directly to the tests.

codetheweb commented 5 years ago

Got it, that makes more sense.

So test against static data for unit tests and @tuyapi/stub for end-to-end?

kueblc commented 5 years ago

Yup, that's it. Hopefully I'll be able to help out again soon, just finishing up a couple big jobs.

codetheweb commented 5 years ago

Alright, sounds good.

codetheweb commented 4 years ago

Due to other commitments, I unfortunately don't see this getting done by the end of the year. I think January / early February is a more realistic target at this point.

The core code is mostly done, we just have tests and error handling / edge cases left (unless we want to restructure it slightly).

Bobo-amg commented 4 years ago

Any progress?

Thanks

codetheweb commented 4 years ago

TL;DR: Yes. No. Maybe?

I've been meaning to post an update for a while. I don't know if you saw it, but @tuyapi/driver is where I've been working on "v6". But I haven't worked on it in a while (4 months according to the commit log) for a number of factors:

So. All that to say that I don't yet know what the future holds for TuyAPI. If I do new development work in the future, it'll probably be on cloud libraries or tuya-convert.

gredin commented 4 years ago

@codetheweb I certainly agree with your analysis of the users (2 categories of needs).

Which tool would you recommend for using Tuya cloud instead of local communication with the device? I think "many" people just need to use any login/password from one of the official app and still be able to interact with Tuya Cloud API.

codetheweb commented 4 years ago

There's already a repo for their Open API, but I haven't thoroughly documented it yet. We would also have to add a lot more functions to the wrapper to allow for device control, but that's fairly trivial.

It would be nice to be able to use your login & password from the Tuya app, but without extracting the API key from the official app you're only able to use the Home Assistant API that Tuya made available. I don't think that API can control all device types / attributes.

TRSx80 commented 4 years ago

Came across the link here while reading through #5, and read your thoughts above:

there are really only two categories of users

I would say that I am in a third category. I would normally be in your second category, in fact I bought my devices from the outset with the intention of flashing {Tasmota,Espurna,etc.} on them, but ended up learning they were not ESP8266 based at all but rather TW-02 (WinnerMicro W600-based) smart sockets. Therefore I think the only option available to me will be to try and contain them on my own local network, and control them locally.

I am definitely NOT OK with them "phoning home" in fact if I cannot figure out a way to get this to work without doing so, I am simply going to chuck them in the bin.

I have no idea how common these "not ESP8266" based modules are, nor if they will become more common in future, but just something to factor into your consideration whether there is still a need here.

I'm not implying you need to force yourself to do anything you don't want to. You have done a lot already @codetheweb, and it is greatly appreciated! If you feel 2.0 is "good enough" then so be it. Overall your assessment of the shifting sands is more or less correct I suppose. Just wanted to point out there is a third category (since you asked). :wink:

Cheers, mate! :beers: Happy Friday! :tada:

codetheweb commented 4 years ago

Good point, thanks for chipping in.

If you go through Tuya's wizard to create new whitelabeled devices, there are actually a far number of hardware choices that aren't ESP based - so I expect to continue to see non ESP Tuya devices manufactured. I dunno how many though.

Would be very nice if there were some stats somewhere showing what chipsets active Tuya devices use as percentages.

TRSx80 commented 4 years ago

Would be very nice if there were some stats somewhere showing what chipsets active Tuya devices use as percentages.

More data points are almost always good for decision making, I suppose. In that vein, and for the record, the following is what I came across during my research.

I remember @kueblc while back (2018?) in #5 saying ~ "API keys used to be easy to get, now they cost $1,000." Now maybe he was talking about different type of keys than current method (not sure) but that certainly does not appear to be the case right now (or I am mixing things up).

Or possibly Tuya market strategy changed. More recently they seem to want to be much more open to devs. I base this on article like this: Tuya Smart unveils 2020 strategy, launches Cloud Development Platform to global developers during the AI+IoT Business Conference which is from May 27, 2020.

Personally I am always leery of big companies marketing bullshit. I suppose maybe once I create new developer account (or perhaps independent of it) I could email them and ask what the percentages are. We will find out real fast just how "open and co-operative" they really are trying to be...

codetheweb commented 4 years ago

Yeah, they are much more developer-friendly than they were a few years ago (although they still have a long ways to go). Their Home Assistent module was quite recently added, so they do seem open to hobbyist integrations.

TRSx80 commented 4 years ago

I was aware that Tuya themselves are in fact the official contributor / maintainer of the HA module. I don't use HA nor Tuya stuff (other than these few modules I have been trying to get working) but my understanding is that HA module is cloud only?

In fact, when someone from here tried to suggest using tuyapi, the issue was immediately closed. About a year later, someone asked essentially well "why no local API?" and then the thread was actually locked. lol Which tells me everything I need to know about how "open" they actually want to be in reality.

I understand that average Joe consumer needs something easy that "just works" out of the box. Which actually requires some cloud crap if you start thinking about just how you would implement something like that. This is in fact your first case, above. However, completely locking down the platform like typical dinosaur business model control freaks is another thing entirely. And companies who act in such ways can go DIAF, IMHO.

I only looked into this a moderate amount, in the context of my research trying to get these few modules working, so perhaps my read on the situation is off. But, barring further feedback from you guys more involved, I don't think it is.

As a matter of fact, the amount of time this has taken just re-enforces my belief that it is ultimately a waste of time trying to deal with such closed off things at all in the first place. Don't get me wrong, I greatly appreciate all your efforts, and thankfully tuyapi gives us some more options, where otherwise there would be none. However ultimately we are totally dependent on some profit motivated multinational corporation, who could really shut down their API whenever they choose to do so. Which is a position I decided already some years ago that I no longer am willing to be put into. And at this point, I try (whenever I feel is appropriate) to encourage others to also realize these things, and to vote with our feet/wallets for more truly open options.

I suppose I am writing all of this @codetheweb mostly directed at you (and perhaps any others reading). But in your case, I realized you are still quite a young guy, still in college if I am not mistaken. And wow, good for you, having such projects under your belt already at such a young age!

Anyway you are clearly a bright young person. I don't know if you ever been exposed to ideas of Free / Libre Software as more often nowadays "Open Source" seems to get mentioned a lot more. But ultimately that is what we are talking about here, freedom and control. I trust you are smart enough to figure out what is good and right actions to take, if only provided with the right information. Personally, I never understood what all the fuss was about until I got my head around these ideas, and for me that was not even until just a few years ago.

Anyway, enough of my idiotic ramblings. Cheers, and thanks for all the fish! :beers:

goatzen commented 3 years ago

Hi, I wonder if anyone can help. I'm using tuya api for a personal project, a sort of smart life app for local control of tuya devices. I will share the code if I get it finished. So far so good apart from RGB control. According to the information Here the following code should set the bulb color to yellow

device.set({ multiple: true, data: { '1': req.body.on, // true '2': req.body.mode, // 'colour' '3': req.body.bright, // 134 '4': 255, '5': req.body.color, // '90b00000ff19' '6': 'cf38000168ffff', '7': 'ffff500100ff00', '8': 'ffff8006ff000000ff000000ffff0000ff0000ff0000', '9': 'ffff5001ff0000', '10': 'ffff0505ff000000ff00ffff00ff00ff0000ff000000' } }).then(() => { statusChanged = true; console.log("values set ok"); }).catch(() => console.log("values set failed"));

On/off switch and white mode work fine. Could the device RGB encoding be different? It's a generic tuya bulb sold under the brand name 'bomcosy'. Thanks for your efforts in developing such a great api.

codetheweb commented 3 years ago

I don't use RGB bulbs myself, so I'm afraid I can't really help there.

You may have better luck posting here.

You could also try using homebridge-tuya with your bulbs to see if that works. If it does, then I'd look at the source to see how it's being encoded. If not, @iRayanKhan is usually helpful and might have some ideas.