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

Device with 22id doesn't work #240

Open Izzami opened 1 year ago

Izzami commented 1 year ago

I tried the fix but tinytuya wizard report:

Product ID = keyxet94msd94uvq  [Valid payload]:
    Address = 192.168.1.7,  Device ID = bf534c9207553e222bjcki, Local Key = "LOCAL_KEY",  
    Version = 3.3,  MAC = 68:57:2d:25:be:9a Unexpected error for 192.168.1.7: Unable to poll

This code report "None"

import tinytuya
a = tinytuya.OutletDevice('bf534c9207553e222bjcki', '192.168.1.7', 'my_local_key', 'device22')
a.set_version(3.3)
a.set_dpsUsed({"1": None})
data =  a.status()
print(data)
jasonacox commented 1 year ago

Hi @Izzami - What type of device is this?

Izzami commented 1 year ago

Hi @Izzami - What type of device is this?

it's a RGBW wifi led strip

jasonacox commented 1 year ago

I should have asked, what version of TinyTuya are you using (python3 -m tinytuya version)? You should be on 1.91. Older version did have issues with the device22 variant but the newer versions should auto-detect and adjust for them. Also, are you able to successful manage other Tuya devices with TinyTuya? I would also verify that your local machine or network is not blocking TCP connections to port 6668 (unlikely but there have been cases).

I had LED strips and Smart Bulbs that would have difficulty connecting but often connected on a retry. In one case, a firmware update was available and fixed the behavior.

This shouldn't make a difference since the status() call you are making is the same, but I would try connect to it as a smart bulb:

import tinytuya

# Add additional debug info
tinytuya.set_debug(True)

d = tinytuya.BulbDevice(DEVICEID, DEVICEIP, DEVICEKEY)
d.set_version(3.3)

# Keep socket connection open between commands
d.set_socketPersistent(True)  

# Show status of device
data = d.status()
print('\nCurrent Status of Bulb: %r' % data)
Izzami commented 1 year ago

I'm on 1.9.1 Trying with BuldDevice instead of OutletDevice report this:

DEBUG:TinyTuya [1.9.1]

DEBUG:status() entry (dev_type is device22)
DEBUG:building command 10 payload=b'{"devId":"bf534c9207553e222bjcki","uid":"bf534c9207553e222bjcki","t":"1671547666","dps":{}}'
DEBUG:sending payload
DEBUG:payload encrypted=b'000055aa000000010000000d00000077332e33000000000000000000000000adf71e57c07630fade3cc019b3e80d87ffe2cf402f5adc6cd0d60e2df96364fcd092ca6d035e06fa7babd4a758cdbc026db2fd86bb538e77a260ceb3d30b1e06ec23dba2320361a339300b430e2c57d24e45929550c7f20b589eb4d7d4b9b6c9ff7cb0cb0000aa55'
DEBUG:received data=b'000055aa000000010000000d0000000c00000000302b238a0000aa55'
DEBUG:received null payload (TuyaMessage(seqno=1, cmd=13, retcode=0, payload=b'', crc=808133514, crc_good=True)), fetch new one - retry 0 / 5
DEBUG:status() received data=None
DEBUG:bulb type set to B
DEBUG:status() entry (dev_type is device22)
DEBUG:building command 10 payload=b'{"devId":"bf534c9207553e222bjcki","uid":"bf534c9207553e222bjcki","t":"1671547671","dps":{}}'
DEBUG:sending payload
DEBUG:payload encrypted=b'000055aa000000020000000d00000077332e33000000000000000000000000adf71e57c07630fade3cc019b3e80d87ffe2cf402f5adc6cd0d60e2df96364fcd092ca6d035e06fa7babd4a758cdbc026db2fd86bb538e77a260ceb3d30b1e065d18ad0e4e201d90f645fcd2595ee6314e45929550c7f20b589eb4d7d4b9b6c9419a033c0000aa55'
DEBUG:received data=b'000055aa000000020000000d0000000c000000008de14f440000aa55'
DEBUG:received null payload (TuyaMessage(seqno=2, cmd=13, retcode=0, payload=b'', crc=2380353348, crc_good=True)), fetch new one - retry 0 / 5
DEBUG:status() received data=None 
jasonacox commented 1 year ago

It does look like TinyTuya is detecting a device22 device. Other users have reported devices getting into this "None response" state and a power cycle helped. I assume you already attempted that. You might try this monitoring loop to see if the device is sending anything:

d = tinytuya.OutletDevice('DEVICEID', 'DEVICEIP', 'DEVICEKEY')
d.set_version(3.3)
d.set_socketPersistent(True)

