jasonacox / tinytuya

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

Auto address detection fails for older device versions in TinyTuya 1.12.9 #478

Open yokoyama-flogics opened 3 months ago

yokoyama-flogics commented 3 months ago

Thank you for the wonderful software. I have a simple question.

Previously, I was able to successfully use the call tinytuya.OutletDevice(dev_id=..., address="Auto", local_key=..., version=3.3) with devices of version 3.3 (with auto detection of the address).

However, when I attempt the same call with a slightly older device (version 3.1), I receive the following error and cannot connect:

Unable to find device on network (specify IP address)

However, everything works fine when I specify the actual IP address:

set_status() result {'devId': 'xxxxxxxx', 'dps': {'1': True, '2': 0}}

Yet, when I use python -m tinytuya scan, I can correctly detect the device as follows:

a_smart_plug Product ID = xxxxxxxx [Valid Broadcast]: Address = 192.168.xxx.xxx Device ID = xxxxxxxx (len:20) Local Key = xxxxxxx Version = 3.1 Type = default, MAC = xx:xx:xx:xx:xx:xx Status: {'1': True, '2': 0}

What is the fundamental difference here? Why does python -m tinytuya scan work, but tinytuya.OutletDevice(..., address="Auto", ...) does not?

I am using tinytuya version 1.12.9.

Atsushi

yokoyama-flogics commented 3 months ago

I'm speculating, but from looking at the output of python -m tinytuya scan, it seems like it detects devices by sending unicast UDP packets to every (?) IP address in the subnet. On the other hand, tinytuya.OutletDevice(address="Auto") seems to perform the scan using broadcast or something similar. Is this assumption correct?

Atsushi

uzlonewolf commented 3 months ago

Hi @yokoyama-flogics !

It is the device which sends UDP broadcasts, TinyTuya only passively listens for them.

Looking at the code it would seem I broke find_device() at some point for the older v3.1 devices. I'll try to get that fixed soon.

uzlonewolf commented 3 months ago

Re-reading it I see my last post wasn't very clear. Both python -m tinytuya scan and Device(address='Auto') work the same way: TinyTuya passively listens for a UDP broadcast from the device, and then makes a unicast TCP connection once it's found. TinyTuya does not probe every IP address on the subnet unless you pass the -force flag to the scanner.

yokoyama-flogics commented 3 months ago

Hi @uzlonewolf,

Thank you for looking into this. I will also try to review the code more closely when I have the time. As a current workaround, I'm using tinytuya.deviceScan() to perform a scan once and then retrieve the IP address (which works for me).

Atsushi

uzlonewolf commented 3 months ago

Hmm, looking at this a bit more, I'm actually not seeing a problem anywhere. I tested with the 3 v3.1 devices I have and all 3 of them are working fine with address=Auto.

Can you run a scan with debug turned on (python3 -m tinytuya scan -d) and post the DEBUG:Received valid UDP packet: { ... } line for the problem device?

yokoyama-flogics commented 3 months ago

Hi @uzlonewolf,

I tried it out. Will this information be helpful?

DEBUG:Received valid UDP packet: {'ip': '172.21.141.104', 'gwId': '02200286dc4f220ec7d6', 'active': 2, 'ability': 0, 'mode': 0, 'encrypt': True, 'productKey': 'PGEkBctAbtzKOZng', 'version': '3.1'}
DEBUG:Received valid UDP packet: {'ip': '172.21.141.104', 'gwId': '02200286dc4f220ec7d6', 'active': 2, 'ability': 0, 'mode': 0, 'encrypt': True, 'productKey': 'PGEkBctAbtzKOZng', 'version': '3.1'}
DEBUG:Received valid UDP packet: {'ip': '172.21.141.104', 'gwId': '02200286dc4f220ec7d6', 'active': 2, 'ability': 0, 'mode': 0, 'encrypt': True, 'productKey': 'PGEkBctAbtzKOZng', 'version': '3.1'}
DEBUG:Received valid UDP packet: {'ip': '172.21.141.104', 'gwId': '02200286dc4f220ec7d6', 'active': 2, 'ability': 0, 'mode': 0, 'encrypt': True, 'productKey': 'PGEkBctAbtzKOZng', 'version': '3.1'}
DEBUG:Received valid UDP packet: {'ip': '172.21.141.104', 'gwId': '02200286dc4f220ec7d6', 'active': 2, 'ability': 0, 'mode': 0, 'encrypt': True, 'productKey': 'PGEkBctAbtzKOZng', 'version': '3.1'}
DEBUG:Received valid UDP packet: {'ip': '172.21.141.104', 'gwId': '02200286dc4f220ec7d6', 'active': 2, 'ability': 0, 'mode': 0, 'encrypt': True, 'productKey': 'PGEkBctAbtzKOZng', 'version': '3.1'}

Atsushi

jasonacox commented 3 months ago

Atsushi, are you still ale to get "Auto" to work on 3.3 devices? And, are you by chance running your script in a container (e.g. docker)?

yokoyama-flogics commented 3 months ago

Yes, I've confirmed that with the same Python environment (using pyenv), I can get a response with "Auto" for devices that are version 3.3.

set_status() result {'devId': '72363820c4dd5703cd6d', 'dps': {'1': False, '9': 0, '18': 0, '19': 0, '20': 1003, '21': 1, '22': 746, '23': 31250, '24': 21052, '25': 990}}

However, it was necessary to include version=3.3 as an argument in tinytuya.OutletDevice(). Without it, I encountered an error:

set_status() result {'Error': 'Timeout Waiting for Device', 'Err': '902', 'Paylo
ad': 'Check device key or version'}

I tried adding version= (3.1 and 3.3) for the 3.1 devices as well, but there was no change.

For your reference, here is the output of pip freeze:

certifi==2023.7.22
charset-normalizer==3.2.0
colorama==0.4.6
elasticsearch==6.8.2
idna==3.4
paho-mqtt==1.6.1
pycryptodome==3.18.0
requests==2.31.0
schedule==1.2.0
tinytuya==1.12.9
urllib3==1.26.5

Python version is 3.9.11.

Atsushi