jasonacox / tinytuya

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

Cannot access sub device on eMylo Smart WiFi IR #269

Closed tyge68 closed 1 year ago

tyge68 commented 1 year ago

I recently purchased an eMylo Smart WiFi IR which I can manage with the smart life app, and access on Tuya IoT platform This device contains actually 2 devices:

With tinytuya 1.10.1

d = tinytuya.Device('anyvalue', '<device ip>', '<localkey found in tuya IoT platform>')
d.set_version(3.3)
data = d.status() 
print('Device status: %r' % data)

It seems it doesn't really care about the device id as I can set any value it always return the status from the main device (humidity and temperature) Is there a limitation regarding support for "sub" devices ?

uzlonewolf commented 1 year ago

Are you sure it actually contains 2 devices? Usually combo devices like this only have 1 device with different DPS values with the IR portion using either 201-202 or 1-13. The virtual subdevices the app adds for the various IR devices do not actually exist and cannot be used directly.

What is the output of that d.status()?

tyge68 commented 1 year ago

@uzlonewolf this is the output of the device d.status() command.

Device status: {'dps': {'101': 187, '102': 50}}

the first is the temperature in Celsius (scale 0.1) and the second the Humidity in %. Do you mean that there is a way to access some other field that would be hidden by default ? (kind of forcing to read other DPS values ?)

uzlonewolf commented 1 year ago

A lot of devices do not return all their DPs when their status is requested, there are quite a few that are only asynchronously broadcast on state change. The newer IR protocols is one of them.

Try running this script:

import tinytuya
from tinytuya import Contrib

tinytuya.set_debug(toggle=True, color=True)

ir = Contrib.IRRemoteControlDevice( [contents of your existing Device() call] )

That will spit out quite a few debug messages, one of which should look something like

DEBUG:Detected control type 2

If so, just use Contrib.IRRemoteControlDevice() instead of tinytuya.Device() in your program. Some IR examples can be found at https://github.com/jasonacox/tinytuya/blob/master/examples/Contrib/IRRemoteControlDevice-example.py

tyge68 commented 1 year ago

@uzlonewolf Thanks, I have tried but it still return the "main" device data

DEBUG:TinyTuya [1.10.1]

DEBUG:building command 7 payload=b'{"devId":"bf05a85a03d9b07a45dfsj","uid":"bf05a85a03d9b07a45dfsj","t":"1675241209","dps":{"201":"{\\"control\\":\\"study_exit\\"}"}}'
DEBUG:sending payload
DEBUG:payload encrypted=b'000055aa000000010000000700000097332e3300000000000000000000000066f211160fc0023531db32da0c17306b36c43d38b862e21ef9946f90c8eff3e573a52f767c03d691edf9ac9f7606210650806701d8ddf71a04bc2e54ffd1267878ea8de7951504f443baa7f98a9c3d0beba8ac6de63e6011ef3709c8722b67c4063021cf51535501f7cbdab51fae6701ae94de86e39ac88c2d99578bedca8d5ae6895f7d0000aa55'
DEBUG:building command 7 payload=b'{"devId":"bf05a85a03d9b07a45dfsj","uid":"bf05a85a03d9b07a45dfsj","t":"1675241209","dps":{"1":"study_exit"}}'
DEBUG:sending payload
DEBUG:payload encrypted=b'000055aa000000020000000700000087332e3300000000000000000000000066f211160fc0023531db32da0c17306b36c43d38b862e21ef9946f90c8eff3e573a52f767c03d691edf9ac9f7606210650806701d8ddf71a04bc2e54ffd1267878ea8de7951504f443baa7f98a9c3d0b2113f3d6296d3898cf71c308e66144b7e837f1b68b28462aa37871a6f8a08c496c352c180000aa55'
DEBUG:status() entry (dev_type is default)
DEBUG:building command 10 payload=b'{"gwId":"bf05a85a03d9b07a45dfsj","devId":"bf05a85a03d9b07a45dfsj","uid":"bf05a85a03d9b07a45dfsj","t":"1675241209"}'
DEBUG:sending payload
DEBUG:payload encrypted=b'000055aa000000030000000a00000088ce776a11a3212bd15e6c390d6e6a444d50806701d8ddf71a04bc2e54ffd12678db08686c55bc5fec0ec2e4d3d67a51e636c43d38b862e21ef9946f90c8eff3e573a52f767c03d691edf9ac9f7606210650806701d8ddf71a04bc2e54ffd1267878ea8de7951504f443baa7f98a9c3d0b0b1978865c6d9dfb06bb2f67724f8e905b92ad7d0000aa55'
DEBUG:received data=b'000055aa00000001000000070000000c00000000a505a9140000aa55'
DEBUG:received null payload (TuyaMessage(seqno=1, cmd=7, retcode=0, payload=b'', crc=2768611604, crc_good=True, prefix=21930, iv=None)), fetch new one - retry 0 / 5
DEBUG:received data=b'000055aa00000002000000070000000c0000000018cfc5da0000aa55'
DEBUG:received null payload (TuyaMessage(seqno=2, cmd=7, retcode=0, payload=b'', crc=416269786, crc_good=True, prefix=21930, iv=None)), fetch new one - retry 1 / 5
DEBUG:received data=b'000055aa000000030000000a0000005c00000000f53599dad76087807774668d05ed2034b6a5f371c25589895d1374e4c276c14904c098a93ec84a4950c6f83df445f284934b58c9b875716461c58f03ec12f0e3c0958f6776661003e2dd95beb744ac36830a62050000aa55'
DEBUG:received message=TuyaMessage(seqno=3, cmd=10, retcode=0, payload=b'\xf55\x99\xda\xd7`\x87\x80wtf\x8d\x05\xed 4\xb6\xa5\xf3q\xc2U\x89\x89]\x13t\xe4\xc2v\xc1I\x04\xc0\x98\xa9>\xc8JIP\xc6\xf8=\xf4E\xf2\x84\x93KX\xc9\xb8uqda\xc5\x8f\x03\xec\x12\xf0\xe3\xc0\x95\x8fgvf\x10\x03\xe2\xdd\x95\xbe\xb7D\xac6', crc=2198495749, crc_good=True, prefix=21930, iv=None)
DEBUG:raw unpacked message = TuyaMessage(seqno=3, cmd=10, retcode=0, payload=b'\xf55\x99\xda\xd7`\x87\x80wtf\x8d\x05\xed 4\xb6\xa5\xf3q\xc2U\x89\x89]\x13t\xe4\xc2v\xc1I\x04\xc0\x98\xa9>\xc8JIP\xc6\xf8=\xf4E\xf2\x84\x93KX\xc9\xb8uqda\xc5\x8f\x03\xec\x12\xf0\xe3\xc0\x95\x8fgvf\x10\x03\xe2\xdd\x95\xbe\xb7D\xac6', crc=2198495749, crc_good=True, prefix=21930, iv=None)
DEBUG:decode payload=b'\xf55\x99\xda\xd7`\x87\x80wtf\x8d\x05\xed 4\xb6\xa5\xf3q\xc2U\x89\x89]\x13t\xe4\xc2v\xc1I\x04\xc0\x98\xa9>\xc8JIP\xc6\xf8=\xf4E\xf2\x84\x93KX\xc9\xb8uqda\xc5\x8f\x03\xec\x12\xf0\xe3\xc0\x95\x8fgvf\x10\x03\xe2\xdd\x95\xbe\xb7D\xac6'
DEBUG:decrypting=b'\xf55\x99\xda\xd7`\x87\x80wtf\x8d\x05\xed 4\xb6\xa5\xf3q\xc2U\x89\x89]\x13t\xe4\xc2v\xc1I\x04\xc0\x98\xa9>\xc8JIP\xc6\xf8=\xf4E\xf2\x84\x93KX\xc9\xb8uqda\xc5\x8f\x03\xec\x12\xf0\xe3\xc0\x95\x8fgvf\x10\x03\xe2\xdd\x95\xbe\xb7D\xac6'
DEBUG:decrypted 3.x payload='{"dps":{"101":175,"102":51,"201":"{\\"control\\":\\"study_exit\\"}"}}'
DEBUG:payload type = <class 'str'>
DEBUG:decoded results='{"dps":{"101":175,"102":51,"201":"{\\"control\\":\\"study_exit\\"}"}}'
DEBUG:status() received data={'dps': {'101': 175, '102': 51, '201': '{"control":"study_exit"}'}}
DEBUG:Detected control type 1

What is not clear is the gwId , normally in Tuya IoT it returns the main device id for the gateway is bf67bb67af7aa9b5e4s0eo while the device id is bf05a85a03d9b07a45dfsj and is marked as a sub device (sub = true)

{
  "result": {
    "active_time": 1673086626,
    "category": "infrared_ac",
    "category_name": "Infrared air conditioner",
    "create_time": 1673086626,
    "gateway_id": "bf67bb67af7aa9b5e4s0eo",
    "icon": "smart/icon/001453365846342fhj9e/11c62df7bd89c10f43d3e5ee8d13b4f4.png",
    "id": "bf05a85a03d9b07a45dfsj",
    "ip": "",
    "lat": "47.7584",
    "local_key": "d639dbe7c9070b3b",
    "lon": "7.3547",
    "model": "",
    "name": "Air Evelyne",
    "node_id": "9e876de4b68e3afd",
    "online": true,
    "owner_id": "76341863",
    "product_id": "qzktzhehinzsz2je",
    "product_name": "Air Conditioning",
    "sub": true,
    "time_zone": "+01:00",
    "update_time": 1673086626,
    "uuid": "bf05a85a03d9b07a45dfsj"
  },
  "success": true,
  "t": 1675239917602,
  "tid": "f57dc167a20911ed97b7f276006a160b"
}

while the main device with id = bf67bb67af7aa9b5e4s0eo returns (as you can see it's not a sub device as sub = false)

{
  "result": {
    "active_time": 1673086018,
    "category": "wnykq",
    "category_name": "Universal Remote Control",
    "create_time": 1673086018,
    "gateway_id": "",
    "icon": "smart/icon/ay1525749833414yotNt/af87e60f680f9a7c2aedaa645418ee51.png",
    "id": "bf67bb67af7aa9b5e4s0eo",
    "ip": "82.65.190.118",
    "lat": "47.7584",
    "local_key": "d639dbe7c9070b3b",
    "lon": "7.3547",
    "model": "S06ProWB3S新界面",
    "name": "Smart IR Evelyne",
    "online": true,
    "owner_id": "76341863",
    "product_id": "whs3cty93fzrqkpt",
    "product_name": "Smart IR",
    "sub": false,
    "time_zone": "+01:00",
    "update_time": 1673086651,
    "uuid": "58b7f007ece1f599"
  },
  "success": true,
  "t": 1675241418933,
  "tid": "7458458da20d11ed97b7f276006a160b"
}

Maybe Tuya have defined a new api for those devices ? or did you ever had similar device with "sub" = true sharing the same local_key ?

uzlonewolf commented 1 year ago

Technically they are virtual devices, not sub devices. Unfortunately Tuya does not distinguish between them and there's not much we can do to identify them.

It is not possible to use the virtual IR devices directly. The SmartLife app simply uses them to look up which IR code is needed, and then sends that IR code to the main device.

If you want to transmit IR, just use the main device and do something like

import tinytuya
from tinytuya import Contrib

ir = Contrib.IRRemoteControlDevice( "bf67bb67af7aa9b5e4s0eo", ... )

ir.send_button( ir.pulses_to_base64( ir.nec_to_pulses( 0x20DF23DC ) ) )
# or
ir.send_key( "head", "key" )
tyge68 commented 1 year ago

@uzlonewolf I found this tool https://github.com/k3a/toshiba-ac but I am not sure if that could be used and how to generate the proper nec_to_pulses instructions.

uzlonewolf commented 1 year ago

With how large those transmissions are I would use the head+key format instead. As an example, head = "010ED80000000000030015004000AB" key = "01$$0048F20D03FC0160010060@$" Where key is "01$$0048" + "F20D03FC" + "01600100" + "60" + "@$" (0x0048 = 72 data bits). The above head has been generated using the Samsung protocol parameters.

from tinytuya import Contrib

ir = Contrib.IRRemoteControlDevice( "bf67bb67af7aa9b5e4s0eo", ... )

data = "F20D03FC" + "01600100" + "60"

head = "010ED80000000000030015004000AB"
key = "01$$0048" + data + "@$"
ir.send_key( head, key )

Change data as needed using that toshiba-ac program.

tyge68 commented 1 year ago

@uzlonewolf it works !! I could set the temperature, turn if on/off etc.. so it really matches the code generated by this tool and with the format you suggested. You are really the best :) Now I have to figure out how to integrate it in the LocalTuya (which already uses tinytuya). But that shouldn't be too hard.

We can close this case which hopefully should help also other persons.