basnijholt / miflora

☘️🌑🌼πŸ₯€πŸ‘ Mi Flora Plant sensor Python package
MIT License
362 stars 99 forks source link

Reading sensor values, blinking lights and lux #136

Open MartinHaimberger opened 4 years ago

MartinHaimberger commented 4 years ago

Hi,

Reading the values from a mi flora sensor works great! But the lux read from the sensor is not correct because while filling the cache the sensor blinks and the blinking distortes the lux reading... in a dark room i get around 30 - 100 lux if the values is read while blinking. Any idea how to disable the blinking or read the lux date if blinking is over or any other idea to get correct readings?

Thx, Martin

ChristianKuehnel commented 4 years ago

If I recall correctly, there is a GATT command to directly control the brightness of the LED. But I no not remember what is was...

MartinHaimberger commented 4 years ago

Any hint where i could find it ?

MartinHaimberger commented 4 years ago

Update: What i have done, to get accurate readings, is insert a sleep of 5 seconds after chancing the mode to realtime and before reading the actual data.

This adds a lot of delay but its ok for me, because the mifloars get pulled every 10 minutes.

A better solution would be turning the blinking off on connecting, but i have not found anything so far.

bangom commented 4 years ago

Hi,

Reading the values from a mi flora sensor works great! But the lux read from the sensor is not correct because while filling the cache the sensor blinks and the blinking distortes the lux reading... in a dark room i get around 30 - 100 lux if the values is read while blinking. Any idea how to disable the blinking or read the lux date if blinking is over or any other idea to get correct readings?

Thx, Martin

Have you considered implementing passive miflora data reading by sniffing all in the air "visible" data, parsing found miflora packets and adding them to the cache? Sensor sends multiple readings per minute without affecting battery life unlike active btle connection. Latest miflora firmware 3.2.1 does not send battery level, but this can be done once a day by active pooling among the firmware version.

Advantages of passive Xiaomi sensors readings: https://github.com/custom-components/sensor.mitemp_bt/blob/master/faq.md#why-is-this-component-called-passive-and-what-does-this-mean

Xiaomi passive BLE Monitor sensor platform for inspiration: https://github.com/custom-components/sensor.mitemp_bt

Passive BTLE Xiaomi sensor reading is also done in ESPhome.

bangom commented 4 years ago

If I recall correctly, there is a GATT command to directly control the brightness of the LED. But I no not remember what is was...

Could you try to find out? Would be great to disable LED blinking while reading from sensor.

bangom commented 4 years ago

Update: What i have done, to get accurate readings, is insert a sleep of 5 seconds after chancing the mode to realtime and before reading the actual data.

This adds a lot of delay but its ok for me, because the mifloars get pulled every 10 minutes.

A better solution would be turning the blinking off on connecting, but i have not found anything so far.

Could you specify which source-code lines you've changed? Patch would be the best :-) Thanks!

MartinHaimberger commented 4 years ago

Hi,

this is just a quitfix in the poller for my setup ... but it works for me. In the file miflora_poller.py (path on my system /usr/local/lib/python3.7/dist-packages/miflora/)

i added following import (import time, line 10)

from datetime import datetime, timedelta
from struct import unpack
import logging
from threading import Lock
from btlewrap.base import BluetoothInterface, BluetoothBackendException
import time

_HANDLE_READ_VERSION_BATTERY = 0x38
...

and in the fill_cache function i added a sleep of 5 seconds after the connection init and the version exchange. (time.sleep(5) line number 79)