print(" > Send Turn Off Command <")
payload = d.generate_payload(tinytuya.CONTROL, {'1': False})
d.send(payload)

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)
resetpointer commented 1 year ago

Hi, I have an LED strip and testing with tinytuya version 1.9.1

What I find is that:

  1. A tuya device reported to support api protocol version 3.2, however, using the reported api version wouldn't work. Communication to the device only work when apiversion is set to 3.3 here is the debug log when version 3.2 is used:

DEBUG:status() entry (dev_type is device22) DEBUG:building command 10 payload=b'{"devId":"....","uid":".....","t":"1671559811","dps":{"1":null,"2":null,"3":null,"4":null,"5":null,"6":null,"7":null,"8":null,"9":null,"10":null}}' DEBUG:sending payload DEBUG:payload encrypted=b'......................' DEBUG:received data=b'000055aa000000010000000d0000000c00000000302b238a0000aa55' DEBUG:received null payload (TuyaMessage(seqno=1, cmd=13, retcode=0, payload=b'', crc=808133514, crc_good=True)), fetch new one - retry 0 / 5 DEBUG:status() received data=None Traceback (most recent call last): File "/etc/openhab/tools/tuya_command.py", line 52, in d.set_version(apiver) File "/etc/openhab/tests/tinytuya/tinytuya/core.py", line 1246, in set_version self.detect_available_dps() File "/etc/openhab/tests/tinytuya/tinytuya/core.py", line 1221, in detect_available_dps if "dps" in data: TypeError: argument of type 'NoneType' is not iterable


2.  If I changed apiver to 3.3 without specify devtype="device22", tinytuya does not detect the correct device type, and so it would not work.

3.  The only way I could get it to work was by expliciting setting apiver to 3.3 and devicetype to "device22"

a = tinytuya.OutletDevice('bf534c9207553e222bjcki', '192.168.1.7', 'my_local_key', 'device22') a.set_version(3.3)



There is something seriously wrong with this approach.  Secondly, why wouldn't TinyTuya auto detect devicetype when apiversion is set to 3.3?

thanks
Izzami commented 1 year ago

It does look like TinyTuya is detecting a device22 device. Other users have reported devices getting into this "None response" state and a power cycle helped. I assume you already attempted that. You might try this monitoring loop to see if the device is sending anything:

d = tinytuya.OutletDevice('DEVICEID', 'DEVICEIP', 'DEVICEKEY')
d.set_version(3.3)
d.set_socketPersistent(True)

print(" > Send Turn Off Command <")
payload = d.generate_payload(tinytuya.CONTROL, {'1': False})
d.send(payload)

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)

every time I do something using the strip button it's reported:

Send Turn Off Command < Begin Monitor Loop < Received Payload: None Send Heartbeat Ping < Received Payload: None Send Heartbeat Ping < Received Payload: None Send Heartbeat Ping < Received Payload: None Send Heartbeat Ping < Received Payload: None Send Heartbeat Ping < Received Payload: {'dps': {'20': False}, 't': 1671568264} Send Heartbeat Ping < Received Payload: {'dps': {'20': True}, 't': 1671568267} Send Heartbeat Ping < Received Payload: None Send Heartbeat Ping < Received Payload: {'dps': {'21': 'colour'}, 't': 1671568274} Send Heartbeat Ping < Received Payload: {'dps': {'24': '007803e803e8'}, 't': 1671568277} Send Heartbeat Ping < Received Payload: {'dps': {'24': '00f003e803e8'}, 't': 1671568279} Send Heartbeat Ping < Received Payload: {'dps': {'21': 'scene', '25': '05646401000003e803e80000000064640100000000000000000000'}, 't': 1671568280} Send Heartbeat Ping < Received Payload: {'dps': {'25': '04515102007803e803e80000000051510200000000000000000000'}, 't': 1671568281} Send Heartbeat Ping < Received Payload: {'dps': {'25': '06646401000003e803e80000000064640100000000000000000000646401007803e803e8000000006464010000000000000000000064640100f003e803e80000000064640100000000000000000000'}, 't': 1671568283} Send Heartbeat Ping < Received Payload: {'dps': {'25': '073c3c02000003e803e8000000003c3c02001d03e803e8000000003c3c02003c03e803e8000000003c3c02007803e803e8000000003c3c0200b403e803e8000000003c3c0200f003e803e8000000003c3c02012c03e803e800000000'}, 't': 1671568284} Send Heartbeat Ping < Received Payload: {'dps': {'21': 'white'}, 't': 1671568284} Send Heartbeat Ping < Received Payload: {'dps': {'20': False}, 't': 1671568288} Send Heartbeat Ping < Received Payload: {'dps': {'20': True}, 't': 1671568290} Send Heartbeat Ping <

