tinytuya icon indicating copy to clipboard operation
tinytuya copied to clipboard

IR REMOTE CONTROL

Open adlerlinhares opened this issue 3 years ago • 39 comments

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

adlerlinhares avatar Oct 24 '21 05:10 adlerlinhares

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.

jasonacox avatar Oct 24 '21 15:10 jasonacox

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

S08

adlerlinhares avatar Oct 24 '21 16:10 adlerlinhares

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 avatar Oct 26 '21 05:10 ClusterM

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 avatar Oct 27 '21 12:10 ClusterM

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

ClusterM avatar Oct 27 '21 13:10 ClusterM

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.

jasonacox avatar Oct 28 '21 05:10 jasonacox

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

ClusterM avatar Oct 30 '21 10:10 ClusterM

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.

ClusterM avatar Dec 07 '21 09:12 ClusterM

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

jasonacox avatar Dec 08 '21 05:12 jasonacox

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. Нашлось решение ?

Mosoonk avatar Jan 17 '22 00:01 Mosoonk

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

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)

jasonacox avatar Jan 24 '22 05:01 jasonacox

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

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

Mosoonk avatar Jan 24 '22 08:01 Mosoonk

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

jasonacox avatar Jan 25 '22 04:01 jasonacox

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

korjaa avatar Feb 11 '22 20:02 korjaa

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'}

helen-fornazier avatar Mar 07 '22 21:03 helen-fornazier

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.

jasonacox avatar Mar 12 '22 22:03 jasonacox

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

helen-fornazier avatar Mar 13 '22 15:03 helen-fornazier

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.

apach3guy avatar Apr 18 '22 14:04 apach3guy

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.

jgric2 avatar May 17 '22 12:05 jgric2

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

mont5piques avatar May 27 '22 15:05 mont5piques

Great discovery, @mont5piques !! This is brilliant! :clap:

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!

jasonacox avatar May 27 '22 18:05 jasonacox

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

jgric2 avatar May 28 '22 05:05 jgric2

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

mont5piques avatar May 30 '22 10:05 mont5piques

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

mont5piques avatar May 30 '22 13:05 mont5piques

@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 avatar May 30 '22 15:05 jasonacox

@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

jasonacox avatar Jun 05 '22 04:06 jasonacox

Just in case you wonder how to decode and encode those button code:

import base64

def base64_to_pulses(code_base_64):
    raw_bytes = base64.b64decode(code_base_64)
    return [int.from_bytes(raw_bytes[x:x+2], byteorder="big") // 4 for x in range(2, len(raw_bytes) - 1, 2)]

def pulses_to_base64(pulses):
    pulses = [0xd500] + [x * 4 for x in pulses] # convert 1 us to 250ns and add unknown constant
    raw_bytes = [[(x >> 8) & 0xFF, x & 0xFF] for x in pulses]
    raw_bytes = [x for xs in raw_bytes for x in xs] + [8] # flatten and add unknown padding
    return base64.b64encode(bytes(raw_bytes)).decode("ascii")

def hex_to_pulses(pulses):
    raw_bytes = bytes.fromhex(pulses)
    return [int.from_bytes(raw_bytes[x:x+2], byteorder="little") for x in range(0, len(raw_bytes), 2)]

def pulses_to_hex(pulses):
    return "".join([f"{((x >> 8) | (x << 8)) & 0xFFFF:04x}" for x in pulses])

Base64 used for local commands, hex strings used for cloud API.

ClusterM avatar Jul 05 '22 19:07 ClusterM

Nice! Thanks @ClusterM !

What do you think about having a separate module/class for IR devices? Is anyone here interested in creating an IR module for TinyTuya?

e.g.

# Example usage of community contributed device modules
from tinytuya import Contrib

ir = Contrib.IRdevice( 'abcdefghijklmnop123456', '172.28.321.475', '1234567890123abc' )

See https://github.com/jasonacox/tinytuya/tree/master/tinytuya/Contrib

jasonacox avatar Jul 06 '22 01:07 jasonacox

Nice! Thanks @ClusterM !

What do you think about having a separate module/class for IR devices? Is anyone here interested in creating an IR module for TinyTuya?

e.g.

# Example usage of community contributed device modules
from tinytuya import Contrib

ir = Contrib.IRdevice( 'abcdefghijklmnop123456', '172.28.321.475', '1234567890123abc' )

See https://github.com/jasonacox/tinytuya/tree/master/tinytuya/Contrib

I'll try :)

ClusterM avatar Jul 06 '22 10:07 ClusterM

Awesome! 👍

jasonacox avatar Jul 07 '22 01:07 jasonacox