Welcome to Bluezero’s documentation!

Overview

_images/bluez_logo.jpg

Overview

Bluetooth

Bluetooth is a standard set of binary protocols for short-range wireless communication between devices.

  • Bluetooth “Classic” (BR/EDR) supports speeds up to about 24Mbps.
  • Bluetooth 4.0 introduces a low energy mode, “Bluetooth Low Energy” (BLE or LE, also known as “Bluetooth Smart”), that operates at 1Mbps. This mode allows devices to leave their transmitters off most of the time. As a result it is “Low Energy”.

BLE functionality is dominated by key/value pairs that create a Generic Attribute Profile (GATT).

BLE defines multiple roles that devices can play:

  • The Broadcaster (beacon) is a transmitter only application.
  • The Observer (scanner) is for receiver only applications.
  • Devices acting in the Peripheral role can receive connections.
  • Devices acting in the Central role can connect to Peripheral devices.

BlueZ

BlueZ is a Bluetooth stack for the Linux family of operating systems. Support for BlueZ can be found in many Linux distributions available.

The highest level of API on BlueZ is the DBus API which can be daunting to users unfamiliar with such APIs. python-bluezero offers users a more gentle learning curve to using Bluetooth functionality on Linux.

Bluezero API Complexity

This section gives guidelines about the complexity of different python-bluezero APIs. We will use the terms Level 1, 10 and 100. A new user would start at Level 1 as this should offer the least friction. If at a later stage a greater level of control is needed then the user can progress on to the other levels. As the user becomes more experienced they may not need Bluezero and will use BlueZ on its own. The numbers for the API levels represent the steps in code and knowledge required with each step.

Level 1
  • At this level the interface will be pythonic.
  • The API will not assume knowledge of Bluetooth, DBus or event loops.
  • For something to exist at this level there will need to be a public Bluetooth Profile in existence so that users does not need to enter UUIDs etc.
  • This might be specific hardware such as the BBC micro:bit or it could be more generalised hardware such as Heart Rate Monitors.
Level 10
  • At this level the API will be pythonic.
  • The API will require some knowledge of Bluetooth such as UUIDs of services and characteristics for selecting required services.
  • The API will not expose DBus terminology and will simplify event loops.
Level 100
  • At this level the interface is expecting the user to know Bluetooth, DBus and event loops.
  • DBus function names are not Pythonic and may be exposed at the level.
  • This level will be very specific to the Linux kernel and so it will be difficult to port this to other operating systems that do not have the BlueZ Daemon running.
  • The previous more abstracted API levels should be easier to port to any hardware.
Summary of Bluezero Files
Level 1 Level 10 Level 100 shared
microbit.py broadcaster.py adapter.py tools.py
eddystone_beacon.py central.py advertisement.py constants.py
  observer.py device.py dbus_tools.py
  peripheral.py GATT.py async_tools.py
    localGATT.py  

Examples

Examples

An example can often speed things up when you are trying to get started with a library so there are few below. There is also a Getting Started workshop using a Raspberry Pi and a BBC micro:bit

Adapter

This example prints out the status of the Bluetooth device on your Linux computer. It also checks to see if it is enabled (powered) before scanning for nearby Bluetooth devices:

from bluezero import adapter
import logging
try:  # Python 2.7+
    from logging import NullHandler
except ImportError:
    class NullHandler(logging.Handler):
        def emit(self, record):
            pass


def main():
    dongles = adapter.list_adapters()
    print('dongles available: ', dongles)
    dongle = adapter.Adapter(dongles[0])

    print('address: ', dongle.address)
    print('name: ', dongle.name)
    print('alias: ', dongle.alias)
    print('powered: ', dongle.powered)
    print('pairable: ', dongle.pairable)
    print('pairable timeout: ', dongle.pairabletimeout)
    print('discoverable: ', dongle.discoverable)
    print('discoverable timeout: ', dongle.discoverabletimeout)
    print('discovering: ', dongle.discovering)
    print('Powered: ', dongle.powered)
    if not dongle.powered:
        dongle.powered = True
        print('Now powered: ', dongle.powered)
    print('Start discovering')
    dongle.nearby_discovery()
    # dongle.powered = False


if __name__ == '__main__':
    print(__name__)
    logger = logging.getLogger('adapter')
    logger.setLevel(logging.DEBUG)
    logger.addHandler(NullHandler())
    main()

Central Role

This example uses the micro:bit API that has been written in Bluezero to interact with the micro:bit

import time
from bluezero import microbit

ubit = microbit.Microbit(adapter_addr='B8:27:EB:22:57:E0',
                         device_addr='E3:AC:D2:F8:EB:B9',
                         accelerometer_service=True,
                         button_service=True,
                         led_service=True,
                         magnetometer_service=False,
                         pin_service=False,
                         temperature_service=True)

ubit.connect()

assert ubit.pixels == [0b01110,
                       0b10000,
                       0b10000,
                       0b10000,
                       0b01110]

ubit.scroll_delay = 20
delay = ubit.scroll_delay
ubit.text = 'Scroll speed {}'.format(delay)
time.sleep(5)
ubit.text = 'This is a really long string '
time.sleep(5)

