Coder Social home page Coder Social logo

ttu / ruuvitag-sensor Goto Github PK

View Code? Open in Web Editor NEW
189.0 19.0 75.0 391 KB

Python package for communicating with RuuviTag BLE Sensor and for decoding sensor data from broadcasted data

Home Page: https://ttu.github.io/ruuvitag-sensor/

License: MIT License

Python 99.26% Shell 0.74%
python-package ruuvitag python pypi bluetooth-le rxpy reactive-extensions

ruuvitag-sensor's Introduction

RuuviTag Sensor Python Package

Build Status License PyPI version PyPI downloads Python versions

ruuvitag-sensor is a Python package for communicating with RuuviTag BLE Sensor and for decoding measurement data from broadcasted BLE data.

Documentation website: https://ttu.github.io/ruuvitag-sensor/

Requirements

  • RuuviTag sensor
  • Bleak communication module (Windows, macOS and Linux)
  • Bluez (Linux-only)
    • Default adapter for Linux
    • Bluez supports
    • Install guide
    • NOTE: The BlueZ-adapter implementation uses deprecated BlueZ tools that are no longer supported.
      • Even though BlueZ is still the default adapter, it is recommended to use the Bleak-communication adapter with Linux. Bleak will be the default adapter for Linux in the next major release.
      • Bleson-adapter supports sync-methods, but please be aware that it is not fully supported due to the alpha release status of the Bleson communication module. See Bleson for more information.
  • Python 3.7+

NOTE: Version 2.0 contains method renames. When using a version prior to 2.0, check the documentation and examples from PyPI or in GitHub, switch to the correct release tag from switch branches/tags.

Installation

Install the latest released version

$ python -m pip install ruuvitag-sensor

Install the latest development version

$ python -m venv .venv
$ source .venv/bin/activate
$ python -m pip install git+https://github.com/ttu/ruuvitag-sensor

# For development, clone this repository and install for development in editable mode
$ python -m pip install -e .[dev]

Full installation guide for Raspberry PI & Raspbian

Usage

The package provides 3 ways to fetch data from sensors:

  1. Asynchronously with async/await
  2. Synchronously with callback
  3. Observable streams with ReactiveX

RuuviTag sensors can be identified using MAC addresses. Methods return a tuple with MAC and sensor data payload.

('D2:A3:6E:C8:E0:25', {'data_format': 5, 'humidity': 47.62, 'temperature': 23.58, 'pressure': 1023.68, 'acceleration': 993.2331045630729, 'acceleration_x': -48, 'acceleration_y': -12, 'acceleration_z': 992, 'tx_power': 4, 'battery': 2197, 'movement_counter': 0, 'measurement_sequence_number': 88, 'mac': 'd2a36ec8e025', 'rssi': -80})

1. Get sensor data asynchronously with async/await

NOTE: Asynchronous functionality works only with Bleak-adapter.

get_data_async returns the data whenever a RuuviTag sensor broadcasts data. get_data_async will execute until iterator is exited. This method is the preferred way to use the library with Bleak.

import asyncio
from ruuvitag_sensor.ruuvi import RuuviTagSensor


async def main():
    async for found_data in RuuviTagSensor.get_data_async():
        print(f"MAC: {found_data[0]}")
        print(f"Data: {found_data[1]}")


if __name__ == "__main__":
    asyncio.get_event_loop().run_until_complete(main())

The optional list of MACs can be passed to the get_data_async function.

import asyncio
from ruuvitag_sensor.ruuvi import RuuviTagSensor

macs = ["AA:2C:6A:1E:59:3D", "CC:2C:6A:1E:59:3D"]


async def main():
    # Get data only for defineded MACs. Exit after 10 found results
    datas = []
    async for found_data in RuuviTagSensor.get_data_async(macs):
        print(f"MAC: {found_data[0]}")
        print(f"Data: {found_data[1]}")
        datas.push(found_data)
        if len(datas) > 10:
            break


if __name__ == "__main__":
    asyncio.get_event_loop().run_until_complete(main())

The line if __name__ == "__main__": is required on Windows and macOS due to the way the multiprocessing library works. It is not required on Linux, but it is recommended. It is omitted from the rest of the examples below.

2. Get sensor data synchronously with callback

NOTE: Asynchronous functionality works only with BlueZ-adapter.

get_data calls the callback whenever a RuuviTag sensor broadcasts data. This method is the preferred way to use the library with BlueZ.

from ruuvitag_sensor.ruuvi import RuuviTagSensor


def handle_data(found_data):
    print(f"MAC {found_data[0]}")
    print(f"Data {found_data[1]}")


if __name__ == "__main__":
    RuuviTagSensor.get_data(handle_data)

The optional list of MACs and run flag can be passed to the get_data function. The callback is called only for MACs in the list and setting the run flag to false will stop execution. If the run flag is not passed, the function will execute forever.

from ruuvitag_sensor.ruuvi import RuuviTagSensor, RunFlag

counter = 10
# RunFlag for stopping execution at desired time
run_flag = RunFlag()


def handle_data(found_data):
    print(f"MAC: {found_data[0]}")
    print(f"Data: {found_data[1]}")

    global counter
    counter = counter - 1
    if counter < 0:
        run_flag.running = False


# List of MACs of sensors which will execute callback function
macs = ["AA:2C:6A:1E:59:3D", "CC:2C:6A:1E:59:3D"]

RuuviTagSensor.get_data(handle_data, macs, run_flag)

3. Get sensor data with observable streams (ReactiveX / RxPY)

RuuviTagReactive is a reactive wrapper and background process for RuuviTagSensor get_data. An optional MAC address list can be passed on the initializer and execution can be stopped with the stop function.

from ruuvitag_sensor.ruuvi_rx import RuuviTagReactive
from reactivex import operators as ops

ruuvi_rx = RuuviTagReactive()

# Print all notifications
ruuvi_rx.get_subject().\
    subscribe(print)

# Print only last data every 10 seconds for F4:A5:74:89:16:57
ruuvi_rx.get_subject().pipe(
      ops.filter(lambda x: x[0] == "F4:A5:74:89:16:57"),
      ops.buffer_with_time(10.0)
    ).subscribe(lambda data: print(data[len(data) - 1]))

# Execute only every time when temperature changes for F4:A5:74:89:16:57
ruuvi_rx.get_subject().pipe(
      ops.filter(lambda x: x[0] == "F4:A5:74:89:16:57"),
      ops.map(lambda x: x[1]["temperature"]),
      ops.distinct_until_changed()
    ).subscribe(lambda x: print(f"Temperature changed: {x}"))

# Close all connections and stop Bluetooth communication
ruuvi_rx.stop()

More samples and a simple HTTP server under the examples directory.

Check the official documentation of ReactiveX and the list of operators.

Other helper methods

Get data for specified sensors for a specific duration

get_data_for_sensors and get_data_for_sensors_async will collect the latest data from sensors for a specified duration.

from ruuvitag_sensor.ruuvi import RuuviTagSensor

# List of MACs of sensors which data will be collected
# If list is empty, data will be collected for all found sensors
macs = ["AA:2C:6A:1E:59:3D", "CC:2C:6A:1E:59:3D"]
# get_data_for_sensors will look data for the duration of timeout_in_sec
timeout_in_sec = 4

data = RuuviTagSensor.get_data_for_sensors(macs, timeout_in_sec)
# data = await RuuviTagSensor.get_data_for_sensors_async(macs, timeout_in_sec)

# Dictionary will have latest data for each sensor
print(data["AA:2C:6A:1E:59:3D"])
print(data["CC:2C:6A:1E:59:3D"])

NOTE: These methods shouldn't be used for a long duration with a short timeout. Methods will start and stop a new BLE scanning process with every method call. For long-running processes, it is recommended to use the get_data- and get_data_async-method.

Get data from a sensor

NOTE: For a single sensor it is recommended to use RuuviTagSensor.get_data or RuuviTagSensor.get_data_async methods instead of RuuviTag- or RuuviTagAsync-class. RuuviTagAsync-class doesn't work with macOS, due to the way how macOS returns MAC address.

from ruuvitag_sensor.ruuvitag import RuuviTag

sensor = RuuviTag("AA:2C:6A:1E:59:3D")

# update state from the device
state = sensor.update()

# get latest state (does not get it from the device)
state = sensor.state

print(state)

Find sensors

RuuviTagSensor.find_ruuvitags and RuuviTagSensor.find_ruuvitags_async methods will execute forever and when a new RuuviTag sensor is found, it will print its MAC address and state at that moment. This function can be used with command-line applications. Logging must be enabled and set to print to the console.

from ruuvitag_sensor.ruuvi import RuuviTagSensor
import ruuvitag_sensor.log

ruuvitag_sensor.log.enable_console()

RuuviTagSensor.find_ruuvitags()

Using different Bluetooth device

If you have multiple Bluetooth devices installed, a device to be used might not be the default (Linux: hci0). The device can be passed with a bt_device-parameter.

from ruuvitag_sensor.ruuvi import RuuviTagSensor
from ruuvitag_sensor.ruuvitag import RuuviTag

sensor = RuuviTag("F4:A5:74:89:16:57", "hci1")

RuuviTagSensor.find_ruuvitags("hci1")

data = RuuviTagSensor.get_data_for_sensors(bt_device="hci1")

RuuviTagSensor.get_data(lambda x: print(f"{x[0]} - {x[1]}"), bt_device=device))

Parse data

from ruuvitag_sensor.data_formats import DataFormats
from ruuvitag_sensor.decoder import get_decoder

full_data = "043E2A0201030157168974A51F0201060303AAFE1716AAFE10F9037275752E76692F23416A5558314D417730C3"
data = full_data[26:]

# convert_data returns tuple which has Data Format type and encoded data
(data_format, encoded) = DataFormats.convert_data(data)

sensor_data = get_decoder(data_format).decode_data(encoded)

print(sensor_data)
# {'temperature': 25.12, 'identifier': '0', 'humidity': 26.5, 'pressure': 992.0}

RuuviTag Data Formats

Example data has data from 4 sensors with different firmware.

  • 1st is Data Format 2 (URL), the identifier is None as the sensor doesn't broadcast any identifier data
  • 2nd is Data Format 4 (URL) and it has an identifier character
  • 3rd is Data Format 3 (RAW)
  • 4th is Data Format 5 (RAW v2)
{
'CA:F7:44:DE:EB:E1': { 'data_format': 2, 'temperature': 22.0, 'humidity': 28.0, 'pressure': 991.0, 'identifier': None, 'rssi': None },
'F4:A5:74:89:16:57': { 'data_format': 4, 'temperature': 23.24, 'humidity': 29.0, 'pressure': 991.0, 'identifier': '0', 'rssi': None },
'A3:GE:2D:91:A4:1F': { 'data_format': 3, 'battery': 2899, 'pressure': 1027.66, 'humidity': 20.5, 'acceleration': 63818.215675463696, 'acceleration_x': 200.34, 'acceleration_y': 0.512, 'acceleration_z': -200.42, 'temperature': 26.3, 'rssi': None },
'CB:B8:33:4C:88:4F': { 'data_format': 5, 'battery': 2.995, 'pressure': 1000.43, 'mac': 'cbb8334c884f', 'measurement_sequence_number': 2467, 'acceleration_z': 1028, 'acceleration': 1028.0389097694697, 'temperature': 22.14, 'acceleration_y': -8, 'acceleration_x': 4, 'humidity': 53.97, 'tx_power': 4, 'movement_counter': 70, 'rssi': -65 }
}

Note on Data Format 2 and 4

There is no reason to use Data Format 2 or 4.

The original reason to use the URL-encoded data was to use Google's Nearby notifications to let users view tags without the need to install any app. Since Google's Nearby has been discontinued, there isn't any benefit in using the Eddystone format anymore.

Logging and printing to the console

Logging can be enabled by importing ruuvitag_sensor.log. Console print can be enabled by calling ruuvitag_sensor.log.enable_console(). The command line application has console logging enabled by default.

from ruuvitag_sensor.ruuvi import RuuviTagSensor
import ruuvitag_sensor.log

ruuvitag_sensor.log.enable_console()

data = RuuviTagSensor.get_data_for_sensors()

print(data)

To enable debug logging to console, set log-level to DEBUG.

import logging
import ruuvitag_sensor.log
from ruuvitag_sensor.log import log
from ruuvitag_sensor.ruuvi import RuuviTagSensor

ruuvitag_sensor.log.enable_console()

log.setLevel(logging.DEBUG)

for handler in log.handlers:
    handler.setLevel(logging.DEBUG)

data = RuuviTagSensor.get_data_for_sensors()

print(data)

Log all events to log-file

By default only errors are logged to ruuvitag_sensor.log-file. The level can be changed by changing FileHandler's log level.

import logging
from ruuvitag_sensor.log import log
from ruuvitag_sensor.ruuvi import RuuviTagSensor

log.setLevel(logging.DEBUG)

for handler in log.handlers:
    if isinstance(handler, logging.FileHandler):
        handler.setLevel(logging.DEBUG)

data = RuuviTagSensor.get_data_for_sensors()

A custom event handler for a specific log event

If custom functionality is required when a specific event happens, e.g. exit when a specific sensor is blacklisted, logging event handlers can be utilized for this functionality.

from logging import StreamHandler
from ruuvitag_sensor.log import log
from ruuvitag_sensor.ruuvi import RuuviTagSensor


class ExitHandler(StreamHandler):

    def emit(self, record):
        if (record.levelname != "DEBUG"):
            return
        msg = self.format(record)
        if "Blacklisting MAC F4:A5:74:89:16:57E" in msg:
            exit(1)


exit_handler = ExitHandler()
log.addHandler(exit_handler)

data = RuuviTagSensor.get_data_for_sensors()

Command line application

$ python ruuvitag_sensor -h

usage: ruuvitag_sensor [-h] [-g MAC_ADDRESS] [-d BT_DEVICE] [-f] [-l] [-s] [--version]

optional arguments:
  -h, --help            show this help message and exit
  -g MAC_ADDRESS, --get MAC_ADDRESS
                        Get data
  -d BT_DEVICE, --device BT_DEVICE
                        Set Bluetooth device id (default hci0)
  -f, --find            Find broadcasting RuuviTags
  -l, --latest          Get latest data for found RuuviTags
  -s, --stream          Stream broadcasts from all RuuviTags
  --version             show program's version number and exit

BLE Communication modules

BlueZ

BlueZ works only on Linux. When using BlueZ, Windows and macOS support is only for testing with hard-coded data and for data decoding.

BlueZ tools require superuser rights.

Install BlueZ.

$ sudo apt-get install bluez bluez-hcidump

ruuvitag-sensor package uses internally hciconfig, hcitool and hcidump. These tools are deprecated. In case tools are missing, an older version of BlueZ is required (Issue)

If you wish to test the library on Windows or macOS, enable it with RUUVI_BLE_ADAPTER environment variable.

$ export RUUVI_BLE_ADAPTER="bluez"

And install ptyprocess.

python -m pip install ptyprocess

BlueZ limitations

ruuvitag-sensor package uses BlueZ to listen to broadcasted BL information (uses hciconf, hcitool, hcidump). Implementation does not handle well all unexpected errors or changes, e.g. when the adapter is busy, rebooted or powered down.

In case of errors, the application tries to exit immediately, so it can be automatically restarted.

Bleak

On Windows and macOS Bleak is installed and used automatically with ruuvitag-sensor package.

On Linux install it manually from PyPI and enable it with RUUVI_BLE_ADAPTER environment variable.

$ python -m pip install bleak

Add environment variable RUUVI_BLE_ADAPTER with value Bleak. E.g.

$ export RUUVI_BLE_ADAPTER="bleak"

Or use os.environ. NOTE: this must be set before importing ruuvitag_sensor.

import os

os.environ["RUUVI_BLE_ADAPTER"] = "bleak"

Bleak supports only async methods.

import asyncio
from ruuvitag_sensor.ruuvi import RuuviTagSensor


async def main():
    async for data in RuuviTagSensor.get_data_async():
        print(data)


if __name__ == "__main__":
    asyncio.get_event_loop().run_until_complete(main())

Check get_async_bleak and other async examples from examples directory.

Bleak dummy BLE data

Bleak-adapter has a development-time generator for dummy data, which can be useful during the development, if no sensors are available. Set RUUVI_BLE_ADAPTER environment variable to bleak_dev.

Bleson

Current state and known bugs in issue #78.

Bleson works with Linux, macOS and partially with Windows.

Bleson is not installed automatically with ruuvitag-sensor package. Install it manually from GitHub.

$ python -m pip install git+https://github.com/TheCellule/python-bleson

Add environment variable RUUVI_BLE_ADAPTER with value bleson. E.g.

$ export RUUVI_BLE_ADAPTER="bleson"

NOTE: On macOS, only Data Format 5 works, as macOS doesn't advertise MAC address and only DF5 has MAC in sensor payload. RuuviTag-class doesn't work with macOS.

NOTE: On Windows, Bleson requires Python 3.6. Unfortunately on Windows, Bleson doesn't send any payload for the advertised package, so it is still unusable.

Python 2.x and 3.6 and below

Last version of ruuvitag-sensor with Python 2.x and <3.7 support is 1.2.1.

Branch / Tag / commit

$ git checkout release/1.2.1

Install from PyPI

$ python -m pip install ruuvitag-sensor==1.2.1

Examples

Examples are in examples directory, e.g.

Changelog

Changelog

Developer notes

Notes for developers who are interested in developing ruuvitag-sensor package or are interested in its internal functionality.

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

License

Licensed under the MIT License.

ruuvitag-sensor's People

Contributors

akx avatar chatne avatar danstahr avatar dg12 avatar dhruvshettty avatar dronir avatar elwell avatar ikke-t avatar jlipponen avatar jpyy avatar juhi24 avatar lalufu avatar latenssi avatar mjazwiecki avatar ojousima avatar pkaila avatar ppetru avatar robertsicoie avatar samporapeli avatar sergioisidoro avatar sevesalm avatar shusso avatar terop avatar ttu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ruuvitag-sensor's Issues

Add timezone support

At the moment time field is set to mirror the current local time of the recipient device without any information of the local timezone.

Either add timezone information to the time string or convert time to UTC.

I will try to make a PR but am currently really limited on time (who isn't) to contribute so don't hold your breath.

Change which ble device to use via cli argument

So firstly, nice work on this, made things very easy!

I've got this working on a CHIP but currently have 2 bluetooth devices (onboard + usb dongle) and device hci0 is already being used for something else. Could we get a optional argument when using this to specifiy which hci device to use?

Error running Parse Example Code in Readme.md

The following is some example code under the heading of Parse Data within the Readme file:

from ruuvitag_sensor.ruuvi import RuuviTagSensor
from ruuvitag_sensor.decoder import UrlDecoder
full_data = '043E2A0201030157168974A5F41E0201060303AAFE1616AAFE10EE037275752E76692341412C3E672B49246AB9'
data = full_data[26:]
encoded = RuuviTagSensor.convert_data(data)
sensor_data = UrlDecoder().decode_data(encoded)
print(sensor_data)

When I run this code as follows , I get the following error:

$ python3 parse_data.py
Encoded value: (None, None) not valid
Traceback (most recent call last):
  File "/home/pi/.local/lib/python3.4/site-packages/ruuvitag_sensor/decoder.py", line 72, in decode_data
    decoded = bytearray(base64.b64decode(encoded, '-_'))
  File "/usr/lib/python3.4/base64.py", line 83, in b64decode
    s = _bytes_from_decode_data(s)
  File "/usr/lib/python3.4/base64.py", line 46, in _bytes_from_decode_data
    "string, not %r" % s.__class__.__name__) from None
TypeError: argument should be a bytes-like object or ASCII string, not 'tuple'
None

No data getting from sensor

Hi Guys
I'm just finish install ruuvitag-sensor on my raspberry pi following your instruction.
And finaly when i type ruuvitag -f , this i s the output:

Finding RuuviTags. Stop with Ctrl+C.
Start receiving broadcasts (device hci0)
End Of File (EOF). Exception style platform.
Stop receiving broadcasts

this is my specs in raspberry
Linux raspberrypi 4.14.79
hci0: Type: Primary Bus: UART
BD Address: XX:XX:XX:XX:XX:XX ACL MTU: 1021:8 SCO MTU: 64:1
UP RUNNING
RX bytes:1548 acl:0 sco:0 events:92 errors:0
TX bytes:2564 acl:0 sco:0 commands:90 errors:0
Features: 0xbf 0xfe 0xcf 0xfe 0xdb 0xff 0x7b 0x87
Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3
Link policy: RSWITCH SNIFF
Link mode: SLAVE ACCEPT
Name: 'raspberrypi'
Class: 0x000000
Service Classes: Unspecified
Device Class: Miscellaneous,
HCI Version: 4.1 (0x7) Revision: 0x168
LMP Version: 4.1 (0x7) Subversion: 0x2209
Manufacturer: Broadcom Corporation (15)
Python Version 3.5
pip3 instaled and updated

thank You for answers
with regards
Boris K.

Unable to find tags and data

Hi,
Thanks for the ruuvitag-sensor library and good documentation for the Raspberry Pi.
I can see the RuuviTags (Flashed with latest weather station firmware) in nrfConnect and also in android nearby notifications.
After successfully installing the ruuvitag-sensor library I tried running the find_tags.py which is not showing any tags. However get_ble_data.py shows the MAC id's of the ruuvitags.
What could be the issue?
print_to_screen.py results in follwing error in log file.

2017-05-05 10:17:13,861 - ruuvitag_sensor - ERROR - Encoded value: BGgYAK447 not valid Traceback (most recent call last): File "/root/.local/lib/python3.6/site-packages/ruuvitag_sensor/url_decoder.py", line 51, in decode_data decoded = bytearray(base64.b64decode(encoded, '-_')) File "/usr/local/lib/python3.6/base64.py", line 87, in b64decode return binascii.a2b_base64(s) binascii.Error: Incorrect padding

Terminate program with Ctrl+C doesn't always work nicely

Describe the bug
Ctrl+C doesn't always terminate the program on the first time. Sometimes Ctrl+C must be inserted rapidly multiple times.

Environment (please complete the following information):

  • OS: Ubuntu 19.04, Raspbian Stretch
  • ruuvitag_sensor package version: 0.12, master, bleson-branch

Example tag_test.py is wrong — no output

I had to change the tag_test.py script to get any output. Had to change it to:

Import ruuvitag_sensor
From ruuvitag_sensor.log import log
From ruuvitag_sensor.ruuvi import RuuviTagSensor

ruuvitag_sensor.log.enable_console()

RuuviTagSensor.find_ruuvitags()

(The import & from should be lower case).

Add dockerfile

Dockerfile could be something like this:

FROM arm32v7/ubuntu:bionic

ADD . /ruuvi
WORKDIR /ruuvi

RUN apt update
RUN apt install -y python3-pip

RUN pip3 install -e .

CMD [ "python3", "ruuvitag_sensor", "-s" ]

Base image should have Python compiled with socket.AF_BLUETOOTH support

arm32v7/ubuntu:bionic as the base, as Ubuntu happened to have a version of Python compiled with socket.AF_BLUETOOTH support (#67 (comment))

Have a way to define the bluetooth device if more than one exists (linux)

Hello,

I have more than one bluetooth device on my linux machine.
BleCommunicationNix.start() will hang, as hci0 is not a usable bluetooth device.

Ideally, id like to use hci1, which is my main bluetooth dongle.
Is it possible to have an environment var to set alternatives or a argument when instancing RuuviTagSensor?

Cheers
Kym

Reactive exception handling

This is related to #67. I'm still getting some exceptions out of the library code.

I tried adding a catch-all exceptions block, but that doesn't work as the reactive code seems to be using threading and the exceptions happen in another thread.

bytearray index out of range

My program stop as below:

Press Ctrl+C to quit.

2018-07-14 10:40:08.420778
Sensor - DF:25:8D:F6:45:47
Temperature: -21.46 C
Humidity: 57.0
Pressure: 1011.97

.......
Encoded value: �
not valid
Traceback (most recent call last):
File "/home/pi/.local/lib/python3.6/site-packages/ruuvitag_sensor/decoder.py", line 88, in decode_data
'temperature': self._get_temperature(decoded),
File "/home/pi/.local/lib/python3.6/site-packages/ruuvitag_sensor/decoder.py", line 59, in _get_temperature
temp = (decoded[2] & 127) + decoded[3] / 100
IndexError: bytearray index out of range
Traceback (most recent call last):
File "bryant.py", line 38, in
line_tem = str.format('Temperature: {0} C', data['temperature'])
TypeError: 'NoneType' object is not subscriptable

Calculation of acceleration is not correct

According to the Data format 3 specification the acceleration value is a 2-complement int16 MSB first. One way to calculate the signed value is to use this:

# Example: twos_complment(0xFC18,16) = -1000
def twos_complement(value, bits):
    if (value & (1 << (bits - 1))) != 0: # Check if sign bit is set
        value = value - (1 << bits)      # Compute negative value
    return value

It might also be useful to get the data for each axle separately.

Accuracy of the measurements has a bug when using python 2.7

when using python 2.7 the accuracy of the data what the _get_temperature, _get_humidity, _get_pressure etc. in the decoder.py is allways integered instead of float.

One way to fix this problem in python2.7 is to add this as the first code line in the decoder.py:
from future import division

Bleson Bluetooth LE communication

Bleson is a cross platform Python 3 module for accessing Bluetooth LE API's

Bleson is still on alpha stage.

Operating Systems:

  • Linux
    • OK
  • macOS
    • Didn't crash on Bleson initialization
    • macOS doesn't send MAC-address with advertised BLE package
  • Windows
    • Error on Bleson initialization

Earlier discussions:
Issues #31 and #18.

Notes
Comments are appreciated!

First code snippet on homepage fails, RuuviTag deprecated?

From the examples on the homepage, the first one seems to be based on a deprecated (or moved?) class:

from ruuvitag_sensor.ruuvi import RuuviTag

fails with an import error.

 ImportError: cannot import name 'RuuviTag'

RuuviTagSensor works fine.

can't find ruuvitag b+ model with certain range

I having a challenged can't find ruuvitag when I put inside it into my fridge(Using pi 3 b+). The other hand using pi zero w using same script which no issue. I am using find_tags.py script for this. Anyone experience this?

Server Error when accessing the example HTTP server

When I want to access the example http ruuvi server running a raspberry pi 3b I get a server error 500 as soon I want to access either /data or /data/MAC:

  • Serving Flask app "ruuvi" (lazy loading)
  • Environment: production
    WARNING: This is a development server. Do not use it in a production deployment.
    Use a production WSGI server instead.
  • Debug mode: off
  • Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
    [2019-10-28 12:20:39,879] ERROR in app: Exception on /data [GET]
    Traceback (most recent call last):
    File "/home/pi/.local/lib/python3.7/site-packages/flask/app.py", line 2446, in wsgi_app
    response = self.full_dispatch_request()
    File "/home/pi/.local/lib/python3.7/site-packages/flask/app.py", line 1951, in full_dispatch_request
    rv = self.handle_user_exception(e)
    File "/home/pi/.local/lib/python3.7/site-packages/flask/app.py", line 1820, in handle_user_exception
    reraise(exc_type, exc_value, tb)
    File "/home/pi/.local/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
    File "/home/pi/.local/lib/python3.7/site-packages/flask/app.py", line 1949, in full_dispatch_request
    rv = self.dispatch_request()
    File "/home/pi/.local/lib/python3.7/site-packages/flask/app.py", line 1935, in dispatch_request
    return self.view_functionsrule.endpoint
    File "ruuvi.py", line 55, in get_all_data
    update_data()
    File "ruuvi.py", line 50, in update_data
    allData[key][0] = value
    TypeError: tuple indices must be integers or slices, not str
    10.0.60.1 - - [28/Oct/2019 12:20:39] "GET /data HTTP/1.1" 500 -

OS: Linux raspberrypi 4.19.75-v7+ #1270 SMP Tue Sep 24 18:45:11 BST 2019 armv7l GNU/Linux
Ruuvitag package: ruuvitag_sensor-0.13.0

pi username should not be in command

I logged in as dgerman
so /home/pi in the following examples

$ python3 /home/pi/.local/lib/python3.4/site-packages/ruuvitag_sensor -h

should probably be

$ python3 ~/.local/lib/python3.4/site-packages/ruuvitag_sensor -h

Great install instructions.
Great package!! THANK YOU!

InfluxDB missing fields

Hi.

I am using post_to_influxdb.py and it's working otherwise nicely to my needs. However when I do fing_tags I can see the battery values from the RuuviTags, but those are not exported into my InfluxDB. The fields I see there (and can use in my Grafana) are: accelerationX, -Y and -Z, humidity, movementCounter, pressure and temperature.

Using RuuviCollector I can get all those into my InfluxDB, but I'd prefer your Python scripts :)

The tags are brand new, have the default firmware (Weather Station, I believe) and are in the default RAW mode (red led flashing).

ruuvi_tag sensor only runs as root on raspbian?

Ruuvitag gives and error when not running as root:
python3 /root/.local/lib/python3.4/site-packages/ruuvitag_sensor --help
Traceback (most recent call last):
File "/usr/lib/python3.4/runpy.py", line 170, in _run_module_as_main
"main", mod_spec)
File "/usr/lib/python3.4/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/root/.local/lib/python3.4/site-packages/ruuvitag_sensor/main.py", line 4, in
import ruuvitag_sensor
ImportError: No module named 'ruuvitag_sensor'

Negative values not decoded correctly from data format3

Negative values seems to be incorrectly decoded. Last night was the first night with subzero temperatures at our home, see the picture. Crossing zero leads ruuvitag_sensor 0.8.0 to report -128.
Ideally add a test case for these boundary values :)

img_3464

Add accelerometer reading

Hi

It would be nice to have support for accelerometer values too as specified in the Data Format 3.
Probably also add the battery voltage too.

Thanks

hcitool and hcidump are not terminated

I’ve noticed that when my Python code is executed from rc.local (i.e. at startup) the RuuviTagSensor.get_data_for_sensors() is firing off new processes that are not terminated. I.e. each time RuuviTagSensor.get_data_for_sensors() is called hcitool lescan and hcidump are executed again and again. This memory leak fills up the RAM… I.e. I get multiple versions of the processes below:

pi@raspberry:~ $ ps 2188
PID TTY STAT TIME COMMAND
2188 ? S 0:00 hcitool lescan --duplicates
pi@raspberry:~ $ ps 2231
PID TTY STAT TIME COMMAND
2231 ? S 0:00 sudo -n hcidump --raw

Note: This does not happen when I execute the Python code on the command line using nohup, doesn’t matter if the user is pi or root. I guess the error lies in:
def stop(hcitool, hcidump):
in ble_communication.py but I have not looked further for now.

binascii.Error occasionally

I use RuuviTagSensor in my own script and it seems to crash every now and then in call
datas = RuuviTagSensor.get_data_for_sensors(macs, timeout_in_sec)
(macs is [], timeout is 4) with traceback below:

Traceback (most recent call last):
  File "/home/pi/.virtualenvs/ruuvi/lib/python3.5/site-packages/ruuvitag_sensor/decoder.py", line 72, in decode_data
    decoded = bytearray(base64.b64decode(encoded, '-_'))
  File "/home/pi/.virtualenvs/ruuvi/lib/python3.5/base64.py", line 88, in b64decode
    return binascii.a2b_base64(s)
binascii.Error: Incorrect padding
Encoded value: w not valid

I'm trying to avoid this (from now) by catching this exception in my code, but I think this should be handled in RuuviTagSensor library, which could raise a custom exception or something.

What do you think?

support RuuvitagSensor first generations ruuvitags? I got an error

Hi,
I have 1 working ruuvitag which are from early kickstarter time (FCC ID 2AKDDRUUVITAG), there is only temperature sensor inside.

I tried to find ruuvitags with RuuvitagSensor via Python (Windows) and I got next log:

MAC DU:MM:YD:AT:A9:3D
{'data_format': 2, 'temperature': 24.0, 'humidity': 30.0, 'pressure': 995.0, 'identifier': None}
MAC NO:TS:UP:PO:RT:ED
{'data_format': 2, 'temperature': 24.0, 'humidity': 30.0, 'pressure': 995.0, 'identifier': None}
Process finished with exit code 0

Is these some default values from RuuvitagSensor? Should this work with this early generation Ruuvitag as well? However I can't flash Weather station to my ruuvitag:(

I updated firmware version to 1.2.12 succesfully.

This ruuvitag works with ruuvi Station showing only temperature.

Decoder index out of range

An exception jumped out of the decoder:

2019-02-11 14:30:41,825  ERROR -               decoder.py:161 -              decode_data(): Value: b'\x03' not valid
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/ruuvitag_sensor/decoder.py", line 148, in decode_data
    acc_x, acc_y, acc_z = self._get_acceleration(byte_data)
  File "/usr/local/lib/python3.6/dist-packages/ruuvitag_sensor/decoder.py", line 130, in _get_acceleration
    acc_x = twos_complement((data[6] << 8) + data[7], 16)
IndexError: index out of range

I'm running with data format 3, and the bleson branch.

Data format 3 decoding is not working with Python2

There is this line of code in the decoder.py (line 124) which does not work with python2

byte_data = bytes.fromhex(data)

It works when changed as:
byte_data = bytearray.fromhex(data)

I suppose this would work also with python3 so you might want to consider making this change?

Thanks for the great library!

Fails to list or connect to Ruuvitags

Hi,
I have a ruuvitag broadcasing about 10 feet from my pi; however, I can't connect to it. I get an error that says "set scan parameters failed: Input/Output error" when I run python ruuvitag_sensor -f from command line. Am I missing something obvious? It also appears in a third-party application known as Ruuvitag-logger that uses this as a dependency.

hcitool deprecated upstream

It seems upstream does not include hcitool anymore in bluez-utils [1]. Sooner or later people will realize this when they run into problems. Although there are legacy packages already for some distros (although not official), I think it might be a good idea to add support for an alternative method to communicate using bluetooth. bluetoothctl is the upstream alternative and therefore might be a good candidate.

[1] https://www.spinics.net/lists/linux-bluetooth/msg69239.html

'Encoded value not valid' error on data format 4 tags

Environment (please complete the following information):

  • OS: Raspbian Stretch
  • ruuvitag_sensor package version: 0.13
  • RuuviTag firmware version: Not sure ?
  • Data format: 4

I get a stderr sometimes on data format 4 tags, but I seem to still get the data. Not sure what 'c' is - maybe that is the problem.

E2:A6:C1:89:4E:03
{'data_format': 4, 'temperature': 23.0, 'humidity': 42.0, 'pressure': 1003.0, 'identifier': 'c'}

From stdout / stderr:

Encoded value: BFQX>+ not valid
Traceback (most recent call last):
File "/home/pi/.local/lib/python3.7/site-packages/ruuvitag_sensor/decoder.py", line 89, in decode_data
decoded = bytearray(base64.b64decode(encoded, '-_'))
File "/usr/lib/python3.7/base64.py", line 87, in b64decode
return binascii.a2b_base64(s)
binascii.Error: Invalid base64-encoded string: number of data characters (5) cannot be 1 more than a multiple of 4
Decoded data is null. MAC: E2:A6:C1:89:4E:03 - Raw: 1F0201060303AAFE1716AAFE10F9037275752E76692F2342465158CE00043E2B02
Can't init device hci0: Connection timed out (110)

Add helper functions

  • Is Tag present function
    • As a static RuuviTagSensor function
    • As a method in RuuviTag class
  • What other functions would be handy?

Running post_to_influxdb.py for a long period creates hundreds of zombie hcitool processes

It also stops actually capturing and uploading data once this starts happening.

root 28948 0.0 0.3 7220 3216 pts/1 S+ Apr19 0:00 sudo -n hcitool lescan --duplicates
root 28955 0.1 0.0 2476 632 pts/1 S+ Apr19 11:05 hcitool lescan --duplicates
root 29302 0.0 0.3 7220 3420 pts/1 S+ Apr19 0:00 sudo -n hcitool lescan --duplicates
root 29308 0.1 0.0 2476 624 pts/1 S+ Apr19 11:22 hcitool lescan --duplicates
root 29384 0.0 0.3 7220 3332 pts/1 S+ Apr20 0:00 sudo -n hcitool lescan --duplicates
root 29391 0.1 0.0 2476 648 pts/1 S+ Apr20 10:29 hcitool lescan --duplicates
root 29406 0.0 0.3 7220 3332 pts/1 S+ Apr20 0:00 sudo -n hcitool lescan --duplicates
root 29413 0.1 0.0 2476 616 pts/1 S+ Apr20 8:43 hcitool lescan --duplicates
root 29642 0.0 0.3 7220 3416 pts/1 S+ Apr19 0:00 sudo -n hcitool lescan --duplicates
root 29649 0.1 0.0 2476 624 pts/1 S+ Apr19 11:27 hcitool lescan --duplicates
root 30432 0.0 0.3 7220 3416 pts/1 S+ Apr20 0:00 sudo -n hcitool lescan --duplicates
root 30438 0.1 0.0 2476 668 pts/1 S+ Apr20 9:08 hcitool lescan --duplicates
root 30466 0.0 0.3 7220 3216 pts/1 S+ Apr20 0:00 sudo -n hcitool lescan --duplicates
root 30473 0.1 0.0 2476 656 pts/1 S+ Apr20 8:37 hcitool lescan --duplicates
root 30576 0.0 0.3 7220 3320 pts/1 S+ Apr20 0:00 sudo -n hcitool lescan --duplicates
root 30583 0.1 0.0 2476 624 pts/1 S+ Apr20 9:12 hcitool lescan --duplicates
root 30700 0.0 0.3 7220 3420 pts/1 S+ Apr20 0:00 sudo -n hcitool lescan --duplicates
root 30707 0.1 0.0 2476 636 pts/1 S+ Apr20 9:02 hcitool lescan --duplicates
root 31163 0.0 0.3 7220 3436 pts/1 S+ Apr19 0:00 sudo -n hcitool lescan --duplicates
root 31171 0.1 0.0 2476 656 pts/1 S+ Apr19 11:03 hcitool lescan --duplicates
root 31322 0.0 0.3 7220 3348 pts/1 S+ Apr20 0:00 sudo -n hcitool lescan --duplicates
root 31329 0.1 0.0 2476 616 pts/1 S+ Apr20 10:39 hcitool lescan --duplicates
root 31853 0.0 0.3 7220 3436 pts/1 S+ Apr23 0:00 sudo -n hcitool lescan --duplicates
root 31860 0.0 0.0 2476 624 pts/1 S+ Apr23 0:14 hcitool lescan --duplicates
root 31930 0.0 0.3 7220 3320 pts/1 S+ Apr21 0:00 sudo -n hcitool lescan --duplicates
root 31935 0.1 0.0 2476 620 pts/1 S+ Apr21 8:08 hcitool lescan --duplicates
root 31998 0.0 0.3 7220 3204 pts/1 S+ Apr23 0:00 sudo -n hcitool lescan --duplicates
root 32005 0.1 0.0 2476 668 pts/1 S+ Apr23 2:10 hcitool lescan --duplicates
root 32276 0.0 0.3 7220 3364 pts/1 S+ Apr21 0:00 sudo -n hcitool lescan --duplicates
root 32283 0.1 0.0 2476 620 pts/1 S+ Apr21 6:07 hcitool lescan --duplicates
root 32712 0.0 0.3 7220 3340 pts/1 S+ Apr21 0:00 sudo -n hcitool lescan --duplicates
root 32718 0.1 0.0 2476 660 pts/1 S+ Apr21 8:25 hcitool lescan --duplicates

Cannot change bluetooth device

Raspberry pi 3 B has a problem with built in bluetooth.

hciconf

hci1: Type: BR/EDR Bus: UART
BD Address: B8:27:EB:F3:E7:01 ACL MTU: 1021:8 SCO MTU: 64:1
UP RUNNING PSCAN
RX bytes:9724 acl:0 sco:0 events:511 errors:0
TX bytes:7207 acl:0 sco:0 commands:432 errors:0

My test scrip (copypaste from examples, tried both hci1 mac and ruuvitag mac, same outcome)

from ruuvitag_sensor.ruuvi import RuuviTagSensor
from ruuvitag_sensor.ruuvitag import RuuviTag

sensor = RuuviTag('B8:27:EB:F3:E7:01', 'hci1')

RuuviTagSensor.find_ruuvitags('hci1')

datas = RuuviTagSensor.get_data_for_sensors(bt_device='hci1')

RuuviTagSensor.get_datas(lambda x: print('%s - %s' % (x[0], x[1]), bt_device=device))

Output when test script is executed:

Can't get device info: No such device
Can't get device info: No such device

For example hcidump seems to work just fine:

pi@raspberrypi:~/ruuvitag $ hcidump -i hci1
HCI sniffer - Bluetooth packet analyzer ver 5.23
device: hci1 snap_len: 1500 filter: 0xffffffff

RSSI

Hi,

can you add RSSI to the list ?

A quick hack lto Df3Decoder like this seems to work:

def _get_rssi(self, data):
    return data[-1]-256

            'rssi': self._get_rssi(byte_data)

{'acceleration': 1038.0934447341433, 'pressure': 1008.27, 'temperature': 24.75, 'acceleration_y': -13, 'acceleration_x': 5, 'battery': 3061, 'acceleration_z': 1038, 'rssi': -68, 'humidity': 42.5}

cannot find psutil on exit(?) -( installed as user xxx not pi)

-l
Exception ImportError: 'No module named psutil' in <generator object get_datas at 0x76b72030> ignored

-f too (shows traceback ending with:
File "/usr/local/lib/python2.7/dist-packages/ruuvitag_sensor/ble_communication.py", line 66, in stop
import psutil
ImportError: No module named psutil

ls -l /usr/local/lib/python2.7/site-packages/
total 0

Should I just move /home/dgerman/.local/lib/python3.4/site-packages to /usr/local...??

piw:/home/dgerman > ls -l .local/lib/python3.4/site-packages/. #notice NOT root .local
total 28
drwxr-x--- 2 dgerman staff 4096 Oct 15 12:06 Rx-1.6.0.dist-info
drwxr-x--- 4 dgerman staff 4096 Oct 15 18:09 psutil
drwxr-x--- 2 dgerman staff 4096 Oct 15 12:06 psutil-5.4.0.egg-info
drwxr-x--- 3 dgerman staff 4096 Oct 15 19:44 ruuvitag_sensor
drwxr-x--- 2 dgerman staff 4096 Oct 15 18:25 ruuvitag_sensor-0.8.2.egg-info
-rw-r----- 1 dgerman staff 78 Oct 15 19:48 ruuvitag_sensor.log
drwxr-x--- 12 dgerman staff 4096 Oct 15 12:06 rx

piw:/usr/bin > uname -a
Linux piw 4.9.35-v7+ #1014 SMP Fri Jun 30 14:47:43 BST 2017 armv7l GNU/Linux

piw:/usr/bin > cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 8 (jessie)"
NAME="Raspbian GNU/Linux"
VERSION_ID="8"
VERSION="8 (jessie)"
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"

============================================
By the way, are these links correct in particular python -> python2.7

lrwxrwxrwx 1 root root 8 Mar 28 2015 pydoc -> pydoc2.7
-rwxr-xr-x 1 root root 79 Sep 17 2016 pydoc2.7
lrwxrwxrwx 1 root root 8 Dec 6 2014 pydoc3 -> pydoc3.4
-rwxr-xr-x 1 root root 79 Oct 19 2014 pydoc3.4
lrwxrwxrwx 1 root root 12 Mar 28 2015 pygettext -> pygettext2.7
-rwxr-xr-x 1 root root 22097 Sep 17 2016 pygettext2.7
lrwxrwxrwx 1 root root 12 Dec 6 2014 pygettext3 -> pygettext3.4
-rwxr-xr-x 1 root root 22368 Oct 19 2014 pygettext3.4
lrwxrwxrwx 1 root root 22 Mar 7 2016 pypy -> ../lib/pypy/bin/pypy-c
-rwxr-xr-x 1 root root 4076 Nov 20 2015 pypyclean
-rwxr-xr-x 1 root root 3511 Nov 20 2015 pypycompile
lrwxrwxrwx 1 root root 9 Mar 28 2015 python -> python2.7
lrwxrwxrwx 1 root root 9 Mar 28 2015 python2 -> python2.7
-rwxr-xr-x 1 root root 3201580 Sep 17 2016 python2.7
lrwxrwxrwx 1 root root 9 Dec 6 2014 python3 -> python3.4
lrwxrwxrwx 1 root root 16 Dec 6 2014 python3-config -> python3.4-config
-rwxr-xr-x 2 root root 3816928 Oct 19 2014 python3.4
lrwxrwxrwx 1 root root 36 Oct 19 2014 python3.4-config -> arm-linux-gnueabihf-python3.4-config
-rwxr-xr-x 2 root root 3816928 Oct 19 2014 python3.4m
lrwxrwxrwx 1 root root 37 Oct 19 2014 python3.4m-config -> arm-linux-gnueabihf-python3.4m-config
lrwxrwxrwx 1 root root 10 Dec 6 2014 python3m -> python3.4m
lrwxrwxrwx 1 root root 17 Dec 6 2014 python3m-config -> python3.4m-config
lrwxrwxrwx 1 root root 29 Mar 28 2015 pyversions -> ../share/python/pyversions.py

Thanks for your help

Add support for asyncio async/await (Python 3.5+)

Add async wrapper (RuuviTagSensorAsync) for RuuviTagSensor.

E.g.

from ruuvitag_sensor.ruuvi_async import RuuviTagSensorAsync

# get_data_for_sensors
datas = await RuuviTagSensorAsync.get_data_for_sensors(macs, timeout_in_sec)

# get_datas
while True:
  data = await RuuviTagSensorAsync.get_datas(macs, run_flag)

Battery level

Could we get the battery level (voltage) as well?

Any plans for OSX?

This is awesome and I'd love to get started right away as my tags arrived today. But I'm on a mac.

Are there plans for supporting readout on OSX any time soon?

There seems to be an open source project around Eddystone on mac in JavaScript / node, but I'd sure love to stay with Python.

0.10.0 updating error

getting an error when updating to 0.10.0

pi@openplotter:~ $ sudo pip install ruuvitag_sensor
Downloading/unpacking ruuvitag-sensor
Downloading ruuvitag_sensor-0.10.0.tar.gz
Running setup.py (path:/tmp/pip-build-MPcNsj/ruuvitag-sensor/setup.py) egg_info for package ruuvitag-sensor
error in ruuvitag_sensor setup command: 'install_requires' must be a string or list of strings containing valid project/version requirement specifiers
Complete output from command python setup.py egg_info:
error in ruuvitag_sensor setup command: 'install_requires' must be a string or list of strings containing valid project/version requirement specifiers


Cleaning up...
Command python setup.py egg_info failed with error code 1 in /tmp/pip-build-MPcNsj/ruuvitag-sensor
Storing debug log for failure in /root/.pip/pip.log

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.