def __init__(self, advert_id, ad_type): """Default initialiser. Creates the interface to the specified advertising data. The DBus path must be specified. :param advert_id: Unique ID of advertisement. :param ad_type: Possible values: "broadcast" or "peripheral" """ # Setup D-Bus object paths and register service self.path = '/ukBaz/bluezero/advertisement{0:04d}'.format(advert_id) self.bus = dbus.SystemBus() self.mainloop = async_tools.EventLoop() self.interface = constants.LE_ADVERTISEMENT_IFACE dbus.service.Object.__init__(self, self.bus, self.path) self.props = { constants.LE_ADVERTISEMENT_IFACE: { 'Type': ad_type, 'ServiceUUIDs': None, 'ManufacturerData': None, 'SolicitUUIDs': None, 'ServiceData': None, 'IncludeTxPower': False, 'Appearance': None, 'LocalName': None } }
def __init__(self): """Default initialiser. """ # Initialise the D-Bus path and register it self.bus = dbus.SystemBus() self.path = dbus.ObjectPath(constants.BLUEZERO_DBUS_OBJECT) dbus.service.Object.__init__(self, self.bus, self.path) # Objects to be associated with this service self.managed_objs = [] self.eventloop = async_tools.EventLoop()
def __init__(self, adapter_address, local_name=None, appearance=None): self.app = localGATT.Application() self.srv_mng = GATT.GattManager(adapter_address) self.services = [] self.characteristics = [] self.descriptors = [] self.primary_services = [] self.dongle = adapter.Adapter(adapter_address) self.local_name = local_name self.appearance = appearance self.advert = advertisement.Advertisement(1, 'peripheral') self.ad_manager = advertisement.AdvertisingManager(adapter_address) self.mainloop = async_tools.EventLoop()
def __init__(self, device_id=None): """Default initialiser. 1. Initialises the program loop using ``GObject``. 2. Registers the Application on the D-Bus. 3. Initialises the list of services offered by the application. """ # Initialise the D-Bus path and register it self.bus = dbus.SystemBus() self.path = '/ukBaz/bluezero' self.bus_name = dbus.service.BusName('ukBaz.bluezero', self.bus) dbus.service.Object.__init__(self, self.bus_name, self.path) # Objects to be associated with this service self.managed_objs = [] self.eventloop = async_tools.EventLoop()
def __init__(self, adapter_addr=None): """Default initialiser. Creates the interface to the local Bluetooth adapter device. If address is not given then first device is list is used. :param adapter_addr: Address of Bluetooth adapter to use. """ self.bus = dbus.SystemBus() if adapter_addr is None: adapters = list_adapters() if len(adapters) > 0: adapter_addr = adapters[0] self.path = dbus_tools.get_dbus_path(adapter=adapter_addr) self.adapter_object = self.bus.get_object( constants.BLUEZ_SERVICE_NAME, self.path) self.adapter_methods = dbus.Interface(self.adapter_object, constants.ADAPTER_INTERFACE) self.adapter_props = dbus.Interface(self.adapter_object, dbus.PROPERTIES_IFACE) self._nearby_timeout = 10 self._nearby_count = 0 self.mainloop = async_tools.EventLoop() self.on_disconnect = None self.on_connect = None self.on_device_found = None self.bus.add_signal_receiver(self._interfaces_added, dbus_interface=constants.DBUS_OM_IFACE, signal_name='InterfacesAdded') self.bus.add_signal_receiver(self._interfaces_removed, dbus_interface=constants.DBUS_OM_IFACE, signal_name='InterfacesRemoved') self.bus.add_signal_receiver(self._properties_changed, dbus_interface=dbus.PROPERTIES_IFACE, signal_name='PropertiesChanged', arg0=constants.DEVICE_INTERFACE, path_keyword='path')
def __init__(self): self.mainloop = async_tools.EventLoop() 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] advert.local_name = 'CPU Temp' advert.appearance = 1344 # 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 __init__(self, device_id=None): """Default initialiser. 1. Initialises the program loop using ``GObject``. 2. Registers the Application on the D-Bus. 3. Initialises the list of services offered by the application. """ # Initialise the loop that the application runs in self.mainloop = async_tools.EventLoop() # Initialise the D-Bus path and register it self.bus = dbus.SystemBus() self.path = '/ukBaz/bluezero/application{}'.format(id(self)) self.bus_name = dbus.service.BusName('ukBaz.bluezero', self.bus) dbus.service.Object.__init__(self, self.bus_name, self.path) # Initialise services within the application self.services = [] self.dongle = adapter.Adapter(device_id)
def setUp(self): """ Patch the DBus module :return: """ self.dbus_mock = MagicMock() self.mainloop_mock = MagicMock() self.gobject_mock = MagicMock() self.process_mock = MagicMock() modules = { 'dbus': self.dbus_mock, 'dbus.mainloop.glib': self.mainloop_mock, 'gi.repository': self.gobject_mock, 'subprocess': self.process_mock } self.module_patcher = patch.dict('sys.modules', modules) self.module_patcher.start() from bluezero import async_tools self.module_under_test = async_tools.EventLoop()
class Scanner: """ For scanning of Bluetooth Low Energy (BLE) beacons """ remove_list = set() mainloop = async_tools.EventLoop() on_eddystone_url = None on_eddystone_uid = None on_ibeacon = None on_altbeacon = None @classmethod def start_event_loop(cls): """ Start the event loop """ cls.mainloop.run() @classmethod def stop_scan(cls): """ Stop scanning for beacons """ cls.mainloop.quit() cls.dongle.stop_discovery() @classmethod def clean_beacons(cls): """ Remove beacons from BlueZ's `devices` list so every advert from a beacon is reported """ not_found = set() for rm_dev in cls.remove_list: logger.debug('Remove %s', rm_dev) try: cls.dongle.remove_device(rm_dev) except dbus.exceptions.DBusException as dbus_err: if dbus_err.get_dbus_name() == 'org.bluez.Error.DoesNotExist': not_found.add(rm_dev) else: raise dbus_err for lost in not_found: cls.remove_list.remove(lost) @classmethod def process_eddystone(cls, data, rssi): """ Extract Eddystone data from advertisement service data :param data: Bytes from Service Data in advertisement :param rssi: Received Signal Strength value """ _url_prefix_scheme = ['http://www.', 'https://www.', 'http://', 'https://', ] _url_encoding = ['.com/', '.org/', '.edu/', '.net/', '.info/', '.biz/', '.gov/', '.com', '.org', '.edu', '.net', '.info', '.biz', '.gov'] tx_pwr = int.from_bytes([data[1]], 'big', signed=True) if data[0] == 0x00: namespace_id = int.from_bytes(data[2:12], 'big') instance_id = int.from_bytes(data[12:18], 'big') logger.info('\t\tEddystone UID: %s - %s \u2197 %s', namespace_id, instance_id, tx_pwr) data = EddyUID(namespace_id, instance_id, tx_pwr, rssi) if cls.on_eddystone_uid: cls.on_eddystone_uid(data) elif data[0] == 0x10: prefix = data[2] encoded_url = data[3:] full_url = _url_prefix_scheme[prefix] for letter in encoded_url: if letter < len(_url_encoding): full_url += _url_encoding[letter] else: full_url += chr(letter) logger.info('\t\tEddystone URL: %s \u2197 %s \u2198 %s', full_url, tx_pwr, rssi) data = EddyURL(url=full_url, tx_pwr=tx_pwr, rssi=int(rssi)) if cls.on_eddystone_url: cls.on_eddystone_url(data) @classmethod def process_ibeacon(cls, data, rssi, beacon_type='iBeacon'): """ Extract iBeacon or AltBeacon data from Manufacturer Data in an advertisement :param data: Bytes from manufacturer data :param rssi: Received Signal Strength value :param beacon_type: iBeacon or AltBeacon """ beacon_uuid = uuid.UUID(bytes=bytes(data[2:18])) major = int.from_bytes(bytearray(data[18:20]), 'big', signed=False) minor = int.from_bytes(bytearray(data[20:22]), 'big', signed=False) tx_pwr = int.from_bytes([data[22]], 'big', signed=True) logger.info('%s: %s - %s - %s \u2197 %s \u2198 %s', beacon_type, beacon_uuid, major, minor, tx_pwr, rssi) if beacon_type == 'iBeacon' and cls.on_ibeacon: data = iBeacon(beacon_uuid, major, minor, tx_pwr, rssi) cls.on_ibeacon(data) elif beacon_type == 'AltBeacon' and cls.on_altbeacon: data = AltBeacon(beacon_uuid, major, minor, tx_pwr, rssi) cls.on_altbeacon(data) @staticmethod def ble_16bit_match(uuid_16, srv_data): """ Utility method to test 16 bit UUID against Bluetooth SIG 128 bit UUID used in service data :param uuid_16: 16 Bit UUID value :param srv_data: :return: """ uuid_128 = f'0000{uuid_16}-0000-1000-8000-00805f9b34fb' return uuid_128 == list(srv_data.keys())[0] @classmethod def on_device_found(cls, bz_device_obj): """ Callback to look at BLE advertisement to see if it is a recognised beacon and if it is, then call the relevant processing function :param bz_device_obj: Bluezero device object of discovered device """ rssi = bz_device_obj.RSSI service_data = bz_device_obj.service_data manufacturer_data = bz_device_obj.manufacturer_data if service_data and cls.ble_16bit_match('feaa', service_data): cls.process_eddystone(service_data[EDDYSTONE_SRV_UUID], rssi) cls.remove_list.add(str(bz_device_obj.remote_device_path)) elif manufacturer_data: for mfg_id in manufacturer_data: # iBeacon 0x004c if mfg_id == 0x004c and manufacturer_data[mfg_id][0] == 0x02: cls.process_ibeacon(manufacturer_data[mfg_id], rssi) cls.remove_list.add(str(bz_device_obj.remote_device_path)) # AltBeacon 0xacbe elif all((mfg_id == 0xffff, manufacturer_data[mfg_id][0:2] == [0xbe, 0xac])): cls.process_ibeacon(manufacturer_data[mfg_id], rssi, beacon_type='AltBeacon') cls.remove_list.add(str(bz_device_obj.remote_device_path)) # elif mfg_id == 0x0310: # print(f'\t\tBlue Maestro {manufacturer_data[mfg_id]}') # remove_list.add(device_path) cls.clean_beacons() @classmethod def start_beacon_scan(cls, on_eddystone_url=None, on_eddystone_uid=None, on_ibeacon=None, on_altbeacon=None): """ Start scan for beacons. Provided callback will be called if matching beacon type is found. All callbacks take one argument which is a named tuple with the fields relevant for that format. - Eddystone URL = ['url', 'tx_pwr', 'rssi'] - Eddystone UID = ['namespace', 'instance', 'tx_pwr', 'rssi'] - iBeacon = ['UUID', 'major', 'minor', 'tx_pwr', 'rssi'] - AltBeacon = ['UUID', 'major', 'minor', 'tx_pwr', 'rssi'] :param on_eddystone_url: Callback for Eddystone URL format :param on_eddystone_uid: Callback for Eddystone UID format :param on_ibeacon: Callback for iBeacon format :param on_altbeacon: Callback for AltBeacon format """ cls.dongle = adapter.Adapter() cls.on_eddystone_url = on_eddystone_url cls.on_eddystone_uid = on_eddystone_uid cls.on_ibeacon = on_ibeacon cls.on_altbeacon = on_altbeacon cls.dongle.on_device_found = cls.on_device_found cls.dongle.show_duplicates() cls.dongle.start_discovery() try: cls.start_event_loop() except KeyboardInterrupt: cls.stop_scan()
from bluezero import microbit from bluezero import async_tools ubit = microbit.Microbit(adapter_addr='02:00:AA:48:25:29', device_addr='F1:55:90:65:29:DC', accelerometer_service=False, button_service=False, led_service=False, magnetometer_service=False, pin_service=False, temperature_service=False, uart_service=True) eloop = async_tools.EventLoop() ubit.connect() def ping(): ubit.uart = 'ping#' return True def goodbye(): ubit.quit_async() ubit.disconnect() return False ubit.subscribe_uart(print) eloop.add_timer(10000, ping) eloop.add_timer(30000, goodbye)