Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IR REMOTE CONTROL #74

Closed
adlerlinhares opened this issue Oct 24, 2021 · 82 comments · Fixed by #228
Closed

IR REMOTE CONTROL #74

adlerlinhares opened this issue Oct 24, 2021 · 82 comments · Fixed by #228
Labels
tuya_device Support for specific Tuya Devices

Comments

@adlerlinhares
Copy link

adlerlinhares commented Oct 24, 2021

HI, first of all apologize my poor english.
Im facing a major issue trying to code the IR remote control.
Is there anyway to find out how to control it with tinytuya?

Im using yout code:

import tinytuya

d = tinytuya.OutletDevice('XXX, "YYY", "ZZZ")
d.set_version(3.3)
d.set_socketPersistent(True)
payload = d.generate_payload
print(" > Send Request for Status < ")
payload = d.generate_payload(tinytuya.DP_QUERY)

print(" > Begin Monitor Loop <")
while(True):
    # See if any data is available
    data = d.receive()
    print('Received Payload: %r' % data)

    # Send keyalive heartbeat
    print(" > Send Heartbeat Ping < ")
    payload = d.generate_payload(tinytuya.HEART_BEAT)
    d.send(payload)

    print(" > Send Request for Status < ")
    payload = d.generate_payload(tinytuya.DP_QUERY)
    d.send(payload)
    
    
    # NOTE Some smart plugs require an UPDATEDPS command to update power data
    print(" > Send DPS Update Request < ")
    payload = d.generate_payload(tinytuya.UPDATEDPS)
    d.send(payload) 

AS result i get the follow message:

Received Payload: {'dps': {'201': '{"control":"study_exit"}'}}

Is there a way that i can have a list of commands?
what shoud be this "study_exit"?

Thank you so much!
Best regards

@jasonacox
Copy link
Owner

jasonacox commented Oct 24, 2021

Hi @adlerlinhares -

Can you post a link to the type of IR device you are trying to control?

I have one IR device and the only thing I have been able to get from the device is the temperature and humidity (no joke!). It can control our HDTV and other devices from the SmartLife App and Alexa we have set up for it, but I have been unable to determine the DPS codes to control it locally. There is a possibly that it is not designed to receive local control and only works via the Tuya Cloud.

If you paste the detail of your device, hopefully others in the community have figured the mystery.

@adlerlinhares
Copy link
Author

Thank you, the model is: " WI-FI INFRARED REMOTE CONTROL - S08 "

S08

@ClusterM
Copy link
Contributor

ClusterM commented Oct 26, 2021

I'm using "Ya-IR01":
image
Seems like it's the same.
I tried to press the virtual button using the Smart Life app and sniffed traffic:
Download as text: rc.log
Download as .pcap rc_filtered.zip

    DEVICE_ID = "bf8c72d8a60c61a70fpje0";
    DEVICE_KEY = "eb3a44d53ff730b3";

I can decrypt replies using this key but I can't decrypt requests:
First, this request:

  • DP_QUERY request (can't decrypt)
  • Reply: "json obj data unvalid" with retcode = 1

And this is repeated many times:

  • Huge CONTROL_NEW request (can't decrypt)
  • Empty answer with retcode = 0

@ClusterM
Copy link
Contributor

ClusterM commented Oct 27, 2021

Also, device info from API:

{
      "active_time":1635191034,
      "biz_type":18,
      "category":"wnykq",
      "create_time":1634501857,
      "icon":"smart/icon/ay1525749833414yotNt/368f140d503717dc53e208316b914518.png",
      "id":"bf8c72d8a60c61a70fpje0",
      "IP":"<censored>",
      "lat":"<censored>",
      "local_key":"eb3a44d53ff730b3",
      "lon":"<censored>",
      "model":"S06WB3S",
      "name":"Пульт ДУ",
      "online":true,
      "owner_id":"39781846",
      "product_id":"rxp2arf7xqixspqy",
      "product_name":"Smart IR",
      "status":[
         
      ],
      "sub":false,
      "time_zone":"+03:00",
      "uid":"<censored>",
      "update_time":1635301796,
      "uuid":"6ff497796722f78a"
   }

"status" field is empty at all :(

@ClusterM
Copy link
Contributor

Also, device responses "json obj data unvalid" on DP_QUERY request and "parse data error" on DP_QUERY_NEW request.

@jasonacox
Copy link
Owner

Ah! For my IR device, it has an onboard temp/humidity sensors which is the only thing that appear on QUERY. I suspect these devices are oriented toward CONTROL (sending signals vs recording or reporting state). Hum... very interesting.

@ClusterM
Copy link
Contributor

Seems like the CONTROL_NEW command uses some alternative encryption key/method. We need to decompile the Smart Life app to understand it.

@ClusterM
Copy link
Contributor

ClusterM commented Dec 7, 2021

I still can't control it locally but it works fine using the official Tuya Cloud API. It's offtopic but maybe it will help somebody. There is a method to send RAW IR signals: https://developer.tuya.com/en/docs/cloud/6c376807b5?id=Kb3oeb91u35ga
remote_id must be any remote ID (so you need to create at least one, I have no idea why it's required) and code is a hexadecimal string of 16-bit (little-endian) values with signal lengths and gap lengths in microseconds. E.g. code 112233445566778899aa means:

  • 1122 = 8721 microseconds of signal
  • 3344 = 17459 microseconds of gap
  • 5566 = 26197 microseconds of signal
  • 7788 = 34935 microseconds of gap
  • 99aa = 43673 microseconds of signal
    And so on.
    So RC-6-encoded button 0x3D looks like 680a7803bc017803bc01bc01bc01bc01bc0178037803bc01bc01bc01bc01bc01bc01bc01bc01bc01bc01bc01bc01bc0178037803bc01bc017803bc01bc01bc01bc01bc01bc0178037803

@jasonacox TinyTuya is designed for local communication only but it's using Cloud API calls in wizard.py. So maybe add Cloud API features to the library too? There is my Python class for Tuya API: https://pastebin.com/xwNmTcC9 - it's partially based on your code.

@jasonacox
Copy link
Owner

@ClusterM I like that idea! Adding Cloud API functions would be fairly straight forward. I'll add that to my wish list for this Christmas. :) I'll see what I can get done.

Great job on the RAW IR discovery! It would be good to capture it in the documentation for the cloud API calls.

@Mosoonk
Copy link

Mosoonk commented Jan 17, 2022

I still can't control it locally but it works fine using the official Tuya Cloud API. It's offtopic but maybe it will help somebody. There is a method to send RAW IR signals: https://developer.tuya.com/en/docs/cloud/6c376807b5?id=Kb3oeb91u35ga remote_id must be any remote ID (so you need to create at least one, I have no idea why it's required) and code is a hexadecimal string of 16-bit (little-endian) values with signal lengths and gap lengths in microseconds. E.g. code 112233445566778899aa means:

  • 1122 = 8721 microseconds of signal
  • 3344 = 17459 microseconds of gap
  • 5566 = 26197 microseconds of signal
  • 7788 = 34935 microseconds of gap
  • 99aa = 43673 microseconds of signal
    And so on.
    So RC-6-encoded button 0x3D looks like 680a7803bc017803bc01bc01bc01bc01bc0178037803bc01bc01bc01bc01bc01bc01bc01bc01bc01bc01bc01bc01bc0178037803bc01bc017803bc01bc01bc01bc01bc01bc0178037803

@jasonacox TinyTuya is designed for local communication only but it's using Cloud API calls in wizard.py. So maybe add Cloud API features to the library too? There is my Python class for Tuya API: https://pastebin.com/xwNmTcC9 - it's partially based on your code.

  1. Где видео?
  2. Нашлось решение ?

@jasonacox
Copy link
Owner

Где видео?
Нашлось решение ?

HI @inermetso , with v1.3.0, TinyTuya now supports cloud API functions. See https://github.com/jasonacox/tinytuya#tuya-cloud-access.

You can now use theCloud class and functions. I don't have the specifics for reading/controlling the IR controls, but it should be possible. Let us know if you get it to work.

import tinytuya

# Connect to Tuya Cloud
# c = tinytuya.Cloud()  # uses tinytuya.json 
c = tinytuya.Cloud(
        apiRegion="us", 
        apiKey="xxxxxxxxxxxxxxxxxxxx", 
        apiSecret="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 
        apiDeviceID="xxxxxxxxxxxxxxxxxxID")

# Display list of devices
devices = c.getdevices()
print("Device List: %r" % devices)

# Select a Device ID to Test
id = "xxxxxxxxxxxxxxxxxxID"

# Display Properties of Device
result = c.getproperties(id)
print("Properties of device:\n", result)

# Display Status of Device
result = c.getstatus(id)
print("Status of device:\n", result)

# Send Command - Turn on switch
commands = {
	'commands': [{
		'code': 'switch_1',
		'value': True
	}, {
		'code': 'countdown_1',
		'value': 0
	}]
}
print("Sending command...")
result = c.sendcommand(id,commands)
print("Results\n:", result)

@Mosoonk
Copy link

Mosoonk commented Jan 24, 2022

Где видео?
Нашлось решение ?

HI @inermetso , with v1.3.0, TinyTuya now supports cloud API functions. See https://github.com/jasonacox/tinytuya#tuya-cloud-access.

You can now use theCloud class and functions. I don't have the specifics for reading/controlling the IR controls, but it should be possible. Let us know if you get it to work.

import tinytuya

# Connect to Tuya Cloud
# c = tinytuya.Cloud()  # uses tinytuya.json 
c = tinytuya.Cloud(
        apiRegion="us", 
        apiKey="xxxxxxxxxxxxxxxxxxxx", 
        apiSecret="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 
        apiDeviceID="xxxxxxxxxxxxxxxxxxID")

# Display list of devices
devices = c.getdevices()
print("Device List: %r" % devices)

# Select a Device ID to Test
id = "xxxxxxxxxxxxxxxxxxID"

# Display Properties of Device
result = c.getproperties(id)
print("Properties of device:\n", result)

# Display Status of Device
result = c.getstatus(id)
print("Status of device:\n", result)

# Send Command - Turn on switch
commands = {
	'commands': [{
		'code': 'switch_1',
		'value': True
	}, {
		'code': 'countdown_1',
		'value': 0
	}]
}
print("Sending command...")
result = c.sendcommand(id,commands)
print("Results\n:", result)

I managed to send a request, a response comes : {'result': True, 'success': True, 't': 1643011067286}, but there is no reaction

@jasonacox
Copy link
Owner

Can you share your code? I'm not certain, but the response may only indicate that it was a correctly formatted command, not that it would actually do anything on the device. Also, in my experience with sending commands to the cloud, the "code" value can be different for same function (e.g. switch_1 may be switch).

@korjaa
Copy link

korjaa commented Feb 11, 2022

I have a Nedis branded IR Remote
image

I paired the device with Smart Life and got it showing up on my Tuya developer account. I then added one remote for "Samsung" TV and noticed that now I have 2 new devices in the Tuya developer view. I tried adding a "Sony" remote and now I have 3 new devices, "IR Remote Control", "TV", and "TV 2". So the specific TV remotes I added through the application now show up as new devices in cloud.

When queried for local key with the wizard, I get same local_key for "IR Remote Control", "TV", and "TV 2". I'm assuming the "IR Remote Control" is a gateway device for these specific remotes?

After querying for status, I get following:

>>> d_ir = tinytuya.Device(dev_id, dev_ip, dev_key)
>>> d_ir.set_version(3.3)
>>> d_ir.status()
{'devId': 'xxxxxxx', 'dps': {'1': 'send_ir'}}

If I repeat the same test, but just replace dev_id with the "TV" device ID, I get following:

>>> d_tv.tinytuya.Device(tv_id, dev_ip, dev_key)
>>> d_tv.set_version(3.3)
>>> d_tv.status()
{'Error': 'Invalid JSON Response from Device', 'Err': '900', 'Payload': 'gw id invalid'}

Not sure if related but there seems to be some concept of "gateway" in the tinytuya in the "payload_dict"

>>> tinytuya.payload_dict["default"][tinytuya.DP_QUERY]["command"]["gwId"]
'xxxx_dev_id_TV_xxxxx'

Not sure if above is any help or not, but just chipping in. I'd love to see this working with local control.

In the cloud view if I select "Debug Device" for "TV" I get following instruction set visible

Code Type Values
-/-- STRING "-/--"
-- -- --
0 STRING "0"
1 STRING 1
2 STRING 2
3 STRING 3
4 STRING 4
5 STRING 5
6 STRING 6
7 STRING 7
8 STRING 8
9 STRING 9
Back STRING "Back"
C ENUM { "min": 1, "max": 999, "scale": 0, "step": 1, "type": "Integer" }
Channel+ STRING "Channel+"
Channel- STRING "Channel-"
Down STRING "Down"
Home STRING "Home"
Left STRING "Left"
Menu STRING "Menu"
OK STRING "OK"
Power STRING "Power"
Right STRING "Right"
Up STRING "Up"
Volume+ STRING "Volume+"
Volume- STRING "Volume-"

@helen-fornazier
Copy link

Hi,

I would like to know if any one had any progress on this. I'm having exactly the same issue.

In the Tuya App, I see the IR blaster and the Samsung Tv I added, and I can successfully control the TV through the app.

When executing examples/cloud.py (slightly modified to pretty print) with the TV in the device id I get the following:

Sending command...
Results
: {'result': True, 'success': True, 't': 1646686305741, 'tid': '669227439e5811ecb5f6e6fd56795acb'}

But there is no reaction.
It doesn't matter if the commands exist or not, it always succeeds if the device is online.
I tested with 4 different IR blasters, I had the same result with all of them.

Full code: https://paste.ee/p/lvsd5
Full result: https://paste.ee/p/E3z3U

It seems that in the Cloud view of "Debug Device" for "TV", I can submit instructions from there, but I always get an error, so I wonder if this is an issue from Tuya side.

image

>>> d_ir = tinytuya.Device(dev_id, dev_ip, dev_key)
>>> d_ir.set_version(3.3)
>>> d_ir.status()
{'devId': 'xxxxxxx', 'dps': {'1': 'send_ir'}}

I also get this, also with:

>>> d_ir.detect_available_dps()
{'1': 'send_ir'}
>>> d_ir.set_value('1', 'send_ir')
{'devId': '50038154500291bd52c5', 'dps': {'1': 'send_ir'}, 't': 1646687924}
>>> d_ir.set_value('1', 'test')
{'Error': 'Timeout Waiting for Device', 'Err': '902', 'Payload': 'Check device key or version'}

@jasonacox
Copy link
Owner

Hi @helen-fornazier - thanks for posting this. It seems like you are close. Have you tried sending a command via cloud calls with just one setting? Maybe try different single commands to see what works?

commands = {
	'commands': [{
		'code': 'Volume+',
		'value': 'Volume+'
	}]
}
result = c.sendcommand(id,commands)

I do wish we could get local control of these IR devices, but Tuya may not make that available.

@helen-fornazier
Copy link

Hi @helen-fornazier - thanks for posting this. It seems like you are close. Have you tried sending a command via cloud calls with just one setting? Maybe try different single commands to see what works?

commands = {
	'commands': [{
		'code': 'Volume+',
		'value': 'Volume+'
	}]
}
result = c.sendcommand(id,commands)

Yes, I tried that with cloud.py, same results :(

I do wish we could get local control of these IR devices, but Tuya may not make that available.

Me too

@apach3guy
Copy link

It’s great to see others investigating this. I’ll be keeping an eye out for any updates here as I just picked up a tuya IR remote and I haven’t had any luck controlling it locally.

@jgric2
Copy link

jgric2 commented May 17, 2022

likewise, I am writing in C# though and always get this response: {"result":true,"success":true,"t":1652789917278,"tid":"7a8d20cad5db11eca914e276ec45657f"}
this is for the "Power" command for my TV, the result is true but no action happens, I feel this may be on Tuya's end.

@mont5piques
Copy link
Contributor

If it could help anyone here, I managed to get it work by doing this:

First, use your mobile app to send some IR commands. They will appear few times later on tuya iot platform (Device Logs when clicking on Debug Device on the device list).

IR Send events are the ones that we should consider, after extracting a decent JSON from the logs (see screenshot), I managed to get my commands work using this code in local lan mode:

import json
command = {"control":"send_ir","head":"","key1":"19iJiESYC9AEmAkAGJgJABiYC9AEmAvQBJgL0ASYC9AEmAkAGJgJABiYCQAYmAvQBJgJABiYC9AEmAkAGJgJABiYC9AEmAvQBJgL0ASYC9AEmAvQBJgJABiYC9AEmAvQBJgL0ASYCQAYmAkAGJgJABiYCQAYmAvQBJgJABiYCQAYmAkAGJgIUm/YimAgmAjDy9iKYCCYCMPL2IpgIJgIw8igjmAgmAjDyKCOYCCYCMPL2IpgIJgIw8igjmAgmAjDyKCOYCCYCMPL2IpgIJgIw8vYiyggmAjDy9iLKCCYCcII=","type":0,"delay":300}
payload = d.generate_payload(tinytuya.CONTROL, {"201": json.dumps(command)})
d.send(payload)

I hope it helps

image

@jasonacox
Copy link
Owner

Great discovery, @mont5piques !! This is brilliant! 👏

Would you like to submit a PR to add this instructions and example to the README in the DPS section? I think we could expand/add to this area: https://github.com/jasonacox/tinytuya#version-33---universal-ir-controller-with-temphumidity

I'm happy to add it myself, but want to give you the chance to get contribution credit too.

Great work!

@jgric2
Copy link

jgric2 commented May 28, 2022

I will look into this tonight and try to make a working C# example. Cheers mate will report how it goes!

@mont5piques
Copy link
Contributor

Hi @jasonacox
I will create a PR with these instructions.

mont5piques added a commit to mont5piques/tinytuya that referenced this issue May 30, 2022
Add a detailled procedure to control Universal IR Controllers using JSON DPs
Related to jasonacox#74
@mont5piques
Copy link
Contributor

mont5piques commented May 30, 2022

@jasonacox With this DPS, the set_value method does not work properly so I generated manally the payload and sent it without waiting for a response.

I think we should have an additional param to the set_value method named wait_for_response for example that is True by default and we could disable for these kinds of DPS.

@jasonacox
Copy link
Owner

@mont5piques, great idea! That would be a great feature and would be fairly easy to do. The example you gave above is basically the template:

payload = d.generate_payload(tinytuya.CONTROL, {index: value})
d.send(payload)

If you want to try it, create a PR for it as well. Otherwise, I'll see if I have time later today to get that in.

@jasonacox
Copy link
Owner

@mont5piques thanks for your suggestion! I added the parameter nowait to all the commands. This is now available in v1.5.0.

# Example use of nowait option
d.turn_on(nowait=True)
d.set_colour(r, g, b, nowait=True)
d.set_value(201, '9AEmAvQBJgL0ASYCQAYmAkAGJgJABiY', nowait=True)  # send IR command
d.set_value(25, '010e0d0000000000000003e803e8', nowait=True)      # set scene

@uzlonewolf
Copy link
Collaborator

@Apollon77 I just found it.

import tinytuya

ir = tinytuya.Device("35217541c45bbef5a1f1", version=3.3, persist=True)
payload = ir.generate_payload(tinytuya.CONTROL, {"1": "send_ir", "3":"020ed8000000000008001600160042015600ac05f3005807b8", "4":"01%^002001FE50AF@&%*@(", "10":3000, "13":0})
print( ir._send_receive(payload, getresponse=True) )

Or

import tinytuya

ir = tinytuya.Device("35217541c45bbef5a1f1", version=3.3, persist=True)
payload = ir.generate_payload(tinytuya.CONTROL, {"1": "study_key", "7":"liGiETMCqQYwAqcGOQKhBjYCSgIsAkwCNgJGAi8CTQIyAkoCLwKmBjYCowYyAqsGLwJlAhsCZQIQAmYCFQJsAg8CaAIRAmsCEQLIBhICzQYQAmYCEQJqAhECbAIQAmoCFwJmAhACxwYUAmcCEwJrAhECxgYTAskGEALHBhMCxgYSAsgGFwIgyw=="})
print( ir._send_receive(payload, getresponse=True) )

Hopefully I'll get a patch put together tomorrow.

@Apollon77
Copy link

Apollon77 commented Dec 1, 2022

Aahhh use study_key AND ID key_study (7) with the code ... what a fun ... good catch ... I should also receive such an IR device soon to verify :-)

@Apollon77
Copy link

With this ... could it be worth a try to build a json like {control: 'study_key', 'key_study': base64key} for the device type with 201/202 ;-) Will earlierst be able to try next week

@Apollon77
Copy link

@uzlonewolf Verified :-) Then we need the "converting formats" only for the 201/202 style devices

@uzlonewolf
Copy link
Collaborator

@Apollon77 I ended up basically rewriting the entire thing. You can now send by:
ir.send_button( base64_code ) or
ir.send_key( head, key ) or
ir.send_command( 'send', {'base64_code': base64_code} ) or
ir.send_command( 'send', { 'head': head, 'key': key } )

ir.receive_button() still receives just like it did, but now works with both types of devices. I also added a default for the timeout (30). Converters to convert to/from head+key were also added.

@Apollon77
Copy link

Cool. Will have a look next week.

@Apollon77
Copy link

@uzlonewolf I tested it and one of my devices with 201/202 do not work with head/key ...

{
   devId: 'bf781b021b60e971f5fvka',
   gwId: 'bf90851a27705b2de3rwll',
   uid: '',
   t: 1670255371,
   dps: {
     '201': '{"control":"send_ir","head":"010e0400000000000600100020003000620c4b0c5b","key1":"002%#000490#000D0010#000100@^","type":0}'
   }
}

base64 sending locally is working as it should.

Did that really worked for you?

@uzlonewolf
Copy link
Collaborator

uzlonewolf commented Dec 5, 2022

That "key1" says repeat twice, but the lack of a "delay" value may be confusing it. Try adding "delay":300. As a test you can just do

print( ir.set_value(201, '{"control":"send_ir","head":"010e0400000000000600100020003000620c4b0c5b","key1":"002%#000490#000D0010#000100@^","type":0,"delay":300}') )

@Apollon77
Copy link

Apollon77 commented Dec 5, 2022

I tried with and without delay ... same effect

dps: {
  '201': '{"control":"send_ir","head":"010e0400000000000600100020003000620c4b0c5b","key1":"002%#000490#000D0010#000100@^","type":0,"delay":300}'
}

Also the blue LED is not even blinking when I send it that way

@Apollon77
Copy link

oooohhhhhh ... I have it ... my issue was that I set the devId to be the "virtual one" and "gwId" to be the "real IR blaster one". I need to set both to be IR-Blaster ... then it also works ... whoooo

@Apollon77
Copy link

So exact: For IR you always need to send locally with devId adn gwId being identical and the "id of the raw IR blaster device".

@uzlonewolf
Copy link
Collaborator

It's not a lot, but I did stumble over some documentation for the 1-13 and 201/202 DPs at https://developer.tuya.com/en/docs/iot-device-dev/Infrared_radio_frequency?id=Kay1cva9iq4ow#title-9-What%20types%20of%20data%20points%20(DPs)%20are%20there%3F . Seems 1-13 are "Non-system DPs" while 201/202 are "System DPs"

I also figured out how to send IR via the Cloud API. The remote/key API commands are documented at https://developer.tuya.com/en/docs/cloud/ir-control-hub-open-service?id=Kb3oe2mk8ya72 and I posted some exampe code in #283

@denisidoro
Copy link

denisidoro commented Mar 3, 2023

Edit: nevermind. This did the trick!

===

Logs for my device are different:

shot 2023-03-03 at 10 03 07

I tried using the these values as key1 and head but to no avail. Data is sent but nothing happens:

DEBUG:building command 7 DEBUG:building command 7 payload=b'{"devId":"1866<...>9c6","uid":"1866080<...>59c6","t":"1677848838","dps":{"201":"{\\"control\\":\\"send_ir\\",\\"key1\\":\\"02$$0020E0E0E01F@%\\",\\"head\\":\\"010ed20000000000040015004000ad0730\\",\\"type\\":0,\\"delay\\":30}"}}'
DEBUG:sending payload
DEBUG:payload encrypted=b'0000<...>0aa55'

The connection seems to be correct because I get the following:

data = d.status() 
print('set_status() result %r' % data)
# set_status() result {'devId': '1866<...>9c6', 'dps': {'1': 'send_ir'}}

Any suggestions?

@uzlonewolf
Copy link
Collaborator

I mean, you can do that, but the entire reason Contrib.IRRemoteControlDevice exists was to abstract away those details. There are 2 different DPS sets in use and they use slightly different key1 formats. I recommend changing to:

from tinytuya.Contrib import IRRemoteControlDevice

ir = IRRemoteControlDevice( 'abcdefghijklmnop123456', '172.28.321.475', '1234567890123abc', persist=True )

ir.send_key( head, key )

@Gie92
Copy link

Gie92 commented Jun 27, 2023

Hello!

Just wondering how do you convert raw ir signals from the Tuya IOT website to pronto code?

Thanks!

@uzlonewolf
Copy link
Collaborator

@Gie92

from tinytuya.Contrib import IRRemoteControlDevice

b64 = 'IyOvEToCZQI5AkoCOgJNAjYCTwI4AlACNQJMAjkCTQI2ApsGSwKZBkkClwZMAp8GLALLBhgC0wYRAtMGEwLRBhMCbgIdAmkCGwLKBhsCagIaAsoGGgJzAhACbwIWAnICFAJvAh0CxgYdAmoCFwLMBhoCcAIUAtAGFALRBhQC0QYUAtAGFQKXnBgjCAkXAiDL'
pulses = IRRemoteControlDevice.base64_to_pulses( b64 )
print( 'pronto:', IRRemoteControlDevice.pulses_to_pronto ( pulses ) )

@Gie92
Copy link

Gie92 commented Jun 28, 2023

Thank you so much!

@Captain-Glen
Copy link

Captain-Glen commented Dec 12, 2023

For anyone in Australia with a Jaycar Smart IR you want:

import tinytuya
from tinytuya.Contrib import IRRemoteControlDevice
import json

power_b64 = "1UbJXFdx2CYMVVRbkDA3VJBMaQHcMGSDASJuIiIRQV8REiJwdCERXUESdG5wIV9jXzBSMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
pulses = IRRemoteControlDevice.base64_to_pulses( power_b64 )
pronto = IRRemoteControlDevice.pulses_to_pronto ( pulses )
print( 'pronto:', pronto )
headkey = IRRemoteControlDevice.pronto_to_head_key( pronto )
print ( 'headkey', headkey )
if headkey:
    head, key = headkey
print( head, key )

d = tinytuya.OutletDevice(
    dev_id="10630805bcddc2ec910c",
    address="192.168.1.140",      # Or set to 'Auto' to auto-discover IP address
    local_key="5141@a:KxJR`q'hd", 
    version=3.1)

payload = d.generate_payload(tinytuya.CONTROL, {"1": "study_key", "7":power_b64})
d.send(payload)

data = d.status() 
print('set_status() result %r' % data)

@fabianoarruda
Copy link

Hey guys, I think I've found a different format with the DPS 201. Check this out:

Moes IR thermostat logs
This is a device from Moes which is specific for Air conditioners. It crates a secondary virtual device which represents the configured Air conditioner. In my case the brand is Midea.

In the log above the commands are sent to the main device via DPS 201, you can notice 2 things:

  • v_devid is the id of the secondary virtual device
  • devid is the MAC address of the main device. seems to be optional, in some requests it is not set.
  • key_num tells how many keys you are sending on a single command
  • key1, key2 are the keys sent. So far I've not seem more than 2.

I was able to control the device locally by sending this exact payload, example:

# this command will power off my AC
command = {"head":"010ece0000000000040014003e00ab00ca","key1":{"data":"02$$0030B24D7B84E01F@%","data_type":0,"key":"power_off"},"devid":"","ver":"3","delay":300,"control":"send_ir","v_devid":"<redacted>","key_num":1}

payload = d.generate_payload(tinytuya.CONTROL, {"201": json.dumps(command)})
d.send(payload)

Another example, It seems to set mode, temperature and fan speed:

command = {"head":"010ece0000000000040014003e00ab00ca","key1":{"data":"02$$0030B24D3FC040BF@%","data_type":0,"key":"M0_T24_S3"},"devid":"","ver":"3","delay":300,"control":"send_ir","v_devid":"<redacted>","key_num":1}

I was able to find some information about the keys using Tuya API:

v2.0/infrareds/<id_of_my_main_device>/categories/5/brands/182/remotes/11272/rules

it will result in a long list of hashes, (showing a single item here):

...
{
  "code": "tbCM00sNmkFpazuYxQu6D4LFzBirY6UDbYLQBC2ZERI=",
  "key": "M0_T24_S3",
  "key_id": 0
},
...

Now, I'm trying to find what is the logic to build the head and data parts of the command.

@uzlonewolf
Copy link
Collaborator

Yep, that is the ver:3 I mentioned a while back

There are a few JSON keys that I have not seen in the wild. When "ver" is set to 3 it uses yet another format, and while I have not dug too deeply into it, it uses the JSON key "feq" to set the frequency.

Presumably the version format Contrib/IRRemoteControlDevice uses will work as long as "ver" isn't set.

If you can figure out the "/rules" format I would be interested in adding it, I took a crack at it a while ago but couldn't figure it out; presumably it is encrypted or compressed or something. Just a few bits changed causes the "/rules" data to be completely different #395 (comment)

@fabianoarruda
Copy link

@uzlonewolf Do you know any tool which can be used to maybe find the encrypt logic based on samples? I want to help with that, but I'm not sure yet how to...

Here are some samples, pairing /rules agains sent commands via Cloud:

Log /rule
{"head":"010ece0000000000040014003e00ab00ca","key1":{"data":"02$$0030B24DBF40D02F@%","data_type":0,"key":"power_on"},"devid":"","key2":{"data":"02$$0030B24D9F6040BF@%","data_type":0,"key":"M0_T24_S1"},"ver":"3","delay":300,"control":"send_ir","v_devid":"[REDACTED]","key_num":2} "code": "7G5fFZTk+ooPc/pM+PCJseS6S9QcKWAOnizZdqZkvjI=","key": "power_on"
{"head":"010ece0000000000040014003e00ab00ca","key1":{"data":"02$$0030B24D7B84E01F@%","data_type":0,"key":"power_off"},"devid":"","ver":"3","delay":300,"control":"send_ir","v_devid":"[REDACTED]","key_num":1} "code": "7l36Hd5Ir/2MaLq90VEMdU7nfMXewaVHyqozPM607t8=","key": "power_off"
{"head":"010ece0000000000040014003e00ab00ca","key1":{"data":"02$$0030B24D9F6040BF@%","data_type":0,"key":"M0_T24_S1"},"devid":"","ver":"3","delay":300,"control":"send_ir","v_devid":"[REDACTED]","key_num":1} "code": "liKcmnAz5ZvO4+Vs6dX85rs/b1p1cNO8MoMnIqf5rU8=","key": "M0_T24_S1"
{"head":"010ece0000000000040014003e00ab00ca","key1":{"data":"02$$0030B24D9F6050AF@%","data_type":0,"key":"M0_T23_S1"},"devid":"","ver":"3","delay":300,"control":"send_ir","v_devid":"[REDACTED]","key_num":1} "code": "liKcmnAz5ZvO4+Vs6dX85ndeF2Xv6lKIbBXEklwT7uU=","key": "M0_T23_S1"
{"head":"010ece0000000000040014003e00ab00ca","key1":{"data":"02$$0030B24D9F60708F@%","data_type":0,"key":"M0_T22_S1"},"devid":"","ver":"3","delay":300,"control":"send_ir","v_devid":"[REDACTED]","key_num":1} "code": "liKcmnAz5ZvO4+Vs6dX85i+M88gmYWTHQKggfMvrbE8=","key": "M0_T22_S1"
{"head":"010ece0000000000040014003e00ab00ca","key1":{"data":"02$$0030B24D9F60609F@%","data_type":0,"key":"M0_T21_S1"},"devid":"","ver":"3","delay":300,"control":"send_ir","v_devid":"[REDACTED]","key_num":1} "code": “liKcmnAz5ZvO4+Vs6dX85kjqeLroolB4MPk+4zlBJDY=","key": "M0_T21_S1"
{"head":"010ece0000000000040014003e00ab00ca","key1":{"data":"02$$0030B24D9F6020DF@%","data_type":0,"key":"M0_T20_S1"},"devid":"","ver":"3","delay":300,"control":"send_ir","v_devid":"[REDACTED]","key_num":1} "code": "liKcmnAz5ZvO4+Vs6dX85ssNicuJNuFsnYYMpLvdFe0=","key": "M0_T20_S1"

@phoenixmarines
Copy link

phoenixmarines commented Jun 20, 2024

@fabianoarruda

I'm trying to send this command using the tinytuya I'm totally lost I don't suppose you can send your whole script as mine doesn't seem to work.

# this command will power off my AC
command = {"head":"010ece0000000000040014003e00ab00ca","key1":{"data":"02$$0030B24D7B84E01F@%","data_type":0,"key":"power_off"},"devid":"","ver":"3","delay":300,"control":"send_ir","v_devid":"<redacted>","key_num":1}

payload = d.generate_payload(tinytuya.CONTROL, {"201": json.dumps(command)})
d.send(payload)

This is what I have currently:

import tinytuya
import json
from tinytuya.Contrib import IRRemoteControlDevice

d = tinytuya.OutletDevice(
    dev_id="MAIN DEVICE ID",
    address="192.168.88.14",      # Or set to 'Auto' to auto-discover IP address
    local_key="MAIN DEVICE LOCAL KEY", 
    version=3.1)

command = {"control":"send_ir","head":"020ece0000000000080010000f002e007e0026003e003f0318","delay":300,"devid":"","v_devid":"VIRTUAL DEVICE ID","key_num":2,"key1":{"key":"power_on","data":"01%&008028C60008087F900C8580000000000470@^","data_type":0,"relearn":False},"key2":{"key":"M1_T23_S0","data":"01%&008028C60008087F900C0E2000000000043C@^","data_type":0,"relearn":False}}

payload = d.generate_payload(tinytuya.CONTROL, {"201": json.dumps(command)})
d.send(payload)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tuya_device Support for specific Tuya Devices
Projects
None yet
Development

Successfully merging a pull request may close this issue.