jasonacox / tinytuya

Python API for Tuya WiFi smart devices using a direct local area network (LAN) connection or the cloud (TuyaCloud API).
MIT License
937 stars 167 forks source link

Add support for newer IR devices, and several IR format converters #228

Closed uzlonewolf closed 1 year ago

uzlonewolf commented 1 year ago

Also adds set_multiple_values() to core as the newer devices require everything to be in one packet. I also updated turn_on() and turn_off() to return the received data instead of just dropping it on the floor while I was in there.

Documentation for new format converters and head/key sending in examples/Contrib/IRRemoteControlDevice-example.py

Closes #225 and I'm also going to say Closes #74 and Closes #27

anar4732 commented 1 year ago

thank you for the patch! it looks like work but sometimes i see this error on console. idk if it is about this or not.

DEBUG:Network connection error in _send_receive() - retry 1/5
Traceback (most recent call last):
  File "C:\Users\ANAR\AppData\Local\Programs\Python\Python310\lib\site-packages\tinytuya\core.py", line 795, in _send_receive
    rmsg = self._receive()
  File "C:\Users\ANAR\AppData\Local\Programs\Python\Python310\lib\site-packages\tinytuya\core.py", line 712, in _receive
    data = self._recv_all(header_len+ret_end_len)
  File "C:\Users\ANAR\AppData\Local\Programs\Python\Python310\lib\site-packages\tinytuya\core.py", line 690, in _recv_all
    newdata = self.socket.recv(length)
ConnectionResetError: [WinError 10054] an existing connection was forcibly closed by the remote host
uzlonewolf commented 1 year ago

@anar4732 Yeah, sometimes the devices just close the connection for no apparent reason. I have no idea why they do that, but tinytuya sees it and retries automatically so it's not a big deal.

Xmister commented 1 year ago

@uzlonewolf Sorry for the ping, but how hard would it be to add support for RF devices? Based on https://github.com/tuya-cloudcutter/tuya-cloudcutter.github.io/blob/3736ae997a30db7e8c379dd5472521922f6cd43f/devices/tuya-generic-ir-rf-universal-remote-controller-v2.0.8.json they must use the same DPS for RF as well.

uzlonewolf commented 1 year ago

@Xmister I'm not sure if you're asking about cloud or local. Local already works for both Zigbee and Bluetooth sub-devices so I would expect it to work for RF devices as well. Does https://github.com/jasonacox/tinytuya/blob/master/examples/zigbee_gateway.py do anything?

Xmister commented 1 year ago

@uzlonewolf I'm talking about local, but I was more interested in learning/sending raw 433MHz codes, like how this IR module does, instead of interacting with an already set up gw-child pair. (My ir-rf gateway is cut from the cloud, so I can't set up subdevices anymore)

uzlonewolf commented 1 year ago

Ah, I see what you mean now. I was thinking they were RF devices similar to how the Zigbee ones work.

It shouldn't be too hard to add, though I'm going to need a packet capture and/or a firmware dump to figure out the protocol. I know yours is cut from the cloud, but does it have any RF devices stored?

Xmister commented 1 year ago

Sadly, no. I was trying libretuya-esphome on it, but didn't realise it doesn't yet support spi on these devices, so communicating with the SH4 radio module is not possible. I was able to get a factory app firmware without the device specific bits, so I can use tinytuya now for local control. Would this firmware help in any way? I've ordered a similar device for the same reason, so that I might be able to sniff the protocol somehow. But it will be weeks until it arrives.

uzlonewolf commented 1 year ago

As long as it has the JSON parsing bits in it it should work. I'm currently digging through my universal hub firmware dump to see if it knows about the RF commands, and I have a IR+RF gateway coming from Amazon in a couple days https://www.amazon.com/gp/product/B0BY2C68BJ/

Xmister commented 1 year ago

Thanks. Here is the one I'm using right now: https://github.com/tuya-cloudcutter/tuya-cloudcutter.github.io/files/11353905/Tuya-Generic_IR---RF-Universal-Remote-Controller-v2.0.8.bin.zip You can find a few different in the commit comments over https://github.com/tuya-cloudcutter/tuya-cloudcutter.github.io/commit/3736ae997a30db7e8c379dd5472521922f6cd43f

