Ejemplo n.º 1
0
    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
            }
        }
Ejemplo n.º 2
0
    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()
Ejemplo n.º 3
0
 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()
Ejemplo n.º 4
0
    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()
Ejemplo n.º 5
0
    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')
Ejemplo n.º 6
0
    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, {})
Ejemplo n.º 7
0
    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)
Ejemplo n.º 8
0
    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()
Ejemplo n.º 9
0
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()
Ejemplo n.º 10
0
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)