google / python-lakeside

Apache License 2.0
45 stars 18 forks source link

RecursionError: maximum recursion depth exceeded #15

Open ljmerza opened 5 years ago

ljmerza commented 5 years ago

Looks to be failing here

try:
            self.s.send(encrypted_packet)
        except:
            self.connect()
            self.s.send(encrypted_packet)

it f ails to send so then tries to connect and then fails and keeps going until recursion depth error

File "/home/hass/home-assistant/lib/python3.6/site-packages/lakeside/__init__.py", line 90, in send_packet
    self.connect()
  File "/home/hass/home-assistant/lib/python3.6/site-packages/lakeside/__init__.py", line 229, in connect
    return device.connect(self)
  File "/home/hass/home-assistant/lib/python3.6/site-packages/lakeside/__init__.py", line 66, in connect
    self.update()
  File "/home/hass/home-assistant/lib/python3.6/site-packages/lakeside/__init__.py", line 246, in update
    response = self.get_status()
  File "/home/hass/home-assistant/lib/python3.6/site-packages/lakeside/__init__.py", line 242, in get_status
    response = self.send_packet(packet, True)
  File "/home/hass/home-assistant/lib/python3.6/site-packages/lakeside/__init__.py", line 232, in send_packet
    return device.send_packet(self, packet, response)
  File "/home/hass/home-assistant/lib/python3.6/site-packages/lakeside/__init__.py", line 90, in send_packet
    self.connect()
  File "/home/hass/home-assistant/lib/python3.6/site-packages/lakeside/__init__.py", line 229, in connect
    return device.connect(self)
  File "/home/hass/home-assistant/lib/python3.6/site-packages/lakeside/__init__.py", line 66, in connect
    self.update()
  File "/home/hass/home-assistant/lib/python3.6/site-packages/lakeside/__init__.py", line 246, in update
    response = self.get_status()
  File "/home/hass/home-assistant/lib/python3.6/site-packages/lakeside/__init__.py", line 239, in get_status
    packet.sequence = self.get_sequence()
  File "/home/hass/home-assistant/lib/python3.6/site-packages/lakeside/__init__.py", line 235, in get_sequence
    return device.get_sequence(self)
  File "/home/hass/home-assistant/lib/python3.6/site-packages/lakeside/__init__.py", line 115, in get_sequence
    response = self.send_packet(packet, True)
  File "/home/hass/home-assistant/lib/python3.6/site-packages/lakeside/__init__.py", line 232, in send_packet
    return device.send_packet(self, packet, response)
  File "/home/hass/home-assistant/lib/python3.6/site-packages/lakeside/__init__.py", line 73, in send_packet
    cipher = AES.new(bytes(key), AES.MODE_CBC, bytes(iv))
  File "/home/hass/home-assistant/lib/python3.6/site-packages/Crypto/Cipher/AES.py", line 232, in new
    return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs)
  File "/home/hass/home-assistant/lib/python3.6/site-packages/Crypto/Cipher/__init__.py", line 79, in _create_cipher
    return modes[mode](factory, **kwargs)
  File "/home/hass/home-assistant/lib/python3.6/site-packages/Crypto/Cipher/_mode_cbc.py", line 274, in _create_cbc_cipher
    cipher_state = factory._create_base_cipher(kwargs)
  File "/home/hass/home-assistant/lib/python3.6/site-packages/Crypto/Cipher/AES.py", line 102, in _create_base_cipher
    cipher = VoidPointer()
  File "/home/hass/home-assistant/lib/python3.6/site-packages/Crypto/Util/_raw_api.py", line 159, in VoidPointer
    return VoidPointer_cffi()
  File "/home/hass/home-assistant/lib/python3.6/site-packages/Crypto/Util/_raw_api.py", line 150, in __init__
    self._pp = ffi.new("void *[1]")
  File "/home/hass/home-assistant/lib/python3.6/site-packages/cffi/api.py", line 257, in new
    cdecl = self._typeof(cdecl)
RecursionError: maximum recursion depth exceeded
mjg59 commented 5 years ago

Can you change the except: block to

except Exception as e:
    print(e)
    self.connect()
    self.s.send(encrypted_packet)

and see what the exception actually is?

ljmerza commented 5 years ago

Got the error again and logged it. Doesn't seem to be coming from that part. It's coming from here

if response:
    data = self.s.recv(1024)
    if (len(data) == 0):
        self.connect()
        self.s.send(encrypted_packet)
        data = self.s.recv(1024)

response is true but data is empty b'' so then it calls connect again

theryecatcher commented 5 years ago

Hello