uzlonewolf commented 1 year ago

Making some progress. Looks like if you set the key rf_type (any value, it's not actually looked at) it puts the parser into RF mode. The command is then in the control key with the options rf_study, rfstudy_exit, rfstudy_send, send_pair, and send_cmd. Still looking into what data those commands expect.

Xmister commented 1 year ago

Since, this is a 2 band RF controller, I guess rf_study_feq would set the actual frequency to listen/send over. (315 vs 433). I've also seen rf_shortstudy when running strings over the binary. How do you set rf_type is that over the 201 DP as well? If you wish I can open a port for you to connect to my device with tinytuya until you have your own.

uzlonewolf commented 1 year ago

201 takes a JSON string so you just send {"rf_type": ..., "control": "...", ...}

Unfortunately I need to see the traffic between the SmartLife app and the device, so just the device won't help. Mine is showing delivery tomorrow so it won't be too long.

uzlonewolf commented 1 year ago

Ok, my device came in and I was able to get some packet captures.

First, I added 2 random curtains to see what "known device" data looks like:

 to   'eb####tb' v3.3 cmd: 13 (0D) len(543)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"mode\\":8,\\"key1\\":{\\"code\\":\\"ffffc01fa4934924924924924d34924da4936db0\\",\\"delay\\":0,\\"intervals\\":0,\\"times\\":5},\\"feq\\":0,\\"rate\\":0,\\"cfg\\":{\\"b\\":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,96,255,0,0,31,16],\\"c\\":[0,102,236,28,240,128,20,8,145,2,2,208],\\"s\\":[174,224,53,0,0,244,16,226,66,32,0,129],\\"d\\":[63,30,128,204,0,0,0,0,0,0,0,41,192,218,33,75,5,0,80,45,0,1,5,5],\\"t\\":[81,154,12,0,12,176,0,31,4,63,127],\\"f\\":[66,113,206,28,66,91,28,28]},\\"control\\":\\"send_pair\\"}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(575)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"mode\\":2,\\"key1\\":{\\"code\\":\\"924924fffcd349369249249249249269249a4924924924924d369a6d36c000\\",\\"delay\\":0,\\"intervals\\":0,\\"times\\":6},\\"feq\\":0,\\"rate\\":0,\\"cfg\\":{\\"b\\":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,96,255,2,0,31,16],\\"c\\":[0,102,236,28,240,128,20,8,145,2,2,208],\\"s\\":[174,224,53,0,0,244,16,226,66,32,0,129],\\"d\\":[71,34,144,204,0,0,0,0,0,0,0,41,192,223,29,91,7,0,80,45,0,1,6,6],\\"t\\":[81,38,3,0,10,176,0,31,4,63,127],\\"f\\":[66,166,231,28,66,144,53,28]},\\"control\\":\\"send_pair\\"}"}}'