...
 def fill_cache(self):
        """Fill the cache with new data from the sensor."""
        _LOGGER.debug('Filling cache with new sensor data.')
        try:
            firmware_version = self.firmware_version()
        except BluetoothBackendException:
            # If a sensor doesn't work, wait 5 minutes before retrying
            self._last_read = datetime.now() - self._cache_timeout + \
                timedelta(seconds=300)
            raise

        with self._bt_interface.connect(self._mac) as connection:
            if firmware_version >= "2.6.6":
                # for the newer models a magic number must be written before we can read the current data
                try:
                    connection.write_handle(_HANDLE_WRITE_MODE_CHANGE, _DATA_MODE_CHANGE)   # pylint: disable=no-member
                    # If a sensor doesn't work, wait 5 minutes before retrying
                except BluetoothBackendException:
                    self._last_read = datetime.now() - self._cache_timeout + \
                        timedelta(seconds=300)
                    return
            time.sleep(5)
            self._cache = connection.read_handle(_HANDLE_READ_SENSOR_DATA)  # pylint: disable=no-member
...

But like i said, this has a few drawbacks, but in my setup, i get correct readings now, because the led stops blinking after 3-4 seconds, but each reading has delay between initiating the read and reading actual data of 5 seconds, which is no problem in my setup.

I hope that helps, Martin

ThomDietrich commented 4 years ago

If this solves the issue and we decide to implement it, let's rather divide the function and let the library user implement the delay.

Side note: Same goes for the handling of exceptions. This should be removed and the responsibility of the user (instead of the ambiguous 5 minutes hack in the background). jm2c seeing the code snippet above

bangom commented 4 years ago

If this solves the issue and we decide to implement it, let's rather divide the function and let the library user implement the delay.

Side note: Same goes for the handling of exceptions. This should be removed and the responsibility of the user (instead of the ambiguous 5 minutes hack in the background). jm2c seeing the code snippet above

@ThomDietrich, I've tried modifying miflora library couple days ago with same hack to mitigate led blinking, but for some strange reason miflora-mqtt-daemon which uses this library failed the step "Adding sensor to device list and testing connection". I have to test again with lastest miflora-mqtt-daemon git version. But I've suspected that miflora sensor doesn't like delay between "magic number write" and actual btle sensor reading...

Of course it is best to divide the mentioned ideas into two tasks: a) (easy tasks) Implement user configurable delay between led blinking and lux reading in miflora library... or find out how to disable led completely (@ChristianKuehnel?) b) (harder) Implement passive miflora sensor reading: whenever there is miflora sensor broadcast in the air, add sensor reading into miflora library cache. If user request sensor data from cache and cache has valid data (fresh passive broadcasts or active readings) use cache, otherwise run new active btle reading connecting to the sensor.

ThomDietrich commented 4 years ago

Imho passive reading is an independent (and very interesting) topic and we should discuss it in a new ticket

... miflora sensor broadcast in the air, add sensor reading into miflora library cache. If user request sensor data from cache...

This assumes the library is active in background, which it is not. The client would need to trigger background listening and provide a callback function to react to new passive readings. Let's move to a new ticket.


I've tried modifying miflora library couple days ago with same hack to mitigate led blinking

Did you try the same or a different solution than @MartinHaimberger in https://github.com/open-homeautomation/miflora/issues/136#issuecomment-605967899

bangom commented 4 years ago

Of course, I've just initially mentioned it here, because it solves the issue with LED blinking. Let's create new "feature request" (passive miflora reading).

Imho passive reading is an independent (and very interesting) topic and we should discuss it in a new ticket

... miflora sensor broadcast in the air, add sensor reading into miflora library cache. If user request sensor data from cache...

This assumes the library is active in background, which it is not. The client would need to trigger background listening and provide a callback function to react to new passive readings. Let's move to a new ticket.

I've tried modifying miflora library couple days ago with same hack to mitigate led blinking

Did you try the same or a different solution than @MartinHaimberger in #136 (comment)

I would say the same solution (I haven't modified import section, because I have already "import time" on line 6. But time.sleep(5) was right before self._cache = connection.read_handle(_HANDLE_READ_SENSOR_DATA) # pylint: disable=no-member. That caused reading delay but also miflora-mqtt-daemon to fail during step "Adding sensor to device list and testing connection".