Seeing the same error here. The wonder is it was working fine for a month till yesterday. the switch.connect() now fails with the error maximum recursion depth exceeded in __instancecheck__. I am not sure why though. I tried to debug the stacktrace but doesn't help much. It seems to try to connect repeatedly on failure and then errors out.

This is my code which actually doesn't do much

import lakeside
import sys

if len(sys.argv) < 2:
    print('Please provide action On(1) Off(0)')
    sys.exit()

action = int(sys.argv[1])

user = 'username'
passwd = 'password' 
try:
    devices = lakeside.get_devices(username=user, password=passwd)
    if devices:
        code = devices[0]['code']
        device_type = devices[0]['type']
        device_addr = devices[0]['address']

        sw = lakeside.switch(address=device_addr, code=code, kind=device_type)
        sw.connect()
        if sw.get_status().switchinfo.packet.switchstatus.power == (1-action):
            sw.set_state(power=action)
except Exception as e:
    print(e)

This is the stacktrace

  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 229, in connect
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 66, in connect
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 246, in update
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 242, in get_status
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 232, in send_packet
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 90, in send_packet
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 229, in connect
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 66, in connect
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 246, in update
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 239, in get_status
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 235, in get_sequence
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 115, in get_sequence
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 232, in send_packet
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 73, in send_packet
  File ".local/lib/python2.7/site-packages/pycryptodome-3.8.0-py2.7-linux-armv7l.egg/Crypto/Cipher/AES.py", line 232, in new
    return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs)
  File ".local/lib/python2.7/site-packages/pycryptodome-3.8.0-py2.7-linux-armv7l.egg/Crypto/Cipher/__init__.py", line 79, in _create_cipher
    return modes[mode](factory, **kwargs)
  File ".local/lib/python2.7/site-packages/pycryptodome-3.8.0-py2.7-linux-armv7l.egg/Crypto/Cipher/_mode_cbc.py", line 293, in _create_cbc_cipher
    return CbcMode(cipher_state, iv)
  File ".local/lib/python2.7/site-packages/pycryptodome-3.8.0-py2.7-linux-armv7l.egg/Crypto/Cipher/_mode_cbc.py", line 95, in __init__
    self._state = VoidPointer()
  File ".local/lib/python2.7/site-packages/pycryptodome-3.8.0-py2.7-linux-armv7l.egg/Crypto/Util/_raw_api.py", line 160, in VoidPointer
    return VoidPointer_cffi()
  File ".local/lib/python2.7/site-packages/pycryptodome-3.8.0-py2.7-linux-armv7l.egg/Crypto/Util/_raw_api.py", line 151, in __init__
    self._pp = ffi.new("void *[1]")
  File "/usr/lib/python2.7/dist-packages/cffi/api.py", line 247, in new
    if isinstance(cdecl, basestring):
RuntimeError: maximum recursion depth exceeded in __instancecheck__
theryecatcher commented 5 years ago

Hello

Seeing the same error here. The wonder is it was working fine for a month till yesterday. the switch.connect() now fails with the error maximum recursion depth exceeded in instancecheck. I am not sure why though. I tried to debug the stacktrace but doesn't help much. It seems to try to connect repeatedly on failure and then errors out.

This is my code which actually doesn't do much

import lakeside
import sys

if len(sys.argv) < 2:
    print('Please provide action On(1) Off(0)')
    sys.exit()

action = int(sys.argv[1])

user = 'username'
passwd = 'password' 
try:
    devices = lakeside.get_devices(username=user, password=passwd)
    if devices:
        code = devices[0]['code']
        device_type = devices[0]['type']
        device_addr = devices[0]['address']

        sw = lakeside.switch(address=device_addr, code=code, kind=device_type)
        sw.connect()
        if sw.get_status().switchinfo.packet.switchstatus.power == (1-action):
            sw.set_state(power=action)
except Exception as e:
    print(e)

