clach04 / python-tuya

Python interface to ESP8266MOD WiFi smart devices from Shenzhen Xenon. NOTE I'm not using any devices with this library so I can't test :-(
MIT License
239 stars 55 forks source link

The TCP reset issue #2

Open clach04 opened 6 years ago

clach04 commented 6 years ago

Need more info., see issue #1

Original reported issue by @nijave:

Traceback (most recent call last):
  File "/config/custom_components/switch/tuya.py", line 103, in update
    self._status = self._device.status()
  File "/config/deps/lib/python3.6/site-packages/pytuya/__init__.py", line 223, in status
    data = s.recv(1024)
ConnectionResetError: [Errno 104] Connection reset by peer

Under Linux Python 3

@clach04 saw:

Traceback (most recent call last):
  File "demo_status.py", line 38, in <module>
    data = d.status()
  File "C:\Users\clach04\py\python-tuya_crypt\pytuya\__init__.py", line 228, in status
    data = s.recv(1024)
socket.error: [Errno 10054] An existing connection was forcibly closed by the remote host

This is under python 2.7. Suspect this is the same thing as original report. NOTE I've not seen this under Py3 yet on my machine but I've had long runs when this never occurs.

clach04 commented 6 years ago

@nijave I think the best way forward here is to add logging to your component, in https://github.com/nijave/home-assistant/blob/component-tuya/homeassistant/components/switch/tuya.py in the except block. Dump the exception that is seen.

nijave commented 6 years ago

Here is a zip of a pcap (from Wireshark) that shows a successful connection followed by what appears to be the outlet/Tuya device terminating the next request with a RST,ACK (see https://stackoverflow.com/questions/1434451/what-does-connection-reset-by-peer-mean)

172.16.2.1 is home-assistant 172.16.2.4 is the outlet

Both times it's requesting the status from the device and the requests appear to be exactly the same except for the packet headers

Voion connect followed by reset.zip

nijave commented 6 years ago
Traceback (most recent call last):
  File "/config/custom_components/switch/tuya.py", line 103, in update
    self._status = self._device.status()
  File "/config/deps/lib/python3.6/site-packages/pytuya/__init__.py", line 223, in status
    data = s.recv(1024)
ConnectionResetError: [Errno 104] Connection reset by peer
clach04 commented 6 years ago

I've not had chance to review the pcap file, sorry :(

Recommend we add an exception catch for ConnectionResetError is you are consistently seeing this. As a first step, add that to your HA component (remove the bare except there at the moment).

For a quick test I wrote a script that hammered status checks:

data = d.status()
print('Dictionary %r' % data)
switch_state = data['dps']['1']

data = d.set_status(pytuya.ON)
if data:
    print('set_status() result %r' % data)
    print('set_status() extra %r' % data[20:-8])

data = d.status()
print('Dictionary %r' % data)
switch_state = data['dps']['1']
print('switch_state %r' % switch_state)

data = d.status()
print('Dictionary %r' % data)
switch_state = data['dps']['1']
print('switch_state %r' % switch_state)

data = d.status()
print('Dictionary %r' % data)
switch_state = data['dps']['1']
print('switch_state %r' % switch_state)

data = d.status()
print('Dictionary %r' % data)
switch_state = data['dps']['1']
print('switch_state %r' % switch_state)

data = d.status()
print('Dictionary %r' % data)
switch_state = data['dps']['1']
print('switch_state %r' % switch_state)

I can't get it to fail consistently BUT I'm seeing something different. invalid json (so no severed connection error). I wonder if this is firmware related? If so, we need to deal with it BUT I want to understand any fix we use :-)

  File "demo_status.py", line 38, in <module>
    data = d.status()
  File "C:\Users\clach04\py\python-tuya_clach04\pytuya\__init__.py", line 234, in status
    result = json.loads(result)
  File "C:\ProgramData\Anaconda2\lib\json\__init__.py", line 339, in loads
    return _default_decoder.decode(s)
  File "C:\ProgramData\Anaconda2\lib\json\decoder.py", line 367, in decode
    raise ValueError(errmsg("Extra data", s, end, len(s)))

I'm not going to get chance to work on this again today. I need to dump the payload I'm getting. Continued in issue #6

nijave commented 6 years ago

This one isn't as important since there's a workaround--just annoying. I suspect it may be quite difficult to debug as well. It's possible it depends on the particular plug + firmware.

Someone else was having mostly success and occasional failures: https://github.com/home-assistant/home-assistant/pull/11000#issuecomment-354833205

clach04 commented 6 years ago

Whilst experimenting I also saw:

Traceback (most recent call last):
  File "demo_status.py", line 38, in <module>
    data = d.status()
  File "C:\Users\clach04\py\python-tuya_crypt\pytuya\__init__.py", line 228, in status
    data = s.recv(1024)
socket.error: [Errno 10054] An existing connection was forcibly closed by the remote host

This is under python 2.7. Suspect this is the same thing as original report. NOTE I've not seen this under Py3 yet on my machine, super odd.

clach04 commented 6 years ago

Opened issue #6 for the issue I'm seeing/

clach04 commented 6 years ago

Idea to reproduce error:

ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host

open socket and send garbage?

Exilit commented 6 years ago

I observed the same error when I tried to connect while the eFamilyCloud was opened. Might that be related?

clach04 commented 6 years ago

Linking Issue #11, exception handling for both will be the same

clach04 commented 6 years ago

On what I think is an unstable wifi network (been experimenting, and Alexa can't always consistently turn off a device that was fine on home network, sometimes fails multiple times in a row and I have to wait) seeing errors on connect:

Windows Python 2.7:

  File ".....pytuya\__init__.py", line 167, in _send_receive
    s.connect((self.address, self.port))
  File "C:\ProgramData\Anaconda2\Lib\socket.py", line 228, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 10060] A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond

3.6.1

  File "....\pytuya\__init__.py", line 167, in _send_receive
    s.connect((self.address, self.port))
TimeoutError: [WinError 10060] A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
dadler commented 6 years ago

I believe the devices can connect to only one TCP client at a time. I see "Connection reset by peer" after controlling the device with the Smart Life app. A minute or two later python-tuya starts working again.

Darkyputz commented 6 years ago

Same experience here... But i isolated my plug in my network so that it does not talk to the chineasien servers or phones anymore Only my router is checking for my needs and is talking to it from now on...turning it off and on when my script says so Great enhancement to my life...thx so much

ImNightwing commented 6 years ago

I'm seeing this as well. My experience is with hassbian (Home Assistant)

  1. ha will request the status every 30 seconds
  2. each status request tries 3 times
  3. first attempt USUALLY fails with connection reset then second attempt MIGHT work
  4. a failure of all 3 status requests happens about every 1-2 min.

I tried using socket.sendall with same result Tried using socket.shutdown before closing with same result. Even tried sleeping 2 seconds before socket.recv just in case: same result. I'm stumped

EDIT: I tried lowering the requested status interval to 60 seconds and didn't fail for about 8 min but then it start acting up again. So limiting the amount of request to the device seems to help.

clach04 commented 6 years ago

@ImNightwing reducing number of hits to device is what I ended up doing too. I also have an Amazon Echo hooked up and that talks directly to the device and even then it sometimes does not respond (and its the only thing on the network talking to it). I once had a device get lock up and I had to unplug/replug so the devices themselves whilst good are not 100%

BillSobel commented 6 years ago

I was having trouble with my devices resetting the connection immediately as well, but I found that (for the MS frame work) turning off data buffering had an immediate improvement in the device sending data back. The MS option is https://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.nodelay(v=vs.110).aspx I suspect there is an equivalent option in your frame work. It looks like if the device does not receive data fairly quickly after a connection it resets the connection.

clach04 commented 6 years ago

Thanks @BillSobel, that is odd. I've not seen that under Windows but enabling no delay as there is only one send/recieve makes this a very reasonable/easy change to make. This may help if anyone tries to run under IronPython.

Posted https://github.com/clach04/python-tuya/commit/01c123f899e0ded10554fb0a6fc5e64d20176d30

I'm not using Iron Python but I ran a quick test with IronPython 2.7.7 (2.7.7.0) on .NET 4.0.30319.42000 and it runs fine:

# Demoes everything supported so far

import json
import sys
import time

import logging
log = logging.getLogger('pytuya')

log.setLevel(level=logging.INFO)

import pytuya

f = open('device_info.json')
device_info = f.read()
device_info = json.loads(device_info)
f.close()

f = open('devices.json')
devices = f.read()
devices = json.loads(devices)
f.close()

for uuid in devices:
    device_info[uuid]['ip'] = devices[uuid]['ip']

uuid = '?????'  FIXME your id here
d = pytuya.OutletDevice(uuid, device_info[uuid]['ip'], device_info[uuid]['localKey'])

print(d)

data = d.status()
print('Dictionary %r' % data)
switch_state = data['dps']['1']

data = d.set_status(not switch_state)
if data:
    print('set_status() result %r' % data)
    print('set_status() extra %r' % data[20:-8])

data = d.status()
print('Dictionary %r' % data)
switch_state = data['dps']['1']
print('switch_state %r' % switch_state)

data = d.set_timer(3)
if data:
    print('set_status() result %r' % data)
    print('set_status() extra %r' % data[20:-8])

data = d.status()
print('Dictionary %r' % data)
switch_state = data['dps']['1']
print('switch_state %r' % switch_state)

data = d.status()
print('Dictionary %r' % data)
switch_state = data['dps']['1']
print('switch_state %r' % switch_state)

data = d.status()
print('Dictionary %r' % data)
switch_state = data['dps']['1']
print('switch_state %r' % switch_state)

data = d.status()
print('Dictionary %r' % data)
switch_state = data['dps']['1']
print('switch_state %r' % switch_state)

needs config files and device id.

jdanders commented 6 years ago

@dadler - I can confirm that was my problem -- it appears the device can only have one open connection. While I had the app on my phone open, I couldn't make a connection with pytuya. Once I closed the app, I was able to connect again.

guillaumecardon commented 5 years ago

Hello,

I had recently bought HORSKY socket (https://www.amazon.fr/dp/B0774LKY3K/) and it works great ! However, sometimes, i have the TCP issue I have the 7.0.3 version of the library.

  File "/home/pi/.local/lib/python2.7/site-packages/pytuya/__init__.py", line 260, in status
    data = self._send_receive(payload)
  File "/home/pi/.local/lib/python2.7/site-packages/pytuya/__init__.py", line 172, in _send_receive
    data = s.recv(1024)
socket.error: [Errno 104] Connection reset by peer

Some infos if it can help you :

I'm a beginner in Python, but if i can help you, it's a pleasure 👍

mrdarrengriffin commented 4 years ago

Any update on this?