diff --git a/bluezero/GATT.py b/bluezero/GATT.py index d0d219f..1039af9 100755 --- a/bluezero/GATT.py +++ b/bluezero/GATT.py @@ -46,7 +46,7 @@ def resolve_gatt(self): self.srv_uuid) @property - def UUID(self): + def UUID(self): # pylint: disable=invalid-name """ Return the value of the Service UUID for this path. @@ -118,7 +118,7 @@ def resolve_gatt(self): return False @property - def UUID(self): + def UUID(self): # pylint: disable=invalid-name """ Return the value of the Characteristic UUID for this path. @@ -152,7 +152,7 @@ def value(self): @value.setter def value(self, new_value): - if type(new_value) is not list: + if not isinstance(new_value, list): new_value = [new_value] self.write_value(new_value) @@ -262,7 +262,7 @@ def props_changed_cb(self, iface, changed_props, invalidated_props): if iface != constants.GATT_CHRC_IFACE: return - if not len(changed_props): + if not changed_props: return value = changed_props.get('Value', None) @@ -322,7 +322,7 @@ def resolve_gatt(self): self.dscr_uuid) @property - def UUID(self): + def UUID(self): # pylint: disable=invalid-name """ Return the value of the Descriptor UUID for this path. @@ -419,7 +419,7 @@ def release(self): self.profile_methods.Release() @property - def UUIDs(self): + def UUIDs(self): # pylint: disable=invalid-name """ 128-bit GATT service UUIDs to auto connect. diff --git a/bluezero/adapter.py b/bluezero/adapter.py index cf43bb8..9f45a84 100755 --- a/bluezero/adapter.py +++ b/bluezero/adapter.py @@ -18,6 +18,7 @@ class AdapterError(Exception): + """Custom exception for missing Bluetooth adapter""" pass @@ -26,7 +27,7 @@ def list_adapters(): return [dongle.address for dongle in Adapter.available()] -class Adapter(object): +class Adapter: """Bluetooth Adapter Class. This class instantiates an object that interacts with the physical diff --git a/bluezero/advertisement.py b/bluezero/advertisement.py index ab85d27..2f82a7b 100644 --- a/bluezero/advertisement.py +++ b/bluezero/advertisement.py @@ -86,9 +86,11 @@ def __init__(self, advert_id, ad_type): } def start(self): + """Start GLib event loop""" self.mainloop.run() def stop(self): + """Stop GLib event loop""" self.mainloop.quit() def get_path(self): @@ -98,7 +100,7 @@ def get_path(self): @dbus.service.method(constants.LE_ADVERTISEMENT_IFACE, in_signature='', out_signature='') - def Release(self): + def Release(self): # pylint: disable=invalid-name """ This method gets called when the service daemon removes the Advertisement. A client can use it to do @@ -110,13 +112,13 @@ def Release(self): pass @property - def service_UUIDs(self): + def service_UUIDs(self): # pylint: disable=invalid-name """List of UUIDs that represent available services.""" return self.Get(constants.LE_ADVERTISEMENT_IFACE, 'ServiceUUIDs') @service_UUIDs.setter - def service_UUIDs(self, UUID): + def service_UUIDs(self, UUID): # pylint: disable=invalid-name self.Set(constants.LE_ADVERTISEMENT_IFACE, 'ServiceUUIDs', UUID) @@ -125,7 +127,7 @@ def manufacturer_data(self): """Manufacturer Data to be broadcast (Currently not supported)""" pass - def solicit_UUIDs(self): + def solicit_UUIDs(self): # pylint: disable=invalid-name """Manufacturer Data to be broadcast (Currently not supported)""" pass @@ -137,10 +139,10 @@ def service_data(self): @service_data.setter def service_data(self, data): - for UUID in data: + for uuid in data: self.Set(constants.LE_ADVERTISEMENT_IFACE, 'ServiceData', - {UUID: dbus.Array(data[UUID], signature='y')}) + {uuid: dbus.Array(data[uuid], signature='y')}) @property def include_tx_power(self): @@ -174,7 +176,7 @@ def appearance(self, appearance): @dbus.service.method(constants.DBUS_PROP_IFACE, in_signature='s', out_signature='a{sv}') - def GetAll(self, interface_name): + def GetAll(self, interface_name): # pylint: disable=invalid-name """Return the advertisement properties. This method is registered with the D-Bus at @@ -219,7 +221,8 @@ def GetAll(self, interface_name): @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v') - def Get(self, interface_name, property_name): + def Get(self, interface_name, # pylint: disable=invalid-name + property_name): """DBus API for getting a property value""" if interface_name != constants.LE_ADVERTISEMENT_IFACE: @@ -234,7 +237,8 @@ def Get(self, interface_name, property_name): @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ssv', out_signature='') - def Set(self, interface_name, property_name, value, *args, **kwargs): + def Set(self, interface_name, property_name, value, + *args, **kwargs): # pylint: disable=invalid-name """Standard D-Bus API for setting a property value""" try: diff --git a/bluezero/async_tools.py b/bluezero/async_tools.py index abf1e5d..bb826c4 100644 --- a/bluezero/async_tools.py +++ b/bluezero/async_tools.py @@ -1,3 +1,6 @@ +""" +Collection of functions to work with the GLib Event Loop +""" # Main eventloop import import dbus import dbus.mainloop.glib @@ -9,6 +12,7 @@ def add_timer_ms(time, callback, data=None): + """Call given callback every x milliseconds""" if data: GLib.timeout_add(time, callback, data) else: @@ -16,6 +20,7 @@ def add_timer_ms(time, callback, data=None): def add_timer_seconds(time, callback, data=None): + """Call given callback every x seconds""" if data: GLib.timeout_add_seconds(time, callback, data) else: @@ -23,6 +28,7 @@ def add_timer_seconds(time, callback, data=None): class EventLoop: + """Facade class to help with using GLib event loop""" # def generic_error_cb(self, error): # """Generic Error Callback function.""" # logger.error('D-Bus call failed: ' + str(error)) @@ -36,10 +42,13 @@ def __init__(self): self.mainloop = GLib.MainLoop() def run(self): + """Run event loop""" self.mainloop.run() def quit(self): + """Stop event loop""" self.mainloop.quit() def is_running(self): + """Check if event loop is running""" self.mainloop.is_running() diff --git a/bluezero/broadcaster.py b/bluezero/broadcaster.py index 378d414..a897c1e 100644 --- a/bluezero/broadcaster.py +++ b/bluezero/broadcaster.py @@ -53,7 +53,7 @@ def include_tx_power(self, show_power=None): :return: """ if show_power is None: - self.broadcaster.include_tx_power + return self.broadcaster.include_tx_power else: self.broadcaster.include_tx_power = show_power diff --git a/bluezero/central.py b/bluezero/central.py index 786969b..36a33cd 100755 --- a/bluezero/central.py +++ b/bluezero/central.py @@ -29,7 +29,7 @@ def __init__(self, device_addr, adapter_addr=None): @staticmethod def available(adapter_address=None): """Generator for getting a list of devices""" - return device.Device.available() + return device.Device.available(adapter_address) def add_characteristic(self, srv_uuid, chrc_uuid): """ @@ -56,7 +56,7 @@ def load_gatt(self): available = chrc.resolve_gatt() if available: logger.info('Service: %s and characteristic: %s added', - (chrc.srv_uuid, chrc.chrc_uuid)) + chrc.srv_uuid, chrc.chrc_uuid) else: logger.warning('Service: %s and characteristic: %s not ' 'available on device: %s', chrc.srv_uuid, @@ -64,6 +64,7 @@ def load_gatt(self): @property def services_available(self): + """Get a list of Service UUIDs available on this device""" return self.rmt_device.services_available @property @@ -97,7 +98,9 @@ def disconnect(self): self.rmt_device.disconnect() def run(self): + """Start event loop""" self.dongle.run() def quit(self): + """Stop event loop""" self.dongle.quit() diff --git a/bluezero/dbus_tools.py b/bluezero/dbus_tools.py index 837613b..02caabe 100755 --- a/bluezero/dbus_tools.py +++ b/bluezero/dbus_tools.py @@ -17,11 +17,13 @@ def bluez_version(): """ get the version of the BlueZ daemon being used on the system + :return: String of BlueZ version """ - p = subprocess.Popen(['bluetoothctl', '-v'], stdout=subprocess.PIPE) - ver = p.communicate() - return str(ver[0].decode().rstrip()) + cmd = ['bluetoothctl', '-v'] + cmd_output = subprocess.run(cmd, capture_output=True, check=True) + version = cmd_output.stdout.decode('utf-8').split() + return version[1] def bluez_experimental_mode(): @@ -92,13 +94,22 @@ def get_managed_objects(): def get_mac_addr_from_dbus_path(path): + """ + [Deprecated] Function to get the address of a remote device from + a given D-Bus path. Path must include device part + (e.g. dev_XX_XX_XX_XX_XX_XX) + """ logger.warning('get_mac_addr_from_dbus_path has been deprecated and has' 'been replaced with get_device_address_from_dbus_path') return get_device_address_from_dbus_path(path) def get_device_address_from_dbus_path(path): - """Return the mac address from a dev_XX_XX_XX_XX_XX_XX dbus path""" + """ + [Deprecated] Function to get the address of a remote device from + a given D-Bus path. Path must include device part + (e.g. dev_XX_XX_XX_XX_XX_XX) + """ for path_elem in path.split('/'): if path_elem.startswith('dev_'): return path_elem.replace("dev_", '').replace("_", ":") @@ -323,6 +334,12 @@ def get_props(adapter=None, def get_services(path_obj): + """ + Return a list of GATT Service UUIDs for a given Bluetooth device D-Bus path + + :param path_obj: D-Bus path for remote Bluetooth device + :return: List of GATT Service UUIDs + """ found_services = [] valid_structure = re.match(r'/org/bluez/hci\d+/dev(_([0-9A-Fa-f]){2}){6}', path_obj) @@ -378,8 +395,10 @@ def get(dbus_prop_obj, dbus_iface, prop_name, default=None): def str_to_dbusarray(word): + """Helper function to represent Python string as D-Dbus Byte array""" return dbus.Array([dbus.Byte(ord(letter)) for letter in word], 'y') def bytes_to_dbusarray(bytesarray): + """Helper function to represent Python bytearray as D-Bus Byte array""" return dbus.Array([dbus.Byte(elem) for elem in bytesarray], 'y') diff --git a/bluezero/device.py b/bluezero/device.py index 436f207..4d5aa10 100755 --- a/bluezero/device.py +++ b/bluezero/device.py @@ -16,7 +16,7 @@ logger = tools.create_module_logger(__name__) -class Device(object): +class Device: """Remote Bluetooth Device Class. This class instantiates an object that interacts with a remote @@ -207,7 +207,7 @@ def modalias(self): constants.DEVICE_INTERFACE, 'Modalias') @property - def RSSI(self): + def RSSI(self): # pylint: disable=invalid-name """ Received Signal Strength Indicator of the remote device. diff --git a/bluezero/localGATT.py b/bluezero/localGATT.py index a4ec206..84bc8ff 100644 --- a/bluezero/localGATT.py +++ b/bluezero/localGATT.py @@ -73,7 +73,7 @@ def __init__(self, device_id=None): @dbus.service.method(constants.DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}') - def GetManagedObjects(self): + def GetManagedObjects(self): # pylint: disable=invalid-name """Get all objects that are managed by the application. Return type is a dictionary whose keys are each registered object and @@ -81,27 +81,29 @@ def GetManagedObjects(self): """ response = {} - for object in self.managed_objs: - for iface in object.props.keys(): - response[object.get_path()] = {iface: object.GetAll(iface)} + for obj in self.managed_objs: + for iface in obj.props.keys(): + response[obj.get_path()] = {iface: obj.GetAll(iface)} return response - def add_managed_object(self, object): + def add_managed_object(self, service_obj): """Add a service to the list of services offered by the Application. - :param object: Python object of dbus path to be managed + :param service_obj: Python object of dbus path to be managed """ - self.managed_objs.append(object) + self.managed_objs.append(service_obj) def get_path(self): """Return the DBus object path""" return dbus.ObjectPath(self.path) def start(self): + """Start event loop""" self.eventloop.run() def stop(self): + """Stop event loop""" self.eventloop.quit() @@ -146,7 +148,7 @@ def get_path(self): @dbus.service.method(constants.DBUS_PROP_IFACE, in_signature='s', out_signature='a{sv}') - def GetAll(self, interface_name): + def GetAll(self, interface_name): # pylint: disable=invalid-name """Return the service properties. This method is registered with the D-Bus at @@ -169,7 +171,8 @@ def GetAll(self, interface_name): @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v') - def Get(self, interface_name, property_name): + def Get(self, # pylint: disable=invalid-name + interface_name, property_name): """DBus API for getting a property value""" if interface_name != constants.GATT_SERVICE_IFACE: @@ -184,7 +187,8 @@ def Get(self, interface_name, property_name): @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ssv', out_signature='') - def Set(self, interface_name, property_name, value, *args, **kwargs): + def Set(self, interface_name, # pylint: disable=invalid-name + property_name, value, *args, **kwargs): """Standard D-Bus API for setting a property value""" try: @@ -208,13 +212,14 @@ def Set(self, interface_name, property_name, value, *args, **kwargs): @dbus.service.signal(constants.DBUS_PROP_IFACE, signature='sa{sv}as') - def PropertiesChanged(self, interface, changed, invalidated): + def PropertiesChanged(self, # pylint: disable=invalid-name + interface, changed, invalidated): """Emit a Properties Changed notification signal. This signal is registered with the D-Bus at ``org.freedesktop.DBus.Properties``. """ - logger.debug('Service properties changed: ', + logger.debug('Service properties changed: %s, %s, %s', interface, changed, invalidated) @@ -251,8 +256,8 @@ def __init__(self, characteristic_id, :param flags: """ # Setup D-Bus object paths and register service - PATH_BASE = service_obj.get_path() + '/char' - self.path = PATH_BASE + str('{0:04d}'.format(characteristic_id)) + path_base = service_obj.get_path() + '/char' + self.path = path_base + str('{0:04d}'.format(characteristic_id)) self.bus = dbus.SystemBus() dbus.service.Object.__init__(self, self.bus, self.path) self.props = { @@ -274,12 +279,15 @@ def get_path(self): return dbus.ObjectPath(self.path) def add_call_back(self, callback): - self.PropertiesChanged = callback + """ + Add function to be called when D-Bus PropertiesChanged signal is sent + """ + self.PropertiesChanged = callback # pylint: disable=invalid-name @dbus.service.method(constants.DBUS_PROP_IFACE, in_signature='s', out_signature='a{sv}') - def GetAll(self, interface_name): + def GetAll(self, interface_name): # pylint: disable=invalid-name """Return the service properties. This method is registered with the D-Bus at @@ -302,7 +310,8 @@ def GetAll(self, interface_name): @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v') - def Get(self, interface_name, property_name): + def Get(self, # pylint: disable=invalid-name + interface_name, property_name): """DBus API for getting a property value. This method is registered with the D-Bus at @@ -324,7 +333,8 @@ def Get(self, interface_name, property_name): @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ssv', out_signature='') - def Set(self, interface_name, property_name, value, *args, **kwargs): + def Set(self, interface_name, # pylint: disable=invalid-name + property_name, value, *args, **kwargs): """Standard D-Bus API for setting a property value""" if property_name not in self.props[constants.GATT_CHRC_IFACE]: @@ -341,7 +351,8 @@ def Set(self, interface_name, property_name, value, *args, **kwargs): @dbus.service.signal(constants.DBUS_PROP_IFACE, signature='sa{sv}as') - def PropertiesChanged(self, interface, changed, invalidated): + def PropertiesChanged(self, # pylint: disable=invalid-name + interface, changed, invalidated): """Emit a Properties Changed notification signal. This signal is registered with the D-Bus at @@ -351,7 +362,7 @@ def PropertiesChanged(self, interface, changed, invalidated): @dbus.service.method(constants.GATT_CHRC_IFACE, in_signature='a{sv}', out_signature='ay') - def ReadValue(self, options): + def ReadValue(self, options): # pylint: disable=invalid-name """ DBus method for getting the characteristic value :return: value @@ -360,7 +371,7 @@ def ReadValue(self, options): @dbus.service.method(constants.GATT_CHRC_IFACE, in_signature='aya{sv}', out_signature='') - def WriteValue(self, value, options): + def WriteValue(self, value, options): # pylint: disable=invalid-name """ DBus method for setting the characteristic value :return: value @@ -369,7 +380,7 @@ def WriteValue(self, value, options): @dbus.service.method(constants.GATT_CHRC_IFACE, in_signature='', out_signature='') - def StartNotify(self): + def StartNotify(self): # pylint: disable=invalid-name """ DBus method for enabling notifications of the characteristic value. :return: value @@ -384,7 +395,7 @@ def StartNotify(self): @dbus.service.method(constants.GATT_CHRC_IFACE, in_signature='', out_signature='') - def StopNotify(self): + def StopNotify(self): # pylint: disable=invalid-name """ DBus method for disabling notifications of the characteristic value. :return: value @@ -430,8 +441,8 @@ def __init__(self, :param flags: Flags specifying access permissions """ # Setup D-Bus object paths and register service - PATH_BASE = characteristic_obj.get_path() + '/desc' - self.path = PATH_BASE + str('{0:04d}'.format(descriptor_id)) + path_base = characteristic_obj.get_path() + '/desc' + self.path = path_base + str('{0:04d}'.format(descriptor_id)) self.bus = dbus.SystemBus() dbus.service.Object.__init__(self, self.bus, self.path) self.props = { @@ -453,7 +464,7 @@ def get_path(self): @dbus.service.method(constants.DBUS_PROP_IFACE, in_signature='s', out_signature='a{sv}') - def GetAll(self, interface_name): + def GetAll(self, interface_name): # pylint: disable=invalid-name """Return the descriptor properties. This method is registered with the D-Bus at @@ -476,7 +487,8 @@ def GetAll(self, interface_name): @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v') - def Get(self, interface_name, property_name): + def Get(self, # pylint: disable=invalid-name + interface_name, property_name): """DBus API for getting a property value. This method is registered with the D-Bus at @@ -498,7 +510,8 @@ def Get(self, interface_name, property_name): @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ssv', out_signature='') - def Set(self, interface_name, property_name, value, *args, **kwargs): + def Set(self, interface_name, # pylint: disable=invalid-name + property_name, value, *args, **kwargs): """Standard D-Bus API for setting a property value""" if property_name not in self.props[constants.GATT_DESC_IFACE]: @@ -515,7 +528,8 @@ def Set(self, interface_name, property_name, value, *args, **kwargs): @dbus.service.signal(constants.DBUS_PROP_IFACE, signature='sa{sv}as') - def PropertiesChanged(self, interface, changed, invalidated): + def PropertiesChanged(self, # pylint: disable=invalid-name + interface, changed, invalidated): """Emit a Properties Changed notification signal. This signal is registered with the D-Bus at @@ -525,7 +539,7 @@ def PropertiesChanged(self, interface, changed, invalidated): @dbus.service.method(constants.GATT_DESC_IFACE, in_signature='a{sv}', out_signature='ay') - def ReadValue(self, options): + def ReadValue(self, options): # pylint: disable=invalid-name """ DBus method for getting the characteristic value :return: value @@ -534,7 +548,7 @@ def ReadValue(self, options): @dbus.service.method(constants.GATT_DESC_IFACE, in_signature='aya{sv}', out_signature='') - def WriteValue(self, value, options): + def WriteValue(self, value, options): # pylint: disable=invalid-name """ DBus method for setting the descriptor value :return: diff --git a/bluezero/media_player.py b/bluezero/media_player.py index d9c79b7..3db77f9 100644 --- a/bluezero/media_player.py +++ b/bluezero/media_player.py @@ -1,3 +1,4 @@ +"""Access BlueZ Media Player functionality""" import dbus # python-bluezero imports @@ -9,6 +10,7 @@ class MediaPlayerError(Exception): + """Custom exception""" pass @@ -131,7 +133,7 @@ def subtype(self): """Return the player subtype""" return self.player_props.Get(constants.MEDIA_PLAYER_IFACE, 'Subtype') - def type(self, type): + def type(self, player_type): """Player type Possible values: "Audio" @@ -140,7 +142,7 @@ def type(self, type): "Video Broadcasting" """ self.player_props.Set( - constants.MEDIA_PLAYER_IFACE, 'Type', type) + constants.MEDIA_PLAYER_IFACE, 'Type', player_type) @property def position(self): diff --git a/bluezero/microbit.py b/bluezero/microbit.py index 95b3ecf..fe25e28 100644 --- a/bluezero/microbit.py +++ b/bluezero/microbit.py @@ -75,12 +75,12 @@ def __init__(self, device_addr, adapter_addr=None, **kwargs): :param adapter_addr: Optional unless you have more than one adapter on your machine """ - LEGACY_PARAMS = ['accelerometer_service', 'button_service', + legacy_params = ['accelerometer_service', 'button_service', 'led_service', 'magnetometer_service', 'pin_service', 'temperature_service', 'uart_service'] for kwarg in kwargs: - if kwarg in LEGACY_PARAMS: + if kwarg in legacy_params: logger.warning('The parameter %s has been deprecated. There ' 'is no longer a requirement to specify which ' 'services the micro:bit has.\nYou will get an ' @@ -542,7 +542,6 @@ def pin_values(self): :return: Dictionary (keys are pins) """ - xx = self._io_pin_data.value return_dict = {} values = self._io_pin_data.value for i in range(0, len(values), 2): @@ -560,7 +559,7 @@ def _pin_pwm_control(self): :param period: Period is in microseconds and is an unsigned integer :return: """ - self._io_pin_pwm.value + return self._io_pin_pwm.value @_pin_pwm_control.setter def _pin_pwm_control(self, data): @@ -627,6 +626,7 @@ def _uart_read(self, iface, changed_props, invalidated_props): @property def on_disconnect(self): + """Add callback for on_disconnect action""" return self.ubit.dongle.on_disconnect @on_disconnect.setter @@ -651,6 +651,13 @@ def quit_async(self): class MIpower(Microbit): + """ + Initialization of an instance of a remote micro:bit + with a MI:power board attached + + :param device_addr: Connect to a BLE device with this address + :param adapter_addr: Use the adapter with this address + """ def __init__(self, device_addr, adapter_addr=None, accelerometer_service=True, button_service=True, @@ -659,13 +666,7 @@ def __init__(self, device_addr, adapter_addr=None, pin_service=False, temperature_service=True ): - """ - Initialization of an instance of a remote bit:bot - with a MI:power board attached - :param device_addr: Connect to a BLE device with this address - :param adapter_addr: Use the adapter with this address - """ Microbit.__init__(self, device_addr, adapter_addr, accelerometer_service=accelerometer_service, button_service=button_service, @@ -980,6 +981,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): self.clean_up() def clean_up(self): + """Put BitCommander back to known state""" if self.connected: pass @@ -1037,7 +1039,8 @@ def joystick(self): """ values = self.ubit.pin_values if '1' in values: - x, y, z = values['1'], values['0'], values['8'] + x, y, z = (values['1'], # pylint: disable=invalid-name + values['0'], values['8']) return x, y, z @property diff --git a/bluezero/observer.py b/bluezero/observer.py index b5a1358..8754644 100644 --- a/bluezero/observer.py +++ b/bluezero/observer.py @@ -1,4 +1,6 @@ -from gi.repository import GLib +""" +Code for create a Bluetooth Low Energy Observer (beacon scanner) application +""" from collections import namedtuple import uuid import dbus @@ -14,7 +16,8 @@ EddyURL = namedtuple('EddyURL', ['url', 'tx_pwr', 'rssi']) EddyUID = namedtuple('EddyUID', ['namespace', 'instance', 'tx_pwr', 'rssi']) -iBeacon = namedtuple('iBeacon', ['UUID', 'major', 'minor', 'tx_pwr', 'rssi']) +iBeacon = namedtuple('iBeacon', # pylint: disable=invalid-name + ['UUID', 'major', 'minor', 'tx_pwr', 'rssi']) AltBeacon = namedtuple('AltBeacon', ['UUID', 'major', 'minor', 'tx_pwr', 'rssi']) diff --git a/bluezero/peripheral.py b/bluezero/peripheral.py index 4b3b282..65a13d8 100755 --- a/bluezero/peripheral.py +++ b/bluezero/peripheral.py @@ -10,6 +10,8 @@ This requires BlueZ to have the experimental flag to be enabled """ +# python import +import array # D-Bus imports import dbus @@ -23,8 +25,6 @@ from bluezero import adapter from bluezero import constants -# array import -import array logger = tools.create_module_logger(__name__) @@ -148,7 +148,7 @@ def get_primary_service(self): @dbus.service.method(constants.DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}') - def GetManagedObjects(self): + def GetManagedObjects(self): # pylint: disable=invalid-name """Get all objects that are managed by the application. Return type is a dictionary whose keys are each registered object and @@ -280,7 +280,7 @@ class Service(dbus.service.Object): PATH_BASE = '/ukBaz/bluezero/service1' - def __init__(self, uuid, primary, type='peripheral'): + def __init__(self, uuid, primary, ad_type='peripheral'): """Default initialiser. 1. Registers the service on the D-Bus. @@ -299,15 +299,15 @@ def __init__(self, uuid, primary, type='peripheral'): # Setup UUID, primary flag self.uuid = uuid self.primary = primary - self.type = type + self.type = ad_type self.service_data = None # Initialise characteristics within the service self.characteristics = [] - def UUID(self): + def UUID(self): # pylint: disable=invalid-name """Return Service UUID""" - return self.service.Get(constants.GATT_SERVICE_IFACE, 'UUID') + return self.Get(constants.GATT_SERVICE_IFACE, 'UUID') def get_properties(self): """Return a dictionary of the service properties. @@ -367,7 +367,7 @@ def get_characteristics(self): @dbus.service.method(constants.DBUS_PROP_IFACE, in_signature='s', out_signature='a{sv}') - def GetAll(self, interface): + def GetAll(self, interface): # pylint: disable=invalid-name """Return the service properties. This method is registered with the D-Bus at @@ -385,7 +385,7 @@ def GetAll(self, interface): @dbus.service.method(constants.DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}') - def GetManagedObjects(self): + def GetManagedObjects(self): # pylint: disable=invalid-name """Get all objects that are managed by the service. Return type is a dictionary whose keys are each registered object and @@ -506,7 +506,7 @@ def get_descriptors(self): @dbus.service.method(constants.DBUS_PROP_IFACE, in_signature='s', out_signature='a{sv}') - def GetAll(self, interface): + def GetAll(self, interface): # pylint: disable=invalid-name """Return the characteristic properties. This method is registered with the D-Bus at @@ -525,7 +525,7 @@ def GetAll(self, interface): @dbus.service.method(constants.GATT_CHRC_IFACE, in_signature='a{sv}', out_signature='ay') - def ReadValue(self, options): + def ReadValue(self, options): # pylint: disable=invalid-name """Return the characteristic value. This method is registered with the D-Bus at @@ -540,7 +540,7 @@ def ReadValue(self, options): return self.value @dbus.service.method(constants.GATT_CHRC_IFACE, in_signature='aya{sv}') - def WriteValue(self, value, options): + def WriteValue(self, value, options): # pylint: disable=invalid-name """Set the characteristic value. This method is registered with the D-Bus at @@ -569,7 +569,7 @@ def add_write_event(self, object_id): self.write_cb = object_id @dbus.service.method(constants.GATT_CHRC_IFACE) - def StartNotify(self): + def StartNotify(self): # pylint: disable=invalid-name """Start BLE notifications for the characteristic. This method is registered with the D-Bus at @@ -584,7 +584,7 @@ def StartNotify(self): self.notify_cb() @dbus.service.method(constants.GATT_CHRC_IFACE) - def StopNotify(self): + def StopNotify(self): # pylint: disable=invalid-name """Stop BLE notifications for the characteristic. This method is registered with the D-Bus at @@ -712,7 +712,7 @@ def get_path(self): @dbus.service.method(constants.DBUS_PROP_IFACE, in_signature='s', out_signature='a{sv}') - def GetAll(self, interface): + def GetAll(self, interface): # pylint: disable=invalid-name """Return the descriptor properties. This method is registered with the D-Bus at @@ -731,7 +731,7 @@ def GetAll(self, interface): @dbus.service.method(constants.GATT_DESC_IFACE, in_signature='a{sv}', out_signature='ay') - def ReadValue(self, options): + def ReadValue(self, options): # pylint: disable=invalid-name """Return the descriptor value. This method is registered with the D-Bus at @@ -743,7 +743,7 @@ def ReadValue(self, options): raise NotSupportedException() @dbus.service.method(constants.GATT_DESC_IFACE, in_signature='aya{sv}') - def WriteValue(self, value, options): + def WriteValue(self, value, options): # pylint: disable=invalid-name """Set the descriptor value. This method is registered with the D-Bus at @@ -930,7 +930,7 @@ def add_service_data(self, uuid, data): @dbus.service.method(constants.DBUS_PROP_IFACE, in_signature='s', out_signature='a{sv}') - def GetAll(self, interface): + def GetAll(self, interface): # pylint: disable=invalid-name """Return the advert properties. This method is registered with the D-Bus at @@ -950,7 +950,7 @@ def GetAll(self, interface): @dbus.service.method(constants.LE_ADVERTISEMENT_IFACE, in_signature='', out_signature='') - def Release(self): + def Release(self): # pylint: disable=invalid-name """Release an advert. This method is registered with the D-Bus at diff --git a/bluezero/tools.py b/bluezero/tools.py index 30e08e7..f8a48ed 100755 --- a/bluezero/tools.py +++ b/bluezero/tools.py @@ -7,6 +7,7 @@ def int_to_uint16(value_in): """ Convert integer to Unsigned 16 bit little endian integer + :param value_in: Integer < 65535 (0xFFFF) :return: """ @@ -16,24 +17,26 @@ def int_to_uint16(value_in): return [little_byte, big_byte] -def sint16_to_int(bytes): +def sint16_to_int(byte_array): """ Convert a signed 16-bit integer to integer - :param bytes: + + :param byte_array: :return: """ - return int.from_bytes(bytes, byteorder='little', signed=True) + return int.from_bytes(byte_array, byteorder='little', signed=True) -def bytes_to_xyz(bytes): +def bytes_to_xyz(byte_array): """ Split 6 byte long in integers representing x, y & z - :param bytes: + + :param byte_array: :return: """ - x = sint16_to_int(bytes[0:2]) / 1000 - y = sint16_to_int(bytes[2:4]) / 1000 - z = sint16_to_int(bytes[4:6]) / 1000 + x = sint16_to_int(byte_array[0:2]) / 1000 # pylint: disable=invalid-name + y = sint16_to_int(byte_array[2:4]) / 1000 # pylint: disable=invalid-name + z = sint16_to_int(byte_array[4:6]) / 1000 # pylint: disable=invalid-name return [x, y, z] @@ -41,15 +44,18 @@ def bytes_to_xyz(bytes): def int_to_uint32(value_in): """ Convert integer to unsigned 32-bit (little endian) + :param value_in: :return: """ - return [octet for octet in value_in.to_bytes(4, - byteorder='little', - signed=False)] + return list(value_in.to_bytes(4, byteorder='little', signed=False)) def bitwise_or_2lists(list1, list2): + """ + Takes two bit patterns of equal length and performs the logical + inclusive OR operation on each pair of corresponding bits + """ list_len = len(list2) return_list = [None] * list_len for i in range(list_len): @@ -58,6 +64,10 @@ def bitwise_or_2lists(list1, list2): def bitwise_and_2lists(list1, list2): + """ + Takes two bit patterns of equal length and performs the logical + inclusive AND operation on each pair of corresponding bits + """ list_len = len(list2) return_list = [None] * list_len for i in range(list_len): @@ -66,6 +76,10 @@ def bitwise_and_2lists(list1, list2): def bitwise_xor_2lists(list1, list2): + """ + Takes two bit patterns of equal length and performs the logical + inclusive XOR operation on each pair of corresponding bits + """ list_len = len(list1) return_list = [None] * list_len for i in range(list_len): @@ -77,6 +91,7 @@ def 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: """ @@ -94,19 +109,19 @@ def url_to_advert(url, frame_type, tx_power): ) encode_search = True - for x in prefix: - if x in url and encode_search is True: + for domain in prefix: + if domain in url and encode_search is True: # print('match prefix ' + url) - prefix_sel = prefix.index(x) + prefix_sel = prefix.index(domain) prefix_start = url.index(prefix[prefix_sel]) prefix_end = len(prefix[prefix_sel]) + prefix_start encode_search = False encode_search = True - for y in suffix: - if y in url and encode_search is True: + for tld in suffix: + if tld in url and encode_search is True: # print('match suffix ' + y) - suffix_sel = suffix.index(y) + suffix_sel = suffix.index(tld) suffix_start = url.index(suffix[suffix_sel]) suffix_end = len(suffix[suffix_sel]) + suffix_start encode_search = False @@ -116,20 +131,20 @@ def url_to_advert(url, frame_type, tx_power): if suffix_start is None: suffix_start = len(url) service_data.extend([prefix_sel]) - for x in range(prefix_end, suffix_start): - service_data.extend([ord(url[x])]) + for domain in range(prefix_end, suffix_start): + service_data.extend([ord(url[domain])]) elif suffix_end == len(url): service_data.extend([prefix_sel]) - for x in range(prefix_end, suffix_start): - service_data.extend([ord(url[x])]) + for domain in range(prefix_end, suffix_start): + service_data.extend([ord(url[domain])]) service_data.extend([suffix_sel]) else: service_data.extend([prefix_sel]) - for x in range(prefix_end, suffix_start): - service_data.extend([ord(url[x])]) + for domain in range(prefix_end, suffix_start): + service_data.extend([ord(url[domain])]) service_data.extend([suffix_sel]) - for x in range(suffix_end, len(url)): - service_data.extend([ord(url[x])]) + for domain in range(suffix_end, len(url)): + service_data.extend([ord(url[domain])]) return service_data @@ -140,12 +155,13 @@ def get_fn_parameters(fn): def create_module_logger(module_name): + """helper function to create logger in Bluezero modules""" logger = logging.getLogger(module_name) - ch = logging.StreamHandler() + strm_hndlr = logging.StreamHandler() formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - ch.setFormatter(formatter) - logger.addHandler(ch) + strm_hndlr.setFormatter(formatter) + logger.addHandler(strm_hndlr) return logger # Improve the above logger helper so that report level can be easily changed diff --git a/docs/conf.py b/docs/conf.py index 60f8df3..66cf840 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -72,9 +72,9 @@ def __getattr__(cls, name): # built documents. # # The short X.Y version. -version = u'0.3.0' +version = u'0.4.0' # The full version, including alpha/beta/rc tags. -release = u'0.3.0' +release = u'0.4.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/system_setup.rst b/docs/system_setup.rst index e295995..49cc885 100755 --- a/docs/system_setup.rst +++ b/docs/system_setup.rst @@ -29,10 +29,18 @@ 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:: +and will need to be copied to the correct location. + +If you have done a ``git clone`` of the library then the command is:: sudo cp examples/ukBaz.bluezero.conf /etc/dbus-1/system.d/. +If you have done a ``pip3 install bluezero`` then you will need to get a copy +of the example D-Bus configuration file. This can be done with:: + + curl https://raw.githubusercontent.com/ukBaz/python-bluezero/master/examples/ukBaz.bluezero.conf > ukBaz.bluezero.conf + sudo cp ukBaz.bluezero.conf /etc/dbus-1/system.d/ + Notes for getting debug information ----------------------------------- diff --git a/setup.cfg b/setup.cfg index 3496087..8d9f747 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,7 @@ [bumpversion] -current_version = 0.3.0 +current_version = 0.4.0 tag = True -tag_name = {new_version} +tag_name = v{new_version} [bdist_wheel] universal = 1 @@ -12,3 +12,6 @@ universal = 1 [coverage:run] source = bluezero, examples + +[metadata] +license_files = LICENSE diff --git a/setup.py b/setup.py index 67424ae..19ec444 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ # Versions should comply with PEP440. For a discussion on single-sourcing # the version across setup.py and the project code, see # https://packaging.python.org/en/latest/single_source_version.html - version='0.3.0', + version='0.4.0', description='Python library for Bluetooth Low Energy (BLE) on Linux', long_description=long_description, @@ -67,8 +67,7 @@ # Specify the Python versions you support here. In particular, ensure # that you indicate whether you support Python 2, Python 3 or both. - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3' + 'Programming Language :: Python :: 3 :: Only' ], # What does your project relate to? diff --git a/tests/test_dbus_tools.py b/tests/test_dbus_tools.py index f2dd54f..908e47e 100644 --- a/tests/test_dbus_tools.py +++ b/tests/test_dbus_tools.py @@ -1,3 +1,4 @@ +import subprocess import unittest from unittest.mock import MagicMock from unittest.mock import patch @@ -37,7 +38,8 @@ def setUp(self): } self.dbus_mock.Interface.return_value.GetManagedObjects.return_value = tests.obj_data.full_ubits self.process_mock.check_output = self.get_bluetooth_service - self.process_mock.Popen.return_value.communicate.return_value = (b'5.43\n', None) + self.process_mock.run.return_value = subprocess.CompletedProcess( + args=['bluetoothctl', '-v'], returncode=0, stdout=b'bluetoothctl: 5.53\n', stderr=b'') self.module_patcher = patch.dict('sys.modules', modules) self.module_patcher.start() from bluezero import dbus_tools @@ -78,7 +80,7 @@ def test_profile_path(self): def test_bluez_version(self): bluez_ver = self.module_under_test.bluez_version() - self.assertEqual('5.43', bluez_ver) + self.assertEqual('5.53', bluez_ver) def test_bluez_service_experimental(self): TestDbusModuleCalls.experimental = True