Python - ruuvitag_sensor package


#24

Thanks for the info! I’ll check newer version of Bluez and maybe modify BLE communication to use bluetoothctl and btmgmt. At least I will try to find some solution for BLE communication that would work with all different distros.


#25

Are you @ttu having any plans on adding support for the new RuuviTag sensor packet format (Data Format 3)? I’ve hacked your code a bit to get some data out but I have not put much effort into it.


#26

I was planning to check it this weekend as we have a 4 day holiday in Finland, so it’s a perfect time to do some coding :slight_smile: I created a new branch for it (https://github.com/ttu/ruuvitag-sensor/tree/experimental-weatherstation), so if you have some code ready you and you want to publish it, you can create a pull request there.


Test of humidity in closed versus open enclosure
#27

Ok, it was a small fix, so made it already. At least it looks like it is working. Still need to update README etc. so it will take some time and then will publish new version to pypi. Now the development version from the experimental-weatherstation branch can be installed with pip:

$ pip3 install git+https://github.com/ttu/ruuvitag-sensor@experimental-weatherstation

Added identifier to sensor data. Older weatherstations will have it None.

{
'CA:F7:44:DE:EB:E1': {'temperature': 22.0, 'humidity': 28.0, 'pressure': 991.0, 'identifier': None}, 
'F4:A5:74:89:16:57': {'temperature': 23.24, 'humidity': 29.0, 'pressure': 991.0, 'identifier': '0'}
}

#28

Nice, it works as it should for the Eddystone URL.
But I must have been a bit confused before because what I had been doing was to add a interpreter for the Eddystone protocol (the GAP manufacturer specific data), i.e. when the RuuviTag is in red-blinking-mode. I’ll continue on this work some more.


#29

Sorry my bad. Somehow mixed numbers 3 and 4 in my head…

Data Format 4 is now on main branch, but not yet released.

I committed initial implementation of Data Format 3 to GitHub (https://github.com/ttu/ruuvitag-sensor/tree/data-format-3). Humidity, pressure, temperature and battery seem to show correct values, but acceleration calculation still needs a correct implementation.


#30

Ok, I’ll have a look at that!
In the longer run I’m aiming to connect the tags to Home Assistant (or similar Home automation software). For HA it exists a BLE tracker component but not a component for BLE sensors. I guess it would be nice to have a standardised packet format for sensor data to make it easy to connect various types of BLE sensors. The unencrypted Eddystone-TLM packet does only include temperature and battery voltage unfortunately. The encrypted TLM version has however 12 bytes of Encrypted TLM data available. Have you used any Home automation software or have any other thoughts on this @ttu?


#31

Last summer I was checking Home Assistant and because of that I decided to make this Python package :slight_smile:

I made some fixes for HA’s Sony Bravia component, so I had some minor understanding how HA works, but in the end didn’t have time to start figuring out how to properly get data from BLE sensors.

One way to use RuuviTags with Home Assistant would be running a HTTP Server and getting values with a web request. I made similiar thing with Arduino’s weather board and Home Assistan’s REST platform. Not pretty, but works.


#32

I have been using Domoticz for many years and it’s been working alright but it is a nightmare to make your own fixes for. HA seems much more promising.
I have now made a simple parser that uses your Python sensor package to get sensor data (format 3, not url) and stores them in an InfluxDB. Then I use Grafana to display the data from the database. There exist two InfluxDB components for HA already so I will look more into that.


#33

That InfluxDB solution with HA is much better. Have to check that also. Thanks for the tip.


#34

Hi,
Thanks for sharing this library but I have encountered some issues. I have just followed the install notes for ruuvitag_sensor.ble_communication.
before doing so I checked sudo hcitool lescan which listed addresses and names of devices such as Ruuvitags and puck.js decices I have localy
eg
sudo apt-get install bluez-hcidump
pip3 install --user ruuvitag-sensor
some warnings but notthing critical
python3 /home/pi/.local/lib/python3.4/site-packages/ruuvitag_sensor -h
respond with : usage: ruuvitag_sensor [-h] [-g MAC_ADDRESS] [-f] [-l] [-s] [–version] …

python3 /home/pi/.local/lib/python3.4/site-packages/ruuvitag_sensor -f
<No devices detected here so ended with ctrl+c>
Finding RuuviTags. Stop with Ctrl+C.
Start receiving broadcasts
^CStop receiving broadcasts
I then had the same result eg No output using python3 tag_test.py

I then tried the print_ble.py code you suggested on 28 April:
from ruuvitag_sensor.ble_communication import BleCommunicationNix
ble = BleCommunicationNix()
for ble_data in ble.get_datas():
print(ble_data)

This displayed lots of data such as
(‘C5:60:B7:C1:xx:xx’, ‘1702010504160918150E0952757576695461672034316437BF’)
(‘C5:60:B7:C1:xx:xx’, ‘1211079ECADC240EE5A9E093F3A3B50100406EBE’)

Then I saw that you provided advice to install from github so I tried:

pip3 install --user git+https://github.com/ttu/ruuvitag-sensor --upgrade
Downloading/unpacking git+https://github.com/ttu/ruuvitag-sensor
Cloning https://github.com/ttu/ruuvitag-sensor to /tmp/pip-6pvyjlve-build
Running setup.py (path:/tmp/pip-6pvyjlve-build/setup.py) egg_info for package from git+https://github.com/ttu/ruuvitag-sensor

Requirement already up-to-date: psutil in ./.local/lib/python3.4/site-packages (from ruuvitag-sensor==0.6.0)
Requirement already up-to-date: rx in ./.local/lib/python3.4/site-packages (from ruuvitag-sensor==0.6.0)
Installing collected packages: ruuvitag-sensor
Found existing installation: ruuvitag-sensor 0.6.0
Uninstalling ruuvitag-sensor:
Successfully uninstalled ruuvitag-sensor
Running setup.py install for ruuvitag-sensor

Successfully installed ruuvitag-sensor
Cleaning up…

After the above - no change in the output of python3 /home/pi/.local/lib/python3.4/site-packages/ruuvitag_sensor -f
Start receiving broadcasts
<>
python3 print_ble.py Still works as expected

I should add that I have 3 Ruu.vi sensorTags one running Weather Station, one running Eddystone and one running Espruino as well as several puck.js devices in the area.

I am running Python 3.4.2

Do you have any advice on how to resolve the above?

James

PS. I have subsequently seen advice to turn on logging to console. I have tried the following but no change in results.

tag_find.py:
from ruuvitag_sensor.ruuvi import RuuviTagSensor
import ruuvitag_sensor.log
ruuvitag_sensor.log.enable_console()
RuuviTagSensor.find_ruuvitags()


#35

Thanks for the bug report! Let’s try to figure out what is wrong.

print_ble.py prints all BLE devices that are found, not only RuuviTags, so are both of these RuuviTags?

('C5:60:B7:C1:xx:xx', '1702010504160918150E0952757576695461672034316437BF')
('C5:60:B7:C1:xx:xx', '1211079ECADC240EE5A9E093F3A3B50100406EBE')

Can you check if you have some info in ruuvitag_sensor.log file? All errors should be logged there.

I chaged logging and console print few days ago to match more Python best practices, so if you use the version from GitHub, then you need to enable console logging “manually”.


#36

Hi, Thanks for that.
Firstly, the library version 0.6.0 was downloaded yesterday 7 June. Initially installed using Pip and later installed from github.
$ python3 /home/pi/.local/lib/python3.4/site-packages/ruuvitag_sensor --version
ruuvitag_sensor 0.6.0

I am not sure what you mean by enable console manually.
That data was from a Ruu.vi tag with espruino installed.
The Weather Station Ruu.vi with firmware downloaded in May (April Code) advertisements as follows:
(‘FA:08:56:5C:XX:XX’, ‘1F0201060303AAFE1716AAFE10F9037275752E76692F2342474156414D435571BB’)

(‘FA:08:56:5C:XX:XX’, ‘1F0201060303AAFE1716AAFE10F9037275752E76692F2342474156414D435571BE’)

(‘FA:08:56:5C:XX:XX’, ‘1F0201060303AAFE1716AAFE10F9037275752E76692F2342474156414D435571BA’)

Does this narrow things down?

As for log - that’s empty!

However, I have just run the find code again ad its now reporting the weather station data… How weird is that? Maybe it was connected to something when I tried before. For the record it outputs as follows:
python3 tag_find.py
Finding RuuviTags. Stop with Ctrl+C.
Start receiving broadcasts
FA:08:56:5C:XX:XX
{‘temperature’: 21.0, ‘pressure’: 993.0, ‘humidity’: 48.0, ‘identifier’: ‘q’}


#37

Good that it works now. Pretty likely it just didn’t find correct devices as this is correct and valid data:

('FA:08:56:5C:XX:XX', '1F0201060303AAFE1716AAFE10F9037275752E76692F2342474156414D435571BE')

By manually I mean that before codebase had print functions all around, so you could see in the console what was happening. I changed all those to log.info functions, so unless you define in the code that you want info-level messages to the console, nothing is printed.

And that can be done with these 2 lines

import ruuvitag_sensor.log
ruuvitag_sensor.log.enable_console()

That logging functionality change is not yet released to pypi, but it is in development version which is installed from GitHub. Development version has same version number as latest version in pypi.


#38

Hi @ttu and all others using this great Python package!
I’ve been trying out some different ways of gathering sensor data and storing them in a Influx DB. I am hover not completely satisfied that I found the best solution. What I’m looking for is the following:

  • Detect RuuviTags’ MAC-addresses and store them temporarily e.g. in a dictionary. Items in the dictionary could be cleared after X minutes since the last detection so a timestamp could also be of interest ( {‘MAC’: ‘Timestamp’} )
  • Get sensor data for the MAC-addresses above. As soon as new data arrives for any tag, store it into the Influx DB. Hence, it is recommended to send data from the tags more seldom (10 s advertising interval).

What do you think would be the recommended approach? I’ve tried both the reactive variant and “get_data_for_sensors” but feel that both are a tradeoff (not been able to only and always store data when, and only when, new data has arrived).


#39

I would go with RuuviTagSensor.get_data_for_sensors, but would add one extra layer of handling on top of it.

  • Get all found data every 10 sec with RuuviTagSensor.get_data_for_sensors
  • If data is new or changed add it to all_data dictionary with a current timestamp
    • Ignore data that is not changed
  • Send all new data to DB
  • Clear sensors from all_data dict that haven’t got any new data in x seconds/minutes
all_data = {}

while True:
    datas = RuuviTagSensor.get_data_for_sensors([], 10)
    current_time = datetime.now()

    for key, value in datas.items():
        if key in all_data and all_data[key]['data'] == value:
            continue
        all_data[key] = {'data': value, 'timestamp': current_time}

    new_data = {key: value for key, value in all_data.items() if value['timestamp'] is current_time}
    # TODO: insert new_data to DB

    not_found = [key for key, value in all_data.items() if value['timestamp'] < datetime.now() - timedelta(minutes=5)]
    for key in not_found:
        del all_data[key]

I don’t have any RuuviTags with my at the moment, so can’t verify that this actually works, but it is relatively simple, so it should :slight_smile:


#40

Code works, thanks!
However my problem is still that get_data_for_sensors() only returns one dataset for each senor and time interval. In my current setup I have a modified version of the Weatherstation that for sudden sensor value changes is advertising more frequently (few times each second). So my Tags can advertise in the range of 200ms - 10s, where 10s is default state. Should I have a look on get_data_for_sensors() and make a new command that fits my need?


#41

Ok, missed some requirements, so time for the next iteration :slight_smile: Let’s try another simple solution. RuuviTagSensor.get_datas should return all advertised data, so let’s use that and pretty much same handling as previous solution had. Still can’t verify that this works, but if previous one did, maybe this one will also.

all_data = {}
last_clean = datetime.now()


def handle_data(new_data):
    current_time = datetime.now()
    key = new_data[0]
    value = new_data[1]

    if key not in all_data or all_data[key]['data'] != value:
        all_data[key] = {'data': value, 'timestamp': current_time}
        # TODO: Insert new_data to DB etc.

    # Do not check all datas with every handle_data call
    global last_clean
    if last_clean < current_time:
        last_clean = current_time
        not_found = [key for key, value in all_data.items() if value['timestamp'] < current_time - timedelta(minutes=10)]
        for key in not_found:
            # TODO: Notify of lost sensors
            del all_data[key]

RuuviTagSensor.get_datas(handle_data)

Performance wise this is not the best solution as writing to db can be slow, but let’s hope that db writes are fast :slight_smile: Also cleanup is in the same handle_data function, so it is an ugly solution, but maybe for now we can live with that.


#42

I’ve tried this solution before but then I must have screwed up the code (popping the dictionary) making the save-to-db-handle run more or less continuously. Now, with more proper code it works, thanks!
I have the DB on the same machine so for now it seems like writing speed is sufficient. Although I guess I need something to aggregate older data in the DB over time to keep the size down.


#43

Hi,

anyone can advise what i should do to fix this error ?

pi@raspberrypi:~/ruuvitag $ sudo ./ruuvitag-logger.py
Traceback (most recent call last):
File “./ruuvitag-logger.py”, line 6, in
from ruuvitag_sensor.url_decoder import UrlDecoder
ImportError: No module named ‘ruuvitag_sensor.url_decoder’

thanks