This is the stacktrace

  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 229, in connect
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 66, in connect
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 246, in update
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 242, in get_status
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 232, in send_packet
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 90, in send_packet
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 229, in connect
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 66, in connect
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 246, in update
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 239, in get_status
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 235, in get_sequence
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 115, in get_sequence
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 232, in send_packet
  File "build/bdist.linux-armv7l/egg/lakeside/__init__.py", line 73, in send_packet
  File ".local/lib/python2.7/site-packages/pycryptodome-3.8.0-py2.7-linux-armv7l.egg/Crypto/Cipher/AES.py", line 232, in new
    return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs)
  File ".local/lib/python2.7/site-packages/pycryptodome-3.8.0-py2.7-linux-armv7l.egg/Crypto/Cipher/__init__.py", line 79, in _create_cipher
    return modes[mode](factory, **kwargs)
  File ".local/lib/python2.7/site-packages/pycryptodome-3.8.0-py2.7-linux-armv7l.egg/Crypto/Cipher/_mode_cbc.py", line 293, in _create_cbc_cipher
    return CbcMode(cipher_state, iv)
  File ".local/lib/python2.7/site-packages/pycryptodome-3.8.0-py2.7-linux-armv7l.egg/Crypto/Cipher/_mode_cbc.py", line 95, in __init__
    self._state = VoidPointer()
  File ".local/lib/python2.7/site-packages/pycryptodome-3.8.0-py2.7-linux-armv7l.egg/Crypto/Util/_raw_api.py", line 160, in VoidPointer
    return VoidPointer_cffi()
  File ".local/lib/python2.7/site-packages/pycryptodome-3.8.0-py2.7-linux-armv7l.egg/Crypto/Util/_raw_api.py", line 151, in __init__
    self._pp = ffi.new("void *[1]")
  File "/usr/lib/python2.7/dist-packages/cffi/api.py", line 247, in new
    if isinstance(cdecl, basestring):
RuntimeError: maximum recursion depth exceeded in __instancecheck__

For anyone facing this issue I just plugged out and plugged the device back in and it started behaving normally back again. I think there might be some state where the device still works on wifi with the app but errors out on socket connections.

AISavvyToday commented 4 years ago

you could also increase limit with:

import sys sys.setrecursionlimit(10**6)

turboaaa commented 4 years ago

I ran into this problem today. Solution put forth by @theryecatcher worked. Power cycling the power switch allowed my scripts to connect to the switch again.

Not sure if there is anything we can actually do about it. This just tells me I need to take the next step in my home automation and start using z-wave.

[root@docker01 eufy]# docker logs -f eufy_filter
maximum recursion depth exceeded
maximum recursion depth exceeded
maximum recursion depth exceeded
maximum recursion depth exceeded
[Errno 104] Connection reset by peer
Checking for subscription
('PM2.5 is ', '39.9')
('Switch is ', '1')
PM2.5 is too high
Fan is already on. Sleeping
mitchsw commented 3 years ago

I hit this rare error today too. I suppose the Eufy device or Python socket wedges itself in some manner which requires a restart. I added logging to the exception catch, and will update if I see it again.

Nonetheless, this connect() stack overflow is undoubtably a bug:

connect() -> update() -> get_status() -> send_packet(response=True) -> connect()

If send_packet() gets a zero-length response from get_status(), it will call connect(). This is an endless loop.

mitchsw commented 3 years ago

Digging into the root cause, forgive my rough logging, but here is the TCP session of the T1012 light in the bad state:

Send packet: 
sequence: 2519593
code: "6DC4F44B922144A4"
ping {
  type: 0
}
Recv 16 bytes
Decrypted packet:
sequence: 2337970683
code: ""
ping {
  type: 1
}

Send packet lakeside:
sequence: 2337970684
code: "6DC4F44B922144A4"
bulbinfo {
  type: 1
}
Recv 0 bytes
Recv attempt 2
Recv attempt 3
Recv attempt 4
Expected response, but no data received!

It appears the bulb is responding to a ping packet, but not a bulbinfo packet. This is repeatable (I haven't turned my light off). The light does work fine from the Eufy app, but not from lakeside.

mitchsw commented 3 years ago

Final update: sniffing the Eufy Android app (and using the lakeside AES keys), I can confirm the bulb also wasn't responding to bulbinfo packets from the app. This did result in state inconsistencies in the official app (e.g. if you restart the app, it didn't know the correct light state). Once the bulb was turned off/on, both the app and lakeside worked fine.

So in conclusion, this is a Eufy device bug that wedged the bulb. (My T1012 is running firmware version 3.7.) That said, even when the bulb is unable to report its state, it still responds to state changes. I wonder if lakeside could be more robust to unknown state, but at this point it may be wishful thinking. :)

DoctorOctagonapus commented 2 years ago

I'm getting a different but I think related error. I've been getting a stacktrace fairly recently that's so long I can't paste it here without crashing my browser, so I've put it in Pastebin: https://pastebin.com/APAG76Np

I have three T1012s running the latest firmware. I'm gonna be honest, the official HA integration with this API has never worked as far as I can tell though the devs have never acknowledged this; I made a custom version with some extra dependencies defined in manifest.json which improves things somewhat to about a 50% failure rate, but the Android app has always worked perfectly so I don't think there's an issue there.