while not ubit.button_a:
    ubit.pixels = [0b00000,
                   0b01000,
                   0b11111,
                   0b01000,
                   0b00000]
    time.sleep(0.5)
    ubit.clear_display()

while not ubit.button_b:
    ubit.pixels = [0b00000,
                   0b00010,
                   0b11111,
                   0b00010,
                   0b00000]
    time.sleep(0.5)
    ubit.clear_display()

ubit.clear_display()
ubit.scroll_delay = 120
ubit.text = '{0}'.format(ubit.temperature)
time.sleep(5)

ubit.text = '{0}'.format(ubit.accelerometer)
time.sleep(5)

ubit.disconnect()

Scanner: Eddystone

This example scans for beacons in the Eddystone format. It will report on UID beacons <https://github.com/google/eddystone/tree/master/eddystone-uid> and URL beacons <https://github.com/google/eddystone/tree/master/eddystone-url>.

This uses the aioblescan Python library which requires your code to be run with sudo

from bluezero import observer


def print_eddystone_values(data):
    """

    :param data:
    :return:
    """
    expected_keys = {'name space': 'hex_format',
                     'instance': 'hex_format',
                     'url': 'string_format',
                     'mac address': 'string_format',
                     'tx_power': 'int_format',
                     'rssi': 'int_format'}
    endian = 'big'

    print('New Eddystone data:')
    for prop in data:
        if prop in expected_keys:
            if expected_keys[prop] == 'string_format':
                print('\t{} = {}'.format(prop, data[prop]))
            if expected_keys[prop] == 'hex_format':
                print('\t{} = 0x{:X}'.format(prop,
                                             int.from_bytes(data[prop],
                                                            endian)))
            if expected_keys[prop] == 'int_format':
                print('\t{} = {}'.format(prop, int(data[prop])))


if __name__ == '__main__':
    observer.scan_eddystone(on_data=print_eddystone_values)

Beacon: Eddystone URL

This example broadcasts a given URL in a format for the Physical Web: You will need to put the BlueZ bluetoothd into experimental mode for this one.

from bluezero import eddystone_beacon

eddystone_beacon.EddystoneURL('https://github.com/ukBaz')

Peripheral Role

This example transmits the temperature of the CPU over the single characteristic. If your hardware does not support the vcgencmd then change the get_cpu_temperature() function to use the randomly generated temperature. Values are only updated when notification are switched on. You will need to have BlueZ in experimental mode and have modified the DBus configuration file to open the permissions for ‘ukBaz.bluezero’

# Standard modules
import os
import dbus
try:
    from gi.repository import GObject
except ImportError:
    import gobject as GObject

# Bluezero modules
from bluezero import constants
from bluezero import adapter
from bluezero import advertisement
from bluezero import localGATT
from bluezero import GATT

# constants
CPU_TMP_SRVC = '12341000-1234-1234-1234-123456789abc'
CPU_TMP_CHRC = '2A6E'
CPU_FMT_DSCP = '2904'


def get_cpu_temperature():
    # return random.randrange(3200, 5310, 10) / 100
    cpu_temp = os.popen('vcgencmd measure_temp').readline()
    return float(cpu_temp.replace('temp=', '').replace("'C\n", ''))


def sint16(value):
    return int(value * 100).to_bytes(2, byteorder='little', signed=True)


def cpu_temp_sint16(value):
    answer = []
    value_int16 = sint16(value[0])
    for bytes in value_int16:
        answer.append(dbus.Byte(bytes))

    return answer


class TemperatureChrc(localGATT.Characteristic):
    def __init__(self, service):
        localGATT.Characteristic.__init__(self,
                                          1,
                                          CPU_TMP_CHRC,
                                          service,
                                          [get_cpu_temperature()],
                                          False,
                                          ['read', 'notify'])

    def temperature_cb(self):
        reading = [get_cpu_temperature()]
        print('Getting new temperature',
              reading,
              self.props[constants.GATT_CHRC_IFACE]['Notifying'])
        self.props[constants.GATT_CHRC_IFACE]['Value'] = reading

        self.PropertiesChanged(constants.GATT_CHRC_IFACE,
                               {'Value': dbus.Array(cpu_temp_sint16(reading))},
                               [])
        print('Array value: ', cpu_temp_sint16(reading))
        return self.props[constants.GATT_CHRC_IFACE]['Notifying']

    def _update_temp_value(self):
        if not self.props[constants.GATT_CHRC_IFACE]['Notifying']:
            return

        print('Starting timer event')
        GObject.timeout_add(500, self.temperature_cb)

    def ReadValue(self, options):
        reading = [get_cpu_temperature()]
        self.props[constants.GATT_CHRC_IFACE]['Value'] = reading
        return dbus.Array(
            cpu_temp_sint16(self.props[constants.GATT_CHRC_IFACE]['Value'])
        )

    def StartNotify(self):
        if self.props[constants.GATT_CHRC_IFACE]['Notifying']:
            print('Already notifying, nothing to do')
            return
        print('Notifying on')
        self.props[constants.GATT_CHRC_IFACE]['Notifying'] = True
        self._update_temp_value()

    def StopNotify(self):
        if not self.props[constants.GATT_CHRC_IFACE]['Notifying']:
            print('Not notifying, nothing to do')
            return

        print('Notifying off')
        self.props[constants.GATT_CHRC_IFACE]['Notifying'] = False
        self._update_temp_value()


class ble:
    def __init__(self):
        self.bus = dbus.SystemBus()
        self.app = localGATT.Application()
        self.srv = localGATT.Service(1, CPU_TMP_SRVC, True)

        self.charc = TemperatureChrc(self.srv)

        self.charc.service = self.srv.path

        cpu_format_value = dbus.Array([dbus.Byte(0x0E),
                                       dbus.Byte(0xFE),
                                       dbus.Byte(0x2F),
                                       dbus.Byte(0x27),
                                       dbus.Byte(0x01),
                                       dbus.Byte(0x00),
                                       dbus.Byte(0x00)])
        self.cpu_format = localGATT.Descriptor(4,
                                               CPU_FMT_DSCP,
                                               self.charc,
                                               cpu_format_value,
                                               ['read'])

        self.app.add_managed_object(self.srv)
        self.app.add_managed_object(self.charc)
        self.app.add_managed_object(self.cpu_format)

        self.srv_mng = GATT.GattManager(adapter.list_adapters()[0])
        self.srv_mng.register_application(self.app, {})

        self.dongle = adapter.Adapter(adapter.list_adapters()[0])
        advert = advertisement.Advertisement(1, 'peripheral')

        advert.service_UUIDs = [CPU_TMP_SRVC]
        # eddystone_data = tools.url_to_advert(WEB_BLINKT, 0x10, TX_POWER)
        # advert.service_data = {EDDYSTONE: eddystone_data}
        if not self.dongle.powered:
            self.dongle.powered = True
        ad_manager = advertisement.AdvertisingManager(self.dongle.address)
        ad_manager.register_advertisement(advert, {})

    def add_call_back(self, callback):
        self.charc.PropertiesChanged = callback

    def start_bt(self):
        # self.light.StartNotify()
        self.app.start()


if __name__ == '__main__':
    print('CPU temperature is {}C'.format(get_cpu_temperature()))
    print(sint16(get_cpu_temperature()))
    pi_cpu_monitor = ble()
    pi_cpu_monitor.start_bt()

Peripheral - Nordic UART Service

This service simulates a basic UART connection over two lines, TXD and RXD.

It is based on a proprietary UART service specification by Nordic Semiconductors. Data sent to and from this service can be viewed using the nRF UART apps from Nordic Semiconductors for Android and iOS.

It uses the Bluezero peripheral file (level 10) so should be easier than the previous CPU Temperature example that was a level 100.

from gi.repository import GLib
from evdev import InputDevice, categorize, ecodes

# Bluezero modules
from bluezero import peripheral

# constants
UART_SERIVCE = '6E400001-B5A3-F393-E0A9-E50E24DCCA9E'
RX_CHARACTERISTIC = '6E400002-B5A3-F393-E0A9-E50E24DCCA9E'
TX_CHARACTERISTIC = '6E400003-B5A3-F393-E0A9-E50E24DCCA9E'


class UartService:
    def __init__(self):
        self.app = peripheral.Application()
        self.ble_uart = peripheral.Service(UART_SERIVCE,
                                           True)
        self.rx_uart = peripheral.Characteristic(RX_CHARACTERISTIC,
                                                 ['write',
                                                  'write-without-response'],
                                                 self.ble_uart)
        self.tx_uart = peripheral.Characteristic(TX_CHARACTERISTIC,
                                                 ['notify'],
                                                 self.ble_uart)
        self.rx_uart.add_write_event(self.uart_print)
        self.tx_uart.add_notify_event(print)
        self.tx_uart.StartNotify()
        self.ble_uart.add_characteristic(self.rx_uart)
        self.ble_uart.add_characteristic(self.tx_uart)
        self.app.add_service(self.ble_uart)

    @staticmethod
    def _from_bytes(data):
        return ''.join(chr(letter) for letter in data)

    @staticmethod
    def _to_bytes(word):
        return [ord(x) for x in word]

    def uart_print(self, value):
        print(self._from_bytes(value))

    def start(self):
        self.app.start()

    def stop(self):
        self.app.stop()


def detect_keys(dev_loop):
    event = dev.read_one()
    if event is not None:
        if event.code == ecodes.KEY_DOWN and event.value == 1:
            dev_loop.tx_uart.send_notify_event('down')
        elif event.code == ecodes.KEY_UP and event.value == 1:
            dev_loop.tx_uart.send_notify_event('up')
        elif event.code == ecodes.KEY_LEFT and event.value == 1:
            dev_loop.tx_uart.send_notify_event('left')
        elif event.code == ecodes.KEY_RIGHT and event.value == 1:
            dev_loop.tx_uart.send_notify_event('right')
        elif event.code == ecodes.KEY_ENTER and event.value == 1:
            dev_loop.disconnect()
            dev_loop.stop()
    return True


