tinytuya
tinytuya copied to clipboard
IR REMOTE CONTROL
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
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.
Thank you, the model is: " WI-FI INFRARED REMOTE CONTROL - S08 "
I'm using "Ya-IR01":
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
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 :(
Also, device responses "json obj data unvalid" on DP_QUERY
request and "parse data error" on DP_QUERY_NEW
request.
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.
Seems like the CONTROL_NEW
command uses some alternative encryption key/method. We need to decompile the Smart Life app to understand it.
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 like680a7803bc017803bc01bc01bc01bc01bc0178037803bc01bc01bc01bc01bc01bc01bc01bc01bc01bc01bc01bc01bc0178037803bc01bc017803bc01bc01bc01bc01bc01bc0178037803
@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 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.
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) andcode
is a hexadecimal string of 16-bit (little-endian) values with signal lengths and gap lengths in microseconds. E.g. code112233445566778899aa
means:
1122
= 8721 microseconds of signal3344
= 17459 microseconds of gap5566
= 26197 microseconds of signal7788
= 34935 microseconds of gap99aa
= 43673 microseconds of signal And so on. So RC-6-encoded button 0x3D looks like680a7803bc017803bc01bc01bc01bc01bc0178037803bc01bc01bc01bc01bc01bc01bc01bc01bc01bc01bc01bc01bc0178037803bc01bc017803bc01bc01bc01bc01bc01bc0178037803
@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.
- Где видео?
- Нашлось решение ?
Где видео? Нашлось решение ?
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)
Где видео? Нашлось решение ?
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 the
Cloud
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
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).
I have a Nedis branded IR Remote
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-" |
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.
>>> 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'}
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.
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
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.
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.
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
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!
I will look into this tonight and try to make a working C# example. Cheers mate will report how it goes!
Hi @jasonacox I will create a PR with these instructions.
@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, 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.
@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
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.
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
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 :)
Awesome! 👍