Izzami commented 1 year ago

I've tried changing the dps setting: import tinytuya a = tinytuya.OutletDevice('bf534c9207553e222bjcki', '192.168.1.7', '8112077749352134', 'device22') a.set_version(3.3) a.set_dpsUsed({"24": None}) data = a.status() print(data)

output: {'dps': {'24': '000003e803e8'}, 't': 1671568645}

Izzami commented 1 year ago

Updates: I succeded in controlling the device using dps: import tinytuya a = tinytuya.OutletDevice('bf534c9207553e222bjcki', '192.168.1.7', '8112077749352134', 'device22') a.set_version(3.3) a.set_dpsUsed({"20": None}) a.set_value(20, True) a.set_value(21, 'white') data = a.status() print(data)

jasonacox commented 1 year ago

Nice work @Izzami !

@resetpointer, I agree with you. The device22 hack is not clean. It seems that those devices are using a firmware that is somewhere between the 3.2 and 3.3 protocol (like an alpha version of 3.3 but still use a lot of 3.2 commands). Most are much closer to 3.2 which isn't surprising that your device announces itself as a 3.2.

You're particular device is very interesting. The 3.2 protocol devices @uzlonewolf and I have found (to be fair, not a lot) responded well to the detect_available_dps() but you are showing a None response in the data payload. We could introduce more error handling, but I still want to figure out why your 3.2 device isn't working with 3.2. Since 3.2 does a lot of the same things as these mystery device22 devices, I suspect there may be an indication of a problem we can address. Also, I do want to find a better way to detect and handle device22 edge cases anyway.

@resetpointer what is the LED strip you are using? I'm wondering if I can get one for testing.

Izzami commented 1 year ago

@jasonacox I found the color scheme of my led strip. Here’s the topic: https://github.com/AMoo-Miki/homebridge-tuya-lan/issues/68

resetpointer commented 1 year ago

jason,

The LED strip I have is called Monster illuminessence strip. I got it from a local walmart about 3 years ago.

In an ideal case, I am just curious if if there is a way to probe for apiversion starting from version 3.4 down to 3.1. I know that we are detecting the version from when we scan the network, reference network scanner. I am curious if the reported apiversion can be validated by sending a heartbeat message to the device. I am not an expert here, just making suggestions.

btw, thank you so much for your contributions

jasonacox commented 1 year ago

Thanks @resetpointer !

TinyTuya can do what you request. We don't advertise it very well in the examples or readme since this is a new addition and does take a bit longer on the initial connection. Basically, using "Auto" or None for the IP address will auto-detect the IP address and version:

import tinytuya

d = tinytuya.BulbDevice(DEVICEID, "Auto", DEVICEKEY)
d.set_socketPersistent(True)  

# Show status of device
data = d.status()
print('\nCurrent Status of Bulb: %r' % data)

Additionally, if TinyTuya has access to the devices.json file for DEVICEKEY lookup, you really only need to specify the ID:

d = tinytuya.BulbDevice(DEVICEID)
uzlonewolf commented 1 year ago

@resetpointer No, there is no API command to query the device version, at least that I've been able to find (and I looked pretty hard while rewriting the scanner). The best you can do is profile a device by sending various commands to see how it reacts; i.e. only a v3.4 device will respond to "negotiate session key," only v3.1 allow polling the status without the local key, etc. Now that I think about it, it probably wouldn't be a bad idea to make a function that does this for you.

uzlonewolf commented 1 year ago

@resetpointer I've been trying to find a v3.2 device but have not been having any luck. Would you mind selling me yours? I only need the controller, so if it's easier you can keep the LEDs attached where they are and just replace the controller.

resetpointer commented 1 year ago

@resetpointer I've been trying to find a v3.2 device but have not been having any luck. Would you mind selling me yours? I only need the controller, so if it's easier you can keep the LEDs attached where they are and just replace the controller.

Would it be ok if I give you desktop or remote access to my network to test?

If you must need the light bar, I can donate it to you, no problem. Just go ahead and pm ur address to my inbox. Thanks

uzlonewolf commented 1 year ago

@resetpointer While that would work for my immediate need (making sure my scanner rewrite plays nice with these older devices), I would like to have one on hand so I can verify future updates work as expected. I'm actually hoping to dump the firmware and load it onto additional devices so I can send a couple to @jasonacox too.

It does not appear Github has a PM feature. Please send me an email at [edited] . I don't mind paying you replacement cost plus shipping if you want, I can do PayPal, Venmo, or Zelle, whichever's easier for you. Thanks!