if __name__ == '__main__':
    dev = InputDevice('/dev/input/event0')
    uart = UartService()
    uart.app.dongle.alias = 'RPi_UART'
    GLib.idle_add(detect_keys, uart)
    uart.start()

Set-up

System Setup

Overview

Bluezero relies on the dbus interface of BlueZ. This version of Bluezero is tested wtih BlueZ version 5.43. As the BlueZ DBus API is undergoing changes between versions it is best to aim for that version when working with Bluezero. BlueZ 5.43 was chosen as the version to align with as this is the default version of BlueZ in Debian Stretch which was the latest/popular version at the time of release. This means it is likely that the Linux version you have installed will have the correct version. To check the version use bluetoothctl and type version:

$ bluetoothctl -v
5.43

More instructions are available in the Getting Started workshop using a Raspberry Pi and a BBC micro:bit

Using Bluezero for a Peripheral role or beacon

The BlueZ DBus API functionality associated with Bluetooth advertising requires the Bluetooth daemon to be run with the experimental flag. Advertising is used for Beacons and Peripheral role. Experimental mode can be switched on by default in the bluetooth.service file

Edit bluetooth.service file to add –experimental flag e.g:

sudo sed -i '/^ExecStart.*bluetoothd\s*$/ s/$/ --experimental/' /lib/systemd/system/bluetooth.service
Restart bluetoothd in experimental mode

You will need to either, reboot or run:

sudo systemctl daemon-reload
sudo service bluetooth restart

The bluetoothd should now be set to run with the experimental flag by default.

To check the bluetoothd is running with the experimental flag:

service bluetooth status

Change DBus permissions for Bluezero

An application that is in the role of a Peripheral will be registered on the System DBus. This requires for some modification of permissions so Bluezero will be using the bus name of ukBaz.bluezero. An example dbus configuration file is provided and will need to be copied to the correct location:

sudo cp examples/ukBaz.bluezero.conf /etc/dbus-1/system.d/.

Notes for getting debug information

Monitor the bluetooth hci traffic

Use Bluetooth monitor tool:

sudo btmon -w ~/btmon.log
Log of the bluetoothd

Stop bluetooth service:

service bluetooth stop

Kill the process (use ‘service bluetooth status’ to get the pid) the launch daemon with debug:

sudo /usr/libexec/bluetooth/bluetoothd -nEd |& tee ~/bluetoothd.log

Manually run bluetoothd with experimental mode with debug:

/usr/libexec/bluetooth/bluetoothd -nEd
Monitor dbus traffic

debug probe to print message bus messages:

dbus-monitor --system

python-bluezero modules

python-bluezero Modules

Level 1

micro:bit
class bluezero.microbit.Microbit(device_addr, adapter_addr=None, accelerometer_service=True, button_service=True, led_service=True, magnetometer_service=False, pin_service=False, temperature_service=True, uart_service=False)

Class to simplify interacting with a micro:bit over Bluetooth Low Energy

accelerometer

Read the values of the accelerometer on the microbit :return: return a list in the order of x, y & z

bearing

Compass bearing in degrees from North. :return: degrees in integer

button_a

Read the state of button A on a micro:bit 3 button states are defined and represented by a simple numeric enumeration: 0 = not pressed, 1 = pressed, 2 = long press. :return: integer representing button value

button_b

Read the state of button B on a micro:bit 3 button states are defined and represented by a simple numeric enumeration: 0 = not pressed, 1 = pressed, 2 = long press. :return: integer representing button value

calibrate()

Request a calibration of the magnetometer

clear_display()

Clear the LED display on the microbit

connect()

Connect to the specified micro:bit for this instance

connected

Indicate whether the remote device is currently connected.

disconnect()

Disconnect from the micro:bit

magnetometer

Exposes magnetometer data. A magnetometer measures a magnetic field such as the earth’s magnetic field in 3 axes. :return: List of x, y & z value

pin_values

Get the values of all the pins that are set as inputs :return: Dictionary (keys are pins)

pixels

Returns a list of 5 binary numbers. Each number represents a row from top to bottom :return: Example [0b01110, 0b01000, 0b10000, 0b10000, 0b01110]

quit_async()

Stops asynchronose mode :return:

run_async()

Puts the code into asynchronous mode :return:

scroll_delay

Specifies a millisecond delay to wait for in between showing each character on the display.

set_pin(pin_number, pin_input, pin_analogue)

For a given pin, set the direction and type for the microbit pin.

Parameters:
  • pin_number – Pin number of the microbit
  • pin_input – False for output, True for input
  • pin_analogue – False for digital, True for analogue
Returns:

subscribe_button_a(user_callback)

Execute user_callback on Button A being press on micro:bit :param user_callback: User callback method receiving the button state :return:

subscribe_button_b(user_callback)

Execute user_callback on Button B being press on micro:bit :param user_callback: User callback method receiving the button state :return:

subscribe_calibrate(user_callback)

Execute user_callback when calibration of magnetometer is complete :param user_callback: :return:

subscribe_pins(user_callback)

