Beispiel #1
0
 def name(self, name):
     if isinstance(name, bytearray) or isinstance(name, bytes):
         try:
             self._name = str(name, encoding='utf-8')  # UTF-8
         except UnicodeDecodeError as e:
             log.error("Received non-UTF-8 name", payload)
             raise e
     else:
         self._name = str(name)  # unit8
Beispiel #2
0
    def centralManager_didDiscoverPeripheral_advertisementData_RSSI_(
            self, manager, peripheral, data, rssi):
        try:
            log.debug('Found: name={} rssi={} data={} '.format(
                peripheral.name(), rssi, data))

            if self.on_advertising_data:
                advertisement = Advertisement()
                advertisement.flags = 0  # Not available
                advertisement.name = peripheral.name()
                advertisement.rssi = rssi
                advertisement.raw_data = data

                if 'kCBAdvDataTxPowerLevel' in data:
                    advertisement.tx_pwr_lvl = int(
                        data['kCBAdvDataTxPowerLevel'])

                if data['kCBAdvDataIsConnectable']:
                    # TODO: handle: kCBAdvDataIsConnectable correctly
                    advertisement.type = 0x01  # BLE_GAP_ADV_TYPE_ADV_DIRECT_IND

                if 'kCBAdvDataServiceUUIDs' in data:
                    log.debug('kCBAdvDataServiceUUIDs:')
                    for cbuuid in data['kCBAdvDataServiceUUIDs']:
                        uuid_bytes = cbuuid.data().bytes().tobytes()

                        if 2 == len(uuid_bytes):
                            uuid = UUID16(uuid_bytes, little_endian=False)
                            advertisement.uuid16s.append(uuid)

                        elif 16 == len(uuid_bytes):
                            uuid = UUID128(uuid_bytes, little_endian=False)
                            advertisement.uuid128s.append(uuid)
                        else:
                            log.error(
                                "Unsupporten UUID length for UUID bytes={}".
                                format(uuid_bytes))

                        log.debug('Service UUID: {} {}'.format(
                            type(cbuuid), cbuuid))

                if 'kCBAdvDataManufacturerData' in data:
                    mfg_data = data['kCBAdvDataManufacturerData']
                    log.debug('kCBAdvDataManufacturerData={}'.format(mfg_data))
                    advertisement.mfg_data = mfg_data

                self.on_advertising_data(advertisement)

        except Exception as e:
            log.exception(e)
Beispiel #3
0
    def from_hcipayload(cls, data):
        data_info = "Data: {}".format(hex_string(data))
        pos_info = "POS : {}".format("".join("{:02} ".format(x)
                                             for x in range(0, len(data))))
        log.debug(data_info)
        log.debug(pos_info)

        num_reports = data[0]
        log.debug("Num Reports {}".format(num_reports))

        if num_reports != 1:
            log.error(
                "TODO: Only 1 Advertising report is supported, creating empty Advertisement"
            )
            # don't make it fatal
            return Advertisement()

        # TODO: move these 2 LUTs to a better place
        gap_adv_type = [
            "ADV_IND",
            "ADV_DIRECT_IND",
            "ADV_SCAN_IND",
            "ADV_NONCONN_IND",
            "SCAN_RSP",
        ][data[1]]
        gap_addr_type = [
            "PUBLIC", "RANDOM", "PUBLIC_IDENTITY", "RANDOM_STATIC"
        ][data[2]]
        gap_addr = data[3:9]
        rssi = rssi_from_byte(data[-1])

        advertisement = Advertisement(address=BDAddress(gap_addr),
                                      rssi=rssi,
                                      raw_data=data)
        advertisement.type = gap_adv_type
        advertisement.address_type = gap_addr_type

        pos = 10
        while pos < len(data) - 1:
            log.debug("POS={}".format(pos))
            length = data[pos]
            gap_type = data[pos + 1]
            payload = data[pos + 2:pos + 2 + length - 1]
            log.debug("Pos={} Type=0x{:02x} Len={} Payload={}".format(
                pos, gap_type, length, hex_string(payload)))

            if GAP_FLAGS == gap_type:
                advertisement.flags = payload[0]
                log.debug("Flags={:02x}".format(advertisement.flags))

            elif GAP_UUID_16BIT_COMPLETE == gap_type:
                uuids = []
                byte_pos = 0
                if len(payload) % 2 != 0:
                    raise ValueError(
                        "PAyload is not divisible by 2 for UUID16")

                while byte_pos < len(payload):
                    log.debug("byte_pos={}".format(byte_pos))
                    byte_pair = payload[byte_pos:byte_pos + 2]
                    log.debug("byte pair = {}".format(byte_pair))
                    uuid = UUID16(byte_pair)
                    uuids.append(uuid)
                    byte_pos += 2

                advertisement.uuid16s = uuids

            elif GAP_UUID_128BIT_INCOMPLETE == gap_type:
                # if length-1 > 16:
                #     log.warning("TODO: >1 UUID128's found, not yet split into individual elements")
                # advertisement.uuid128s=[UUID128(payload)]
                uuids = []
                byte_pos = 0
                if len(payload) % 16 != 0:
                    raise ValueError(
                        "Payload is not divisible by 16 for UUID128")

                while byte_pos < len(payload):
                    log.debug("byte_pos={}".format(byte_pos))
                    byte_list = payload[byte_pos:byte_pos + 16]
                    log.debug("byte_list = {}".format(byte_list))
                    uuid = UUID128(byte_list)
                    uuids.append(uuid)
                    byte_pos += 16

                advertisement.uuid128s_incomplete = uuids

                log.debug(advertisement.uuid128s_incomplete)

            elif GAP_UUID_128BIT_COMPLETE == gap_type:

                # if length-1 > 16:
                #     log.warning("TODO: >1 UUID128's found, not yet split into individual elements")
                # advertisement.uuid128s=[UUID128(payload)]
                uuids = []
                byte_pos = 0
                if len(payload) % 16 != 0:
                    raise ValueError(
                        "Payload is not divisible by 16 for UUID128")

                while byte_pos < len(payload):
                    log.debug("byte_pos={}".format(byte_pos))
                    byte_list = payload[byte_pos:byte_pos + 16]
                    log.debug("byte_list = {}".format(byte_list))
                    uuid = UUID128(byte_list)
                    uuids.append(uuid)
                    byte_pos += 16

                advertisement.uuid128s = uuids

                log.debug(advertisement.uuid128s)

            elif GAP_NAME_INCOMPLETE == gap_type:
                advertisement.name = payload
                advertisement.name_is_complete = False
                log.debug("Incomplete Name={}".format(advertisement.name))

            elif GAP_NAME_COMPLETE == gap_type:
                advertisement.name = payload
                advertisement.name_is_complete = True
                log.debug("Complete Name={}".format(advertisement.name))

            elif GAP_SERVICE_DATA == gap_type:
                advertisement.service_data = payload
                log.debug("Service Data={}".format(advertisement.service_data))

            elif GAP_MFG_DATA == gap_type:
                advertisement.mfg_data = payload
                log.debug("Manufacturer Data={}".format(
                    advertisement.mfg_data))

            elif GAP_TX_POWER == gap_type:
                if payload == None:
                    advertisement.tx_pwr_lvl = payload
                else:
                    advertisement.tx_pwr_lvl = int.from_bytes(
                        payload, byteorder="little")
                log.debug("TX Power={}".format(advertisement.tx_pwr_lvl))

            elif GAP_APPEARANCE == gap_type:
                # https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.gap.appearance.xml
                # "The external appearance of this device. The values are composed of a category (10-bits) and sub-categories (6-bits)."
                # TODO: Add Appearance Names as well as IDs likey as "appearance: {id: , description:}"
                advertisement.appearance = int.from_bytes(payload,
                                                          byteorder="little")
                log.debug("GAP Appearance={}".format(advertisement.appearance))

            else:
                log.warning(
                    "TODO: Unhandled GAP type, pos={} type=0x{:02x} len={}".
                    format(pos, gap_type, length))
                log.warning(data_info)
                log.warning(pos_info)

            pos += length + 1

        log.debug(advertisement)
        return advertisement
Beispiel #4
0
    def from_hcipayload(cls, data):
        data_info = "Data: {}".format(hex_string(data))
        pos_info = "POS : {}".format(''.join('{:02} '.format(x)
                                             for x in range(0, len(data))))
        log.debug(data_info)
        log.debug(pos_info)

        num_reports = data[0]
        log.debug("Num Reports {}".format(num_reports))

        if num_reports != 1:
            log.error(
                "TODO: Only 1 Advertising report is supported, creating emtpy Advertisement"
            )
            # don't make it fatal
            return Advertisement()

        # TODO: move these 2 LUTs to a better place
        gap_adv_type = [
            'ADV_IND', 'ADV_DIRECT_IND', 'ADV_SCAN_IND', 'ADV_NONCONN_IND',
            'SCAN_RSP'
        ][data[1]]
        gap_addr_type = [
            'PUBLIC', 'RANDOM', 'PUBLIC_IDENTITY', 'RANDOM_STATIC'
        ][data[2]]
        gap_addr = data[3:9]
        rssi = rssi_from_byte(data[-1])

        advertisement = Advertisement(address=BDAddress(gap_addr), rssi=rssi)
        advertisement.type = gap_adv_type
        advertisement.address_type = gap_addr_type

        pos = 10
        while pos < len(data) - 1:
            log.debug("POS={}".format(pos))
            length = data[pos]
            gap_type = data[pos + 1]
            payload = data[pos + 2:pos + 2 + length - 1]
            log.debug("Pos={} Type=0x{:02x} Len={} Payload={}".format(
                pos, gap_type, length, hex_string(payload)))

            if GAP_FLAGS == gap_type:
                advertisement.flags = payload[0]
                log.debug("Flags={:02x}".format(advertisement.flags))

            elif GAP_UUID_16BIT_COMPLETE == gap_type:
                uuids = []
                byte_pos = 0
                if len(payload) % 2 != 0:
                    raise ValueError(
                        "PAyload is not divisible by 2 for UUID16")

                while byte_pos < len(payload):
                    log.debug('byte_pos={}'.format(byte_pos))
                    byte_pair = payload[byte_pos:byte_pos + 2]
                    log.debug('byte pair = {}'.format(byte_pair))
                    uuid = UUID16(byte_pair)
                    uuids.append(uuid)
                    byte_pos += 2

                advertisement.uuid16s = uuids

            elif GAP_UUID_128BIT_COMPLETE == gap_type:

                # if length-1 > 16:
                #     log.warning("TODO: >1 UUID128's found, not yet split into individual elements")
                #advertisement.uuid128s=[UUID128(payload)]
                uuids = []
                byte_pos = 0
                if len(payload) % 16 != 0:
                    raise ValueError(
                        "Payload is not divisible by 16 for UUID128")

                while byte_pos < len(payload):
                    log.debug('byte_pos={}'.format(byte_pos))
                    byte_list = payload[byte_pos:byte_pos + 16]
                    log.debug('byte_list = {}'.format(byte_list))
                    uuid = UUID128(byte_list)
                    uuids.append(uuid)
                    byte_pos += 16

                advertisement.uuid128s = uuids

                log.debug(advertisement.uuid128s)

            elif GAP_NAME_INCOMPLETE == gap_type:
                advertisement.name = payload
                advertisement.name_is_complete = False
                log.debug("Incomplete Name={}".format(advertisement.name))

            elif GAP_NAME_COMPLETE == gap_type:
                advertisement.name = payload
                advertisement.name_is_complete = True
                log.debug("Complete Name={}".format(advertisement.name))

            elif GAP_SERVICE_DATA == gap_type:
                advertisement.service_data = payload
                log.debug("Service Data={}".format(advertisement.service_data))

            elif GAP_MFG_DATA == gap_type:
                advertisement.mfg_data = payload
                log.debug("Manufacturer Data={}".format(
                    advertisement.mfg_data))

            else:
                log.warning(
                    "TODO: Unhandled GAP type, pos={} type=0x{:02x} len={}".
                    format(pos, gap_type, length))
                log.warning(data_info)
                log.warning(pos_info)

            pos += length + 1

        log.debug(advertisement)
        return advertisement
Beispiel #5
0
    def peripheral_didWriteValueForCharacteristic_error_(
            self, peripheral, characteristic, error):
        log.debug("peripheral_didWriteValueForCharacteristic_error_")

        if error != None:
            log.error(repr(error))
Beispiel #6
0
 def centralManager_didFailToConnectPeripheral_error_(
         self, manager, peripheral, error):
     log.error(repr(error))
Beispiel #7
0
# Work around for Sphinx
try:
    import objc
    from Foundation import *
    from PyObjCTools import AppHelper
    import CoreBluetooth
except (ImportError, AttributeError) as e:
    import sys
    if 'sphinx' in sys.modules:
        log.warning(
            "macOS modules not found, if this is a documentation build all is good"
        )
    else:
        log.error(
            "macOS modules not found, but this doesn't look like a documentation build"
        )
        raise e

######################################
# Dispatch Queue support

# see: https://bitbucket.org/ronaldoussoren/pyobjc/issues/215/starting-runconsoleeventloop-from-a

# Opaque structure use to pass areound 'dispatch_queue_t' C type
# see: https://stackoverflow.com/questions/5030730/is-it-acceptable-to-subclass-c-void-p-in-ctypes
#class dispatch_queue_t(ctypes.Structure):
#    pass
# The above is not used as the PyObcC 4.0.1 doesn't accept it as a type to create the pointer to the dispatch_queu_

NULL_PTR = ctypes.POINTER(ctypes.c_int)()
Beispiel #8
0
from bleson.core.hci.constants import *
from bleson.logger import log
from bleson.core.hci.type_converters import bytearray_to_hexstring

# Work around for Sphinx
try:
    import blesonwin
except (ImportError, AttributeError) as e:
    import sys
    if 'sphinx' in sys.modules:
        log.warning(
            "blesonwin not found, if this is a documentation build all is good"
        )
    else:
        log.error(
            "blesonwin not found, but this doesn't look like a documentation build"
        )
        raise e

    blesonwin = {}


class BluetoothAdapter(Adapter):
    def __init__(self, device_id=0):
        self.device_id = device_id
        self.connected = False
        self._keep_running = True
        self.on_advertising_data = None
        blesonwin.initialise()

    def open(self):