jasonacox / tinytuya

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

Cannot connect to Smart Switch #507

Open measwel opened 3 months ago

measwel commented 3 months ago

Good day,

The device shows up in the scan. The local key has been fetched with the wizard.

enlarger Product ID = keyjup78v54myhan [Valid Broadcast]: Address = 192.168.51.246 Device ID = bfadddf28e43521d4c7man (len:22) Local Key = xxxxxxxxx Version = 3.3 Type = default, MAC = 10:5a:17:dc:fd:32 Status: {'1': True, '9': 0, '18': 0, '19': 0, '20': 2346, '21': 1, '22': 610, '23': 30696, '24': 17327, '25': 2410, '26': 0, '38': 'memory', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}

I read through all the issues concerning 901 connection errors, but not found a solution.

DEBUG:ERROR Network Error: Unable to Connect - 901 - payload: null DEBUG:status() received data={'Error': 'Network Error: Unable to Connect', 'Err': '901', 'Payload': None}

I have 2 clues.

1 : the core.py throws an error:

File "...\AppData\Local\Programs\Python\Python312\Lib\site-packages\tinytuya\core.py", line 1009, in _get_socket if self.version >= 3.4: ^^^^^^^^^^^^^^^^^^^ TypeError: '>=' not supported between instances of 'str' and 'float'

2 : maybe other active connections are blocking access to the device?

I have a smart life app on my phone which shows the device. Disconnecting the device in the app does not work. When I disconnect without wiping data, the switch still loses its wifi settings. I have to repair it in the app to get it to show on the wifi network again.

Just closing the smart life app on the phone also does not help. Could it be, because other home members also have that app on their phone and are possibly making a connection? If so, how should I solve that? Should I remove the device from all the smart life apps?

Thank you.

So I do not know how to make sure no other active connections are present.

uzlonewolf commented 3 months ago

Yes, the force-scan does something similar: it waits a few seconds to look for broadcast packets, and then starts trying every IP address it did not hear a broadcast from. It goes one step further and uses non-blocking sockets and select() to interrogate 10 devices in parallel every main loop tick (shakes out to around 100 simultaneous requests). Once a connection is established it starts trying every device version and local key combination (minus the keys it already found) until it gets a match.

uzlonewolf commented 3 months ago

I noticed it made no difference whether I used outlet or bulb for the connection test.

OutletDevice is going to be a little quicker as the BulbDevice makes a connection and calls status() to try and figure out what bulb type it is as soon as the BulbDevice() object is created.

Personally I prefer to use tinytuya.Device() when the device type is not known as I feel it makes it a bit more obvious when reading the code that the device type is not known yet.

uzlonewolf commented 3 months ago

I'm not too hopeful, but, if you run this in one terminal:

import tinytuya
import socket
import time

bcast = b'{"from":"app","ip":"192.168.51.1"}'

bcast_port = 7000
bcast_to = '255.255.255.255'
bcast_idx = 0
bcast_cmd = 0x25
bcast_retcode = None
bcast_msg = tinytuya.TuyaMessage( bcast_idx, bcast_cmd, bcast_retcode, bcast, 0, True, tinytuya.PREFIX_6699_VALUE, True )
bcast_packet = tinytuya.pack_message( bcast_msg, hmac_key=tinytuya.udpkey )

bsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
bsock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

while True:
    print("broadcast encrypted=%r" % bcast_packet.hex() )
    bsock.sendto( bcast_packet, (bcast_to, bcast_port) )
    bsock.sendto( bcast_packet, (bcast_to, bcast_port) )
    time.sleep(5)

Does python3 -m tinytuya scan in another then pick up those new bulbs?

measwel commented 3 months ago

Okay, the Terminal was firing broadcast messages such as: broadcast encrypted='xxxxxxx'

In the other Terminal I did a scan:

Unknown v3.4 Device Product ID = tgkr1bwom0jcn3cx [Valid Broadcast]: Address = 192.168.51.15 Device ID = bf01e82420703431551cwm (len:22) Local Key = Version = 3.4 Type = default, MAC = No Stats for 192.168.51.15: DEVICE KEY required to poll for status New Broadcast from App at 192.168.51.4 - {'from': 'app', 'ip': '192.168.51.1'} Scan completed in 18.0288 seconds

Scan Complete! Found 1 devices. Broadcasted: 1 Versions: 3.4: 1 Unknown Devices: 1

The bulbs and the outlet switch were not found. What it did find, (192.168.51.15) is the light intensity sensor. But that thing was also found by a regular scan before.

uzlonewolf commented 3 months ago

Yeah, I figured that wasn't going to work, but thought it was worth a shot. I ordered those AliExpress devices and they should get here today so hopefully I'll be able to figure out what's going on.

uzlonewolf commented 3 months ago

My devices came in and I made some progress. The problem with these new devices is 2-fold: they do not broadcast at all unless they receive a "from: app" broadcast on port 7000, and they broadcast to port 7000. Receiving their broadcasts is a minor change to the scanner as it already listens on port 7000 for apps, but sending a broadcast to get the devices to start broadcasting is turning out to be a bit trickier than I thought.

measwel commented 3 months ago

Hey @uzlonewolf, interesting results so far. I hold my thumbs for you to find a good way to get them to broadcast. Maybe a useful hint, maybe not: I noticed they do return a ping, so maybe one could use that to filter what IPs to try on the network.

uzlonewolf commented 3 months ago

There's no point to pinging them, just attempting a TCP connection with a short time-out does the same thing while also making it so the 2nd step is ready to go should the connection succeed. It currently creates parallel connections to about 50 IP addresses per second, so attempting connections to an entire /24 only takes ~8 seconds (5.12 seconds to open connections plus 3 seconds for the time-out if there's no response from the last IP).

I'll probably be going out of town early next week so I may or may not be able to get sending broadcasts implemented this weekend. It's pretty easy to do on machines which only have 1 IP address, but there's no good way to get a list of IPs in python on multi-interface machines.

measwel commented 3 months ago

Yeah, got that. Enjoy your time outside of town.