def _on_data(self, data):
        log.debug(
            "----------------------------------------------------------------------"
        )
        log.debug("Socket data: len={}, data={}".format(
            len(data), hex_string(data)))

        if data[0] == HCI_EVENT_PKT:
            hci_event_packet = parse_hci_event_packet(data[1:-1])
            log.debug(hci_event_packet)

            if data[1] == EVT_CMD_COMPLETE:
                self._handle_command_complete(data)

            elif data[1] == EVT_DISCONN_COMPLETE:
                self._handle_disconnection_complete(data)

            elif data[1] == EVT_LE_META_EVENT:
                self._handle_meta_event(hci_event_packet)

            else:
                log.warning("TODO: unhandled HCI Event packet, type={}".format(
                    hci_event_packet))
        else:
            log.warning("TODO: Unhandled HCI packet, type={}".format(data[0]))
Exemple #2
0
 def name(self, name):
     if isinstance(name, bytearray) or isinstance(name, bytes):
         try:
             self._name = str(name, encoding='utf-8')  # UTF-8 (BLE core spec)
         except UnicodeDecodeError as e:
             log.warning("Name is not a valid UTF-8 string")
             self._name = name   # make the invalid name available still
     else:
         self._name = str(name)  # unit8
Exemple #3
0
    def start_scanning(self):
        log.info("start scanning")
        if self.on_advertising_data:
            blesonwin.on_advertisement(self._on_advertising_data)
        else:
            log.warning("on_advertising_data is not set")

        log.info(self.on_advertising_data)
        blesonwin.start_observer()
Exemple #4
0
 def start_advertising(self, advertisement, scan_response=None):
     log.warning("TODO")
     return
     adv_data = {
         'CBAdvertisementDataLocalNameKey': 'bleson',
         #'CBAdvertisementDataServiceUUIDsKey': CBUUID.UUIDWithString_(u'6E400001-B5A3-F393-E0A9-E50E24DCCA9E')
         'CBAdvertisementDataServiceUUIDsKey':
         CBUUID.UUIDWithString_(u'0xFFEF')
     }
     self._peripheral_manager.startAdvertising_(adv_data)
Exemple #5
0
 def stop_advertising(self):
     log.warning("TODO")
     return
     adv_data = {
         'CBAdvertisementDataLocalNameKey': 'bleson',
         #'CBAdvertisementDataServiceUUIDsKey': CBUUID.UUIDWithString_(u'6E400001-B5A3-F393-E0A9-E50E24DCCA9E')
         'CBAdvertisementDataServiceUUIDsKey':
         CBUUID.UUIDWithString_(u'0xFFEF')
     }
     if self._peripheral_manager.isAdvertising:
         self._peripheral_manager.stopAdvertising()
    def start_advertising(self, advertisement, scan_response=None):
        self.set_advertising_filter()
        self.set_advertise_enable(False)
        self.set_advertising_parameter()

        self.set_advertising_data(
            AdvertisingDataConverters.from_advertisement(advertisement))

        if scan_response:
            log.warning("TODO: support setting scan response")
        #    self.set_scan_response_data(scan_response_data.data)

        self.set_advertise_enable(True)
    def _handle_meta_event(self, hci_packet):

        log.debug("EVT_LE_META_EVENT")

        if hci_packet.subevent_code == EVT_LE_ADVERTISING_REPORT:
            log.debug('LE Advertising Report')
            if self.on_advertising_data:

                advertisement = AdvertisingDataConverters.from_hcipacket(
                    hci_packet)
                self.on_advertising_data(advertisement)
        else:
            log.warning(
                "TODO: unhandled HCI Meta Event packet, type={}".format(
                    hci_packet))
    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
Exemple #9
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
Exemple #10
0
from bleson.core.types import Advertisement, UUID16, UUID128
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 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):
Exemple #11
0
import threading
from bleson.interfaces.adapter import Adapter
from bleson.core.types import Advertisement, UUID16, UUID128, BDAddress
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