After that I mashed the buttons a bit:

 to   'eb####tb' v3.3 cmd: 13 (0D) len(543)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"mode\\":8,\\"key1\\":{\\"code\\":\\"ffffc01fa4934924924924934934924da4926930\\",\\"delay\\":0,\\"intervals\\":0,\\"times\\":5},\\"feq\\":0,\\"rate\\":0,\\"cfg\\":{\\"b\\":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,96,255,0,0,31,16],\\"c\\":[0,102,236,28,240,128,20,8,145,2,2,208],\\"s\\":[174,224,53,0,0,244,16,226,66,32,0,129],\\"d\\":[63,30,128,204,0,0,0,0,0,0,0,41,192,218,33,75,5,0,80,45,0,1,5,5],\\"t\\":[81,154,12,0,12,176,0,31,4,63,127],\\"f\\":[66,113,206,28,66,91,28,28]},\\"control\\":\\"send_cmd\\"}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(543)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"mode\\":8,\\"key1\\":{\\"code\\":\\"ffffc01fa4934924924924924db4924da4936d30\\",\\"delay\\":0,\\"intervals\\":0,\\"times\\":5},\\"feq\\":0,\\"rate\\":0,\\"cfg\\":{\\"b\\":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,96,255,0,0,31,16],\\"c\\":[0,102,236,28,240,128,20,8,145,2,2,208],\\"s\\":[174,224,53,0,0,244,16,226,66,32,0,129],\\"d\\":[63,30,128,204,0,0,0,0,0,0,0,41,192,218,33,75,5,0,80,45,0,1,5,5],\\"t\\":[81,154,12,0,12,176,0,31,4,63,127],\\"f\\":[66,113,206,28,66,91,28,28]},\\"control\\":\\"send_cmd\\"}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(543)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"mode\\":8,\\"key1\\":{\\"code\\":\\"ffffc01fa4934924924924934d34924da4926db0\\",\\"delay\\":0,\\"intervals\\":0,\\"times\\":5},\\"feq\\":0,\\"rate\\":0,\\"cfg\\":{\\"b\\":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,96,255,0,0,31,16],\\"c\\":[0,102,236,28,240,128,20,8,145,2,2,208],\\"s\\":[174,224,53,0,0,244,16,226,66,32,0,129],\\"d\\":[63,30,128,204,0,0,0,0,0,0,0,41,192,218,33,75,5,0,80,45,0,1,5,5],\\"t\\":[81,154,12,0,12,176,0,31,4,63,127],\\"f\\":[66,113,206,28,66,91,28,28]},\\"control\\":\\"send_cmd\\"}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(543)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"mode\\":8,\\"key1\\":{\\"code\\":\\"ffffc01fa4934924924924934934924da4926930\\",\\"delay\\":0,\\"intervals\\":0,\\"times\\":5},\\"feq\\":0,\\"rate\\":0,\\"cfg\\":{\\"b\\":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,96,255,0,0,31,16],\\"c\\":[0,102,236,28,240,128,20,8,145,2,2,208],\\"s\\":[174,224,53,0,0,244,16,226,66,32,0,129],\\"d\\":[63,30,128,204,0,0,0,0,0,0,0,41,192,218,33,75,5,0,80,45,0,1,5,5],\\"t\\":[81,154,12,0,12,176,0,31,4,63,127],\\"f\\":[66,113,206,28,66,91,28,28]},\\"control\\":\\"send_cmd\\"}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(543)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"mode\\":8,\\"key1\\":{\\"code\\":\\"ffffc01fa4934924924924934d34924da4926db0\\",\\"delay\\":0,\\"intervals\\":0,\\"times\\":5},\\"feq\\":0,\\"rate\\":0,\\"cfg\\":{\\"b\\":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,96,255,0,0,31,16],\\"c\\":[0,102,236,28,240,128,20,8,145,2,2,208],\\"s\\":[174,224,53,0,0,244,16,226,66,32,0,129],\\"d\\":[63,30,128,204,0,0,0,0,0,0,0,41,192,218,33,75,5,0,80,45,0,1,5,5],\\"t\\":[81,154,12,0,12,176,0,31,4,63,127],\\"f\\":[66,113,206,28,66,91,28,28]},\\"control\\":\\"send_cmd\\"}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(543)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"mode\\":8,\\"key1\\":{\\"code\\":\\"ffffc01fa49349249249249249b4924da49369b0\\",\\"delay\\":0,\\"intervals\\":0,\\"times\\":5},\\"feq\\":0,\\"rate\\":0,\\"cfg\\":{\\"b\\":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,96,255,0,0,31,16],\\"c\\":[0,102,236,28,240,128,20,8,145,2,2,208],\\"s\\":[174,224,53,0,0,244,16,226,66,32,0,129],\\"d\\":[63,30,128,204,0,0,0,0,0,0,0,41,192,218,33,75,5,0,80,45,0,1,5,5],\\"t\\":[81,154,12,0,12,176,0,31,4,63,127],\\"f\\":[66,113,206,28,66,91,28,28]},\\"control\\":\\"send_cmd\\"}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(543)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"mode\\":8,\\"key1\\":{\\"code\\":\\"ffffc01fa49349249249249249b4924da49369b0\\",\\"delay\\":0,\\"intervals\\":0,\\"times\\":5},\\"feq\\":0,\\"rate\\":0,\\"cfg\\":{\\"b\\":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,96,255,0,0,31,16],\\"c\\":[0,102,236,28,240,128,20,8,145,2,2,208],\\"s\\":[174,224,53,0,0,244,16,226,66,32,0,129],\\"d\\":[63,30,128,204,0,0,0,0,0,0,0,41,192,218,33,75,5,0,80,45,0,1,5,5],\\"t\\":[81,154,12,0,12,176,0,31,4,63,127],\\"f\\":[66,113,206,28,66,91,28,28]},\\"control\\":\\"send_cmd\\"}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(543)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"mode\\":8,\\"key1\\":{\\"code\\":\\"ffffc01fa4934924924924924db4924da4936d30\\",\\"delay\\":0,\\"intervals\\":0,\\"times\\":5},\\"feq\\":0,\\"rate\\":0,\\"cfg\\":{\\"b\\":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,96,255,0,0,31,16],\\"c\\":[0,102,236,28,240,128,20,8,145,2,2,208],\\"s\\":[174,224,53,0,0,244,16,226,66,32,0,129],\\"d\\":[63,30,128,204,0,0,0,0,0,0,0,41,192,218,33,75,5,0,80,45,0,1,5,5],\\"t\\":[81,154,12,0,12,176,0,31,4,63,127],\\"f\\":[66,113,206,28,66,91,28,28]},\\"control\\":\\"send_cmd\\"}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(543)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"mode\\":8,\\"key1\\":{\\"code\\":\\"ffffc01fa4934924924924934d34924da4926db0\\",\\"delay\\":0,\\"intervals\\":0,\\"times\\":5},\\"feq\\":0,\\"rate\\":0,\\"cfg\\":{\\"b\\":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,96,255,0,0,31,16],\\"c\\":[0,102,236,28,240,128,20,8,145,2,2,208],\\"s\\":[174,224,53,0,0,244,16,226,66,32,0,129],\\"d\\":[63,30,128,204,0,0,0,0,0,0,0,41,192,218,33,75,5,0,80,45,0,1,5,5],\\"t\\":[81,154,12,0,12,176,0,31,4,63,127],\\"f\\":[66,113,206,28,66,91,28,28]},\\"control\\":\\"send_cmd\\"}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(543)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"mode\\":8,\\"key1\\":{\\"code\\":\\"ffffc01fa4934924924924934934924da4926930\\",\\"delay\\":0,\\"intervals\\":0,\\"times\\":5},\\"feq\\":0,\\"rate\\":0,\\"cfg\\":{\\"b\\":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,96,255,0,0,31,16],\\"c\\":[0,102,236,28,240,128,20,8,145,2,2,208],\\"s\\":[174,224,53,0,0,244,16,226,66,32,0,129],\\"d\\":[63,30,128,204,0,0,0,0,0,0,0,41,192,218,33,75,5,0,80,45,0,1,5,5],\\"t\\":[81,154,12,0,12,176,0,31,4,63,127],\\"f\\":[66,113,206,28,66,91,28,28]},\\"control\\":\\"send_cmd\\"}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(543)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"mode\\":8,\\"key1\\":{\\"code\\":\\"ffffc01fa4934924924924934d34924da4926db0\\",\\"delay\\":0,\\"intervals\\":0,\\"times\\":5},\\"feq\\":0,\\"rate\\":0,\\"cfg\\":{\\"b\\":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,96,255,0,0,31,16],\\"c\\":[0,102,236,28,240,128,20,8,145,2,2,208],\\"s\\":[174,224,53,0,0,244,16,226,66,32,0,129],\\"d\\":[63,30,128,204,0,0,0,0,0,0,0,41,192,218,33,75,5,0,80,45,0,1,5,5],\\"t\\":[81,154,12,0,12,176,0,31,4,63,127],\\"f\\":[66,113,206,28,66,91,28,28]},\\"control\\":\\"send_cmd\\"}"}}'

Then I learned a 2-button remote. After learning each key it had me test them:

 to   'eb####tb' v3.3 cmd: 13 (0D) len(127)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"control\\":\\"rf_study\\",\\"study_feq\\":\\"433\\",\\"ver\\":\\"2\\"}"}}'
 from 'eb####tb' v3.3 cmd:  8 (08) len(319)  b'...{"dps":{"202":"eyJudW0iOjEsInZlciI6IjIiLCJzdHVkeV9mZXEiOiI0MzMiLCJkYXRhMCI6InRpU2RBek1CTXdHZEE1MERNd0V6QVowRG5RTXpBVE1CblFNekFaMERNd0dkQTUwRE13RXpBWjBEblFNekFaMERNd0V6QVowRE13R2RBek1CblFPZEF6TUJuUU16QVRNQm5RT2RBek1CTXdHZEF6TUJuUU16QVowRE13R2RBNTBETXdFekFRPT0iLCJsdiI6WzBdfQ=="},"t":1683050684}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(127)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"control\\":\\"rfstudy_exit\\",\\"study_feq\\":\\"433\\",\\"ver\\":\\"2\\"}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(127)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"control\\":\\"rfstudy_exit\\",\\"study_feq\\":\\"433\\",\\"ver\\":\\"2\\"}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(463)  b'...{"dps":{"201":"{\\"ver\\":\\"2\\",\\"rf_type\\":\\"sub_2g\\",\\"control\\":\\"rfstudy_send\\",\\"study_feq\\":\\"433\\",\\"key1\\":{\\"code\\":\\"eyJudW0iOjEsInZlciI6IjIiLCJzdHVkeV9mZXEiOiI0MzMiLCJkYXRhMCI6InRpU2RBek1CTXdHZEE1MERNd0V6QVowRG5RTXpBVE1CblFNekFaMERNd0dkQTUwRE13RXpBWjBEblFNekFaMERNd0V6QVowRE13R2RBek1CblFPZEF6TUJuUU16QVRNQm5RT2RBek1CTXdHZEF6TUJuUU16QVowRE13R2RBNTBETXdFekFRPT0iLCJsdiI6WzBdfQ==\\",\\"times\\":6,\\"delay\\":0,\\"intervals\\":0}}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(463)  b'...{"dps":{"201":"{\\"ver\\":\\"2\\",\\"rf_type\\":\\"sub_2g\\",\\"control\\":\\"rfstudy_send\\",\\"study_feq\\":\\"433\\",\\"key1\\":{\\"code\\":\\"eyJudW0iOjEsInZlciI6IjIiLCJzdHVkeV9mZXEiOiI0MzMiLCJkYXRhMCI6InRpU2RBek1CTXdHZEE1MERNd0V6QVowRG5RTXpBVE1CblFNekFaMERNd0dkQTUwRE13RXpBWjBEblFNekFaMERNd0V6QVowRE13R2RBek1CblFPZEF6TUJuUU16QVRNQm5RT2RBek1CTXdHZEF6TUJuUU16QVowRE13R2RBNTBETXdFekFRPT0iLCJsdiI6WzBdfQ==\\",\\"times\\":6,\\"delay\\":0,\\"intervals\\":0}}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(127)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"control\\":\\"rfstudy_exit\\",\\"study_feq\\":\\"433\\",\\"ver\\":\\"2\\"}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(127)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"control\\":\\"rf_study\\",\\"study_feq\\":\\"433\\",\\"ver\\":\\"2\\"}"}}'
 from 'eb####tb' v3.3 cmd:  8 (08) len(319)  b'...{"dps":{"202":"eyJudW0iOjEsInZlciI6IjIiLCJzdHVkeV9mZXEiOiI0MzMiLCJkYXRhMCI6Ind5U2RBelFCTkFHZEE1MEROQUUwQVowRG5RTTBBVFFCblFNMEFaMEROQUdkQTUwRE5BRTBBWjBEblFNMEFaMEROQUUwQVowRE5BR2RBelFCblFPZEF6UUJuUU5YQVRRQm5RT2RBelFCTkFHZEF6UUJuUU0wQVowRG5RTlhBVFFCblFNMEFRPT0iLCJsdiI6WzBdfQ=="},"t":1683050700}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(127)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"control\\":\\"rfstudy_exit\\",\\"study_feq\\":\\"433\\",\\"ver\\":\\"2\\"}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(127)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"control\\":\\"rfstudy_exit\\",\\"study_feq\\":\\"433\\",\\"ver\\":\\"2\\"}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(463)  b'...{"dps":{"201":"{\\"ver\\":\\"2\\",\\"rf_type\\":\\"sub_2g\\",\\"control\\":\\"rfstudy_send\\",\\"study_feq\\":\\"433\\",\\"key1\\":{\\"code\\":\\"eyJudW0iOjEsInZlciI6IjIiLCJzdHVkeV9mZXEiOiI0MzMiLCJkYXRhMCI6Ind5U2RBelFCTkFHZEE1MEROQUUwQVowRG5RTTBBVFFCblFNMEFaMEROQUdkQTUwRE5BRTBBWjBEblFNMEFaMEROQUUwQVowRE5BR2RBelFCblFPZEF6UUJuUU5YQVRRQm5RT2RBelFCTkFHZEF6UUJuUU0wQVowRG5RTlhBVFFCblFNMEFRPT0iLCJsdiI6WzBdfQ==\\",\\"times\\":6,\\"delay\\":0,\\"intervals\\":0}}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(463)  b'...{"dps":{"201":"{\\"ver\\":\\"2\\",\\"rf_type\\":\\"sub_2g\\",\\"control\\":\\"rfstudy_send\\",\\"study_feq\\":\\"433\\",\\"key1\\":{\\"code\\":\\"eyJudW0iOjEsInZlciI6IjIiLCJzdHVkeV9mZXEiOiI0MzMiLCJkYXRhMCI6Ind5U2RBelFCTkFHZEE1MEROQUUwQVowRG5RTTBBVFFCblFNMEFaMEROQUdkQTUwRE5BRTBBWjBEblFNMEFaMEROQUUwQVowRE5BR2RBelFCblFPZEF6UUJuUU5YQVRRQm5RT2RBelFCTkFHZEF6UUJuUU0wQVowRG5RTlhBVFFCblFNMEFRPT0iLCJsdiI6WzBdfQ==\\",\\"times\\":6,\\"delay\\":0,\\"intervals\\":0}}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(463)  b'...{"dps":{"201":"{\\"ver\\":\\"2\\",\\"rf_type\\":\\"sub_2g\\",\\"control\\":\\"rfstudy_send\\",\\"study_feq\\":\\"433\\",\\"key1\\":{\\"code\\":\\"eyJudW0iOjEsInZlciI6IjIiLCJzdHVkeV9mZXEiOiI0MzMiLCJkYXRhMCI6Ind5U2RBelFCTkFHZEE1MEROQUUwQVowRG5RTTBBVFFCblFNMEFaMEROQUdkQTUwRE5BRTBBWjBEblFNMEFaMEROQUUwQVowRE5BR2RBelFCblFPZEF6UUJuUU5YQVRRQm5RT2RBelFCTkFHZEF6UUJuUU0wQVowRG5RTlhBVFFCblFNMEFRPT0iLCJsdiI6WzBdfQ==\\",\\"times\\":6,\\"delay\\":0,\\"intervals\\":0}}"}}'
 to   'eb####tb' v3.3 cmd: 13 (0D) len(127)  b'...{"dps":{"201":"{\\"rf_type\\":\\"sub_2g\\",\\"control\\":\\"rfstudy_exit\\",\\"study_feq\\":\\"433\\",\\"ver\\":\\"2\\"}"}}'

Oddly enough, decoding that base64 results in another JSON string:

{"num":1,"ver":"2","study_feq":"433","data0":"wySdAzQBNAGdA50DNAE0AZ0DnQM0ATQBnQM0AZ0DNAGdA50DNAE0AZ0DnQM0AZ0DNAE0AZ0DNAGdAzQBnQOdAzQBnQNXATQBnQOdAzQBNAGdAzQBnQM0AZ0DnQNXATQBnQM0AQ==","lv":[0]}

Decoding that base64 and displaying the result as hex results in:

00000000  c3 24 9d 03 34 01 34 01  9d 03 9d 03 34 01 34 01  |.$..4.4.....4.4.|
00000010  9d 03 9d 03 34 01 34 01  9d 03 34 01 9d 03 34 01  |....4.4...4...4.|
00000020  9d 03 9d 03 34 01 34 01  9d 03 9d 03 34 01 9d 03  |....4.4.....4...|
00000030  34 01 34 01 9d 03 34 01  9d 03 34 01 9d 03 9d 03  |4.4...4...4.....|
00000040  34 01 9d 03 57 01 34 01  9d 03 9d 03 34 01 34 01  |4...W.4.....4.4.|
00000050  9d 03 34 01 9d 03 34 01  9d 03 9d 03 57 01 34 01  |..4...4.....W.4.|
00000060  9d 03 34 01                                       |..4.|
Xmister commented 1 year ago

Looks great! Can you show me the messages for the curtain pairing as well? With rf_study, I can get an encoded code, but sending back the same with rfstudy_send doesn't work. I wonder if curtain pairing somehow returns a similar data structure to your first 2 messages.

uzlonewolf commented 1 year ago

There wasn't any pairing, just Add -> Curtain -> [select random brand] -> [select random model] -> "Does it work? [y/n]". Those 2 commands I posted above are the only ones it sent.

What does your send function look like? It should be something like:

import json
import tinytuya

d = tinytuya.Device(..., persist=True)

cmd = {
  "rf_type": "sub_2g",
  "control": "rfstudy_exit",
  "study_feq": "433",
  "ver": "2"
}

d.set_value( 201, json.dumps(cmd).replace(" ", "") )

cmd = {
  "ver": "2",
  "rf_type": "sub_2g",
  "control": "rfstudy_send",
  "study_feq": "433",
  "key1": {
    "code":"eyJudW0iOjEsInZlciI6IjIiLCJzdHVkeV9mZXEiOiI0MzMiLCJkYXRhMCI6Ind5U2RBelFCTkFHZEE1MEROQUUwQVowRG5RTTBBVFFCblFNMEFaMEROQUdkQTUwRE5BRTBBWjBEblFNMEFaMEROQUUwQVowRE5BR2RBelFCblFPZEF6UUJuUU5YQVRRQm5RT2RBelFCTkFHZEF6UUJuUU0wQVowRG5RTlhBVFFCblFNMEFRPT0iLCJsdiI6WzBdfQ==",
    "times": 6,
    "delay": 0,
    "intervals": 0
  }
}

d.set_value( 201, json.dumps(cmd).replace(" ", "") )

With that "code" replaced with what rfstudy returned in 202.

Xmister commented 1 year ago

I was trying the following way:

d._send_receive(d.generate_payload(tinytuya.CONTROL, {201: json.dumps({"rf_type": "sub_2g", "control": "send_cmd", "ver": "2", "key1": {"code": "eyJudW0iOjEsInZlciI6IjIiLCJzdHVkeV9mZXEiOiI0MzMiLCJkYXRhMCI6ImlSNU1FbUFGVVFIV0FvSUIxZ0xXQWxFQjFnSlJBZFlDVVFIV0FsRUIxZ0pSQWRZQ2dnSFdBbEVCMWdLQ0FZSUIxZ0tDQWRZQ2dnSFdBdFlDZ2dIV0FsRUJnZ0hXQXRZQ1VRR0NBZFlDMWdKUkFZSUIxZ0xXQW9JQjFnS0NBWUlCMWdLQ0FkWUNnZ0hXQW9JQjFnS0NBZFlDZ2dIV0FvSUIxZ0xXQW9JQlVRSFdBdFlDZ2dGUkFkWUMxZ0pSQVlJQjFnTFdBb0lCZ2dIV0F0WUNVUUdDQWRZQzFnST0iLCJsdiI6WzBdfQ==", "times": "6", "delay": "0", "intervals": "0"}}).replace(" ", "")}))

It did flash the light, so I thought it's working fine. However your code actually moves the curtain, so thank you for that.

uzlonewolf commented 1 year ago

Glad it's working.

Looking at the command you were sending, it looks like the differences are "rfstudy_send" vs "send_cmd", the inclusion of "study_feq", key1 numbers as integers vs strings, and the DP ID as a string vs integer (d.set_value() casts it to a string as that's what devices normally expect).

Now that you have it working, where do we go from here? Is a new device type still needed/useful?

Xmister commented 1 year ago

Well, I did try with rfstudy_send as well in the same format without luck, so it's possible that the field types was the issue. I mean, I can use it the way it is now, but I think a similar RFRemoteControlDevice would be helpful for a lot of folks

uzlonewolf commented 1 year ago

I don't mind making it, though I have my doubts that "lots" of people will use it. What functions do you want it to have? At this point I think I only have enough info to implement .receive_button() and .send_button().

Xmister commented 1 year ago

I think that's good enough for start. Good RF bridges are high in demand, this class could make these devices a simple one.

uzlonewolf commented 1 year ago

Added in #343