Execute user_callback on input pin being changed on micro:bit :param user_callback: :return:

subscribe_uart(user_callback)

Execute user_callback on data being received on UART service

Parameters:user_callback
Returns:
temperature

Temperature from sensors in micro:bit processors :return: Integer of temperature in Celsius

text

Specify text to be displayed. Limit of 20 characters. The content will be restricted to that number of characters. :param words:

uart

Write string to micro:bit UART service. To read from UART, use

Returns:
class bluezero.microbit.MIpower(device_addr, adapter_addr=None, accelerometer_service=True, button_service=True, led_service=True, magnetometer_service=False, pin_service=False, temperature_service=True)
beep(duration=1)

If a buzzer is attached to pin 0 then a beep will be played :param duration: time in seconds

class bluezero.microbit.BitBot(device_addr, adapter_addr=None, accelerometer_service=False, button_service=True, led_service=True, magnetometer_service=False, pin_service=True, temperature_service=False)

Class to simplify interacting with a microbit attached to a bit:bot over Bluetooth Low Energy The bit:bot is a micro:bit robot available from 4tronix.co.uk

buzzer_off()

Stop the buzzer

buzzer_on()

Play the buzzer

clean_up()

Stop bitbot and turn buzzer is off :return:

connect()

Connect to the bit:bot

connected

Returns true if bit:bot is connected

disconnect()

Disconnect from the bit:bot

drive(left=100, right=100)

Set the drive power of both wheels at same time :param left: percentage of power (negative numbers are reverse) :param right: percentage of power (negative numbers are reverse)

forward()

Spin both wheels forward

left_light_sensor

Get the value of the left light sensor

left_line_senor

Value ofthe left line sensor :return: False = No line True = Line

line_sensors

Get the value of both line sensors :return: (left, right)

reverse()

Spin both wheels backwards

right_light_sensor

Get the value of the left light sensor

right_line_sensor

Value of the right line sensor :return: False = No line True = Line

spin_left()

Spin left wheel forward and right wheel backwards so bit:bot spins

spin_right()

Spin right wheel forward and left wheel backwards so bit:bot spins

stop()

Stop both wheels of the bit:bot

Eddystone
class bluezero.eddystone_beacon.EddystoneURL(url, tx_power=8)

Create and start broadcasting a Eddystone URL beacon

The Eddystone-URL frame broadcasts a URL using a compressed encoding format in order to fit more within the limited advertisement packet. :Example:

>>> from bluezero import eddystone_beacon
>>> eddystone_beacon.EddystoneURL('https://github.com/ukBaz')
Parameters:
  • url – String containing URL e.g. (‘http://camjam.me’)
  • tx_power – Value of Tx Power of advertisement (Not implemented)

Level 10

Broadcaster

The level 10 file for creating beacons This requires BlueZ to have the experimental flag set

class bluezero.broadcaster.Beacon(adapter_addr=None)

Create a non-connectable Bluetooth instance advertising information

add_manufacturer_data(manufacturer, data)

Add manufacturer information to be used in beacon message :param manufacturer: Use numbers from Bluetooth SIG https://www.bluetooth.com/specifications/assigned-numbers/16-bit-UUIDs-for-Members :param data: Data to be sent (Limit of ??)

add_service_data(service, data)

Add service and service data to be used in beacon message :param service: Valid service UUID :param data: Data to be sent (Limit of ??)

include_tx_power(show_power=None)

Use to include TX power in advertisement. This is different to the TX power in specific beacon format (e.g. Eddystone) :param show_power: boolean value :return:

start_beacon()

Start beacon advertising

Central

Classes that represent the GATT features of a remote device.

class bluezero.central.Central(device_addr, adapter_addr=None)

Create a BLE instance taking the Central role.

add_characteristic(srv_uuid, chrc_uuid)

Specify a characteristic of interest on the remote device by using the GATT Service UUID and Characteristic UUID :param srv_uuid: 128 bit UUID :param chrc_uuid: 128 bit UUID :return:

connect(profile=None)

Initiate a connection to the remote device and load GATT database once resolved

Parameters:profile – (optional) profile to use for the connection.
connected

Indicate whether the remote device is currently connected.

disconnect()

Disconnect from the remote device.

load_gatt()

Once the remote device has been connected to and the GATT database has been resolved then it needs to be loaded. :return:

Observer
bluezero.observer.scan_eddystone(on_data=None)

Provide a callback for ‘on_data’. The callback will be run whenever an Eddystone packet is detected.

Parameters:on_data – A function to be called on Eddystone packet
Returns:None
Peripheral

Classes required to create a Bluetooth Peripheral.

Current classes include:

  • Application – Root class
  • Service – Bluetooth Service
  • Characteristic – Bluetooth Characteristic
  • Descriptor – Bluetooth Descriptor
  • Advertisement – Bluetooth Advertisement

This requires BlueZ to have the experimental flag to be enabled

bluezero.peripheral.register_ad_cb()

Advertisement registration callback.

bluezero.peripheral.register_ad_error_cb(error)

Advertisement registration error callback.

bluezero.peripheral.register_service_cb()

Service registration callback.

bluezero.peripheral.register_service_error_cb(error)

Service registration error callback.

Level 100

Adapter

Class and methods that represent a Bluetooth Adapter.

class bluezero.adapter.Adapter(adapter_addr=None)

Bluetooth Adapter Class.

This class instantiates an object that interacts with the physical Bluetooth device.

Example:
>>> from bluezero import adapter
>>> dongle = adapter.Adapter()
>>> dongle.powered = True
address

Return the adapter MAC address.

alias

Return the adapter alias.

Parameters:new_alias – the new alias of the adapter.
bt_class

Return the Bluetooth class of device.

discoverable

Discoverable state of the Adapter.

discoverabletimeout

Discoverable timeout of the Adapter.

discovering

Return whether the adapter is discovering.

get_all()

Return dictionary of all the Adapter attributes.

name

Return the adapter name.

nearby_discovery(timeout=10)

Start discovery of nearby Bluetooth devices.

pairable

pairable state of the Adapter.

Parameters:new_state – boolean.
pairabletimeout

The pairable timeout of the Adapter.

powered

power state of the Adapter.

Parameters:new_state – boolean.
quit()

Stop the EventLoop for async operations

run()

Start the EventLoop for async operations

start_discovery()

Start discovery of nearby Bluetooth devices.

Returns:True on success otherwise False
stop_discovery()

Stop scanning of nearby Bluetooth devices.

uuids

List of 128-bit UUIDs that represent available remote services.

exception bluezero.adapter.AdapterError
bluezero.adapter.list_adapters()

Return list of adapters address available on system.

Device

Class and methods that represent a remote Bluetooth Device.

Classes:

  • Device – Remote Bluetooth Device Class
class bluezero.device.Device(adapter_addr, device_addr)

Remote Bluetooth Device Class.

This class instantiates an object that interacts with a remote Bluetooth device.

RSSI

Received Signal Strength Indicator of the remote device.

(This is inquiry or advertising RSSI).

adapter

The object path of the adapter the device belongs to.

address

Return the remote device address.

alias

remote device alias

appearance

External appearance of device, as found on GAP service.

blocked

Indicate whether the remote device is seen as blocked.

bt_class

The Bluetooth class of device of the remote device.

cancel_pairing()

This method can be used to cancel a pairing operation initiated by the pair method

connect(profile=None)

Initiate a connection to the remote device.

Parameters:profile – (optional) profile to use for the connection.
connected

Indicate whether the remote device is currently connected.

disconnect()

Disconnect from the remote device.

icon

Proposed icon name.

This is set according to the freedesktop.org icon naming specification.

legacy_pairing

Indicate the legacy pairing status.

Set to true if the device only supports the pre-2.1 pairing mechanism.

manufacturer_data

Manufacturer specific advertisement data.

Keys are 16 bits Manufacturer ID followed by its byte array value.

modalias

Remote Device ID information in modalias format.

Used by the kernel and udev.

name

Return the remote device name.

pair()

Pair the device

paired

Indicate whether the remote device is paired.

service_data

Service advertisement data.

Keys are the UUIDs in string format followed by its byte array value.

services_resolved

Indicate whether or not service discovery has been resolved.

trusted

Indicate whether the remote device is seen as trusted.

tx_power

Advertised transmitted power level (inquiry or advertising).

uuids

List of 128-bit UUIDs that represent available remote services.

Advertisement

Class and methods that represent LE Advertising. This class requires BlueZ to have the experimental flag enabled

Classes:

  • Advertisement – Specifies the Advertisement Data to be broadcast
  • AdvertisingManager – register Advertisement Data which should be broadcast to devices
class bluezero.advertisement.AdvertisingManager(adapter_addr=None)

Associate the advertisement to an adapter. If no adapter specified then first adapter in list is used

register_advertisement(advertisement, options=<MagicMock name='mock()' id='139827728598464'>)

Registers an advertisement object to be sent over the LE Advertising channel :param advertisement: Advertisement object :param options: :return:

unregister_advertisement(advertisement)
This unregisters the services that has been
previously registered. The advertisement object must match the value that was used on registration
Parameters:advertisement
Returns:
bluezero.advertisement.register_ad_cb()

Advertisement registration callback.

bluezero.advertisement.register_ad_error_cb(error)

Advertisement registration error callback.

Remote Device GATT

Classes that represent the GATT features of a remote device.

class bluezero.GATT.Characteristic(adapter_addr, device_addr, srv_uuid, chrc_uuid)

Remote GATT Characteristic.

UUID

Return the value of the Characteristic UUID for this path.

Returns:string example ‘00002a00-0000-1000-8000-00805f9b34fb’
add_characteristic_cb(callback=None)

Add a callback for this characteristic.

Parameters:callback – callback function to be added.
flags

Return a list of how this characteristic’s value can be used.

Returns:list example [‘read’, ‘write’, ‘notify’]
notifying

Return whether this characteristic has notifications enabled.

Returns:Boolean
props_changed_cb(iface, changed_props, invalidated_props)

Callback indicating that properties have changed.

Parameters:
  • iface – Interface associated with the callback.
  • changed_props – Properties changed that triggered the callback.
  • invalidated_props – Unused.
read_raw_value(flags='')

Return this characteristic’s value (if allowed).

Parameters:flags – “offset”: Start offset “device”: Device path (Server only)
Returns:
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress org.bluez.Error.NotPermitted org.bluez.Error.InvalidValueLength org.bluez.Error.NotAuthorized org.bluez.Error.NotSupported
resolve_gatt()

Get the methods and properties for the discovered characteristics :return: Boolean of if characteristics have been resolved

service

Return the DBus object for this characteristic’s service.

Returns:DBus object of device
start_notify()

Initialise notifications for this characteristic.

start_notify_cb()

Callback associated with enabling notifications.

stop_notify()

Stop notifications for this characteristic.

stop_notify_cb()

Callback associated with disabling notifications.

value

The cached value of the characteristic.

This property gets updated only after a successful read request and when a notification or indication is received, upon which a PropertiesChanged signal will be emitted.

Returns:DBus byte array
write_value(value, flags='')

Write a new value to the characteristic.

Parameters:
  • value
  • flags
Returns:

class bluezero.GATT.Descriptor(adapter_addr, device_addr, srv_uuid, chrc_uuid, dscr_uuid)

Remote GATT Descriptor.

UUID

Return the value of the Descriptor UUID for this path.

Returns:string example ‘00002a00-0000-1000-8000-00805f9b34fb’
characteristic

Object path of the GATT characteristic’s descriptor.

Returns:DBus object
flags

Return a list of how this descriptor value can be used.

Returns:list example [‘read’, ‘write’]
read_raw_value(flags='')

Issue a request to read the value of the descriptor.

Returns the value if the operation was successful.

Parameters:flags – “offset”: Start offset “device”: Device path (Server only)
Returns:dbus byte array
resolve_gatt()

Get the methods and properties for the discovered Descriptors :return:

value

The cached value of the descriptor.

This property gets updated only after a successful read request, upon which a PropertiesChanged signal will be emitted.

Returns:DBus byte array
write_value(value, flags='')

Issue a request to write the value of the descriptor.

Parameters:
  • value – DBus byte array
  • flags – “offset”: Start offset “device”: Device path (Server only)
Returns:

class bluezero.GATT.GattManager(adapter_addr)

GATT Manager.

register_application(application, options)

Register an application with the GATT Manager.

Parameters:
  • application – Application object.
  • options
Returns:

unregister_application(application)

Unregister an application with the GATT Manager.

Parameters:application – Application object.
Returns:
class bluezero.GATT.Profile(adapter_addr, device_addr, profile_uuid)

Remote GATT Profile.

UUIDs

128-bit GATT service UUIDs to auto connect.

Returns:list of UUIDs
release()

Release the profile.

Returns:
class bluezero.GATT.Service(adapter_addr, device_addr, srv_uuid)

Remote GATT Service.

UUID

Return the value of the Service UUID for this path.

Returns:string for example ‘00001800-0000-1000-8000-00805f9b34fb’
device

Return the DBus object that this service belongs to.

Returns:DBus object of device
primary

Return a boolean saying if this a primary service.

Returns:boolean
resolve_gatt()

Get the methods and properties for the discovered Services :return:

bluezero.GATT.generic_error_cb(error)

Generic Error Callback function.

bluezero.GATT.register_app_cb()

Application registration callback.

bluezero.GATT.register_app_error_cb(error)

Application registration error callback.

Local Device GATT

Classes required to create a Bluetooth Peripheral.

Current classes include: - Service – Bluetooth Service - Characteristic – Bluetooth Characteristic - Descriptor – Bluetooth Descriptor

Shared

Tools

Utility functions for python-bluezero.

bluezero.tools.bytes_to_xyz(bytes)

Split 6 byte long in integers representing x, y & z :param bytes: :return:

bluezero.tools.get_fn_parameters(fn)

return the number of input parameters of the fn , None on error

bluezero.tools.int_to_uint16(value_in)

Convert integer to Unsigned 16 bit little endian integer :param value_in: Integer < 65535 (0xFFFF) :return:

bluezero.tools.int_to_uint32(value_in)

Convert integer to unsigned 32-bit (little endian) :param value_in: :return:

bluezero.tools.sint16_to_int(bytes)

Convert a signed 16-bit integer to integer :param bytes: :return:

bluezero.tools.url_to_advert(url, frame_type, tx_power)

Encode as specified https://github.com/google/eddystone/blob/master/eddystone-url/README.md :param url: :return:

Constants

Global constants file for the python-bluezero project.

This file is a single location for the different object paths that are used as constants around the python-bluezero library.

Example:
from bluezero import constants
my_function(constants.CONST_1, constants.CONST_2)
bluezero.constants.ADAPTER_INTERFACE = 'org.bluez.Adapter1'

BlueZ DBus adapter interface

bluezero.constants.BLUEZ_SERVICE_NAME = 'org.bluez'

BlueZ DBus Service Name

bluezero.constants.DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'

The DBus Object Manager interface

bluezero.constants.DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'

DBus Properties interface

bluezero.constants.DEVICE_INTERFACE = 'org.bluez.Device1'

BlueZ DBus device Interface

bluezero.constants.GATT_CHRC_IFACE = 'org.bluez.GattCharacteristic1'

BlueZ DBus GATT Characteristic Interface

bluezero.constants.GATT_DESC_IFACE = 'org.bluez.GattDescriptor1'

BlueZ DBus GATT Descriptor Interface

bluezero.constants.GATT_MANAGER_IFACE = 'org.bluez.GattManager1'

BlueZ DBus GATT manager Interface

bluezero.constants.GATT_PROFILE_IFACE = 'org.bluez.GattProfile1'

BlueZ DBus GATT Profile Interface

bluezero.constants.GATT_SERVICE_IFACE = 'org.bluez.GattService1'

BlueZ DBus GATT Service Interface

bluezero.constants.LE_ADVERTISEMENT_IFACE = 'org.bluez.LEAdvertisement1'

BlueZ DBus Advertisement Interface

bluezero.constants.LE_ADVERTISING_MANAGER_IFACE = 'org.bluez.LEAdvertisingManager1'

BlueZ DBus Advertising Manager Interface

DBus Tools

Utility functions for DBus use within Bluezero.

bluezero.dbus_tools.bluez_experimental_mode()

Return True if the BlueZ daemon service is in experimental mode :return: True if experimental enabled

bluezero.dbus_tools.bluez_version()

get the version of the BlueZ daemon being used on the system :return: String of BlueZ version

bluezero.dbus_tools.get_dbus_iface(iface, dbus_obj)

Return the DBus interface object for given interface and DBus object :param iface: :param dbus_obj: :return:

bluezero.dbus_tools.get_dbus_obj(dbus_path)

Get the the DBus object for the given path :param dbus_path: :return:

bluezero.dbus_tools.get_dbus_path(adapter=None, device=None, service=None, characteristic=None, descriptor=None)

Return a DBus path for the given properties :param adapter: Adapter address :param device: Device address :param service: GATT Service UUID :param characteristic: GATT Characteristic UUID :param descriptor: GATT Descriptor UUID :return: DBus path

bluezero.dbus_tools.get_iface(adapter=None, device=None, service=None, characteristic=None, descriptor=None)

For the given list of properties return the deepest interface :param adapter: Adapter address :param device: Device address :param service: GATT Service UUID :param characteristic: GATT Characteristic UUID :param descriptor: GATT Descriptor UUID :return: DBus Interface

bluezero.dbus_tools.get_mac_addr_from_dbus_path(path)

Return the mac addres from a dev_XX_XX_XX_XX_XX_XX dbus path

bluezero.dbus_tools.get_managed_objects()

Return the objects currently managed by the DBus Object Manager.

bluezero.dbus_tools.get_methods(adapter=None, device=None, service=None, characteristic=None, descriptor=None)

Get methods available for the specified :param adapter: Adapter Address :param device: Device Address :param service: GATT Service UUID :param characteristic: GATT Characteristic UUID :param descriptor: GATT Descriptor UUID :return: Object of the DBus methods available

bluezero.dbus_tools.get_profile_path(adapter, device, profile)

Return a DBus path for the given properties :param adapter: Adapter address :param device: Device address :param profile: :return:

bluezero.dbus_tools.get_props(adapter=None, device=None, service=None, characteristic=None, descriptor=None)

Get properties for the specified object :param adapter: Adapter Address :param device: Device Address :param service: GATT Service UUID :param characteristic: GATT Characteristic UUID :param descriptor: GATT Descriptor UUID :return: Object of the DBus properties available

bluezero.dbus_tools.interfaces_added(path, interfaces)

Callback for when an interface is added :param path: :param interfaces: :return:

bluezero.dbus_tools.properties_changed(interface, changed, invalidated, path)

Callback for when properties are changed :param interface: :param changed: :param invalidated: :param path: :return:

Async Tools

Developer Documentation

Developer Documentation

Developer Install

If you wish to help with the development of Bluezero then the recommended way of installing for edit is as follows:

git clone https://github.com/ukBaz/python-bluezero.git
cd python-bluezero
pip3 install -e .[dev]

Release Checklist

  • Check Travis-tests are passing (run_local_tests.sh)
  • Update version info (see Update Version Info)
  • Build and publish PyPI package (see Build PyPI package)
  • Check PyPI page for obvious errors
  • git tag with version number
  • Check read the docs page
Update Version Info

Use bumpversion package to update all references at once. This library tries to use Semantic Versioning

Semantic version uses three numbers that represent major.minor.patch.

The bumpversion command allows you to choose which to update. In the following example the version is being updated for a patch.

bumpversion patch setup.py
Build PyPI package

Update version information in setup.py.

To upload to PyPI:

python3 setup.py bdist_wheel sdist
twine upload dist/*
Test Build of Documentation

Do a test build of the documentation and then a visual inspection. To do a local build of the documentation:

cd docs
make clean
make html
  • readthedocs gets update from GitHub
  • readthedocs versions are based on GitHub version tags