h4 / lywsd02

MIT License
169 stars 34 forks source link

Execution stops #26

Open lubbifo opened 3 years ago

lubbifo commented 3 years ago

Hi there,

I use your python library for Openhab integration. Frequently it happens, that this external program gets stuck. I then see the following in the process list:

user  20821  0.0  0.0   1468   244 ?        S    12:11   0:00 sh -c /mijia/mijia_temp read a4:c1:38:41:xxxx
user  20823  0.4  0.3  13936  6492 ?        S    12:11   0:02 python3 /mijia/mijia_temp read a4:c1:38:41:xxxx

It stays there forever.... This is how I use the library (derived from your demo code):

client = lywsd02.Lywsd02Client(mac, notification_timeout=30.0)
data = client.data
print('{:.1f}C {}%'.format(data.temperature,data.humidity))

The device I am using is this:

https://goodereader.com/blog/uploads/images/Screenshot_2020-05-27-C9-86-20-OFF-Newest-Xiaomi-Mijia-Bluetooth-Temperature-humidity-2-Wireless-Smart-Electric-Digital....png

I am using library version 0.0.9 on Python 3.7.3

I understand, that the connectivity to the device is noisy and unreliable. However, the code should return a result or an error after the timeout, but not stuck.

Am I doing anything wrong? Any ideas?

P.S.: "timeout 40 lywsdo2.python ...." is a good workaround

h4 commented 3 years ago

Very strange. Client should fail after 30 sec timeout. I'll try to get similar device and check.

CeKl commented 3 years ago

Hi there,

i also tried to work with the LYWSD03MMC like @lubbifo describes. For about 2 - 4 hours everything works without a problem in a Python script, similar to the example. However, for some reason not yet found, the program freezes. So I have the same problem.

I used the libary in version 0.0.9 with Python 3.8.6 on a Raspberry Pi 3+

Maybe by now there are some ideas or a way to get to the exception, which is normally supposed to show up after 30 seconds as described? If it works though, the libary functions top.

CeKl commented 3 years ago

As a workaround I am currently using signal to abort the request after a set time.

import signal
from lywsd02 import Lywsd02Client

device_mac = 'AA:BB:CC:DD:EE:FF'

class TimeoutException(Exception):
    pass

def timeout_handler(signum, frame):
    raise TimeoutException

signal.signal(signal.SIGALRM, timeout_handler)

while True:
    signal.alarm(20)
    try:
        client = Lywsd02Client(device_mac, notification_timeout=5.0)
        with client.connect():
            data = client.data
            print(data.temperature)
            print(data.humidity)
            print(client.battery)

    except TimeoutException:
        continue

    except Exception as e:
        print(e)

    else:
        signal.alarm(0)

So far I have not found why the bluepy-helper freezes as a process or does nothing more at full CPU load. Maybe this helps someone until the problem is solved.

a-lost-shadow commented 3 years ago

I've been running into the same problem. Looking at the code, I noticed that Lywsd02Client.connect() call does not provide a timeout when it calls Peripheral.connect() in the bluepy library. In that case, it looks like bluepy will send a message to begin the connection, and then go into an infinite loop waiting for a response. So I think this is what's causing the failures we're seeing.

I'm going to try and get a remote debugger setup on my program so I can confirm my suspicion.

a-lost-shadow commented 3 years ago

Turns out it was locking up in the call to getCharacteristic(). Here's the code path that was failing after I added a timeout:

File "/home/iot_debug/mijia/monitor.py", line 76, in read_sensor
  return sensorConnection.data, datetime.datetime.now()
File "/home/iot_debug/mijia/lywsd02/client.py", line 71, in data
  self._get_sensor_data()
File "/home/iot_debug/mijia/lywsd02/client.py", line 159, in _get_sensor_data
  self._subscribe(UUID_DATA, self._process_sensor_data)
File "/home/iot_debug/mijia/lywsd02/client.py", line 182, in _subscribe
  ch = self._peripheral.getCharacteristics(uuid=uuid, timeout=5.0)[0]
File "/home/iot_debug/mijia/bluepy/btle.py", line 539, in getCharacteristics
  rsp = self._getResp('find', timeout)

This ran successfully for 30 hours before I started to poke it again (previously I was seeing failures in the 2-6 hour range). I'm pretty sure I saw 6 timeouts over that duration, but I accidentally ate the timeout exception without properly logging it.

Unfortunately to get this to work, I needed to modify bluepy. I'm going to run a longer term test before submitting a pull request to bluepy. I'll submit a pull request here if/when the bluepy one is accepted.

In the meantime, you can see my changes to bluepy and lywsd02 at: