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
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)
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
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
def peripheral_didWriteValueForCharacteristic_error_( self, peripheral, characteristic, error): log.debug("peripheral_didWriteValueForCharacteristic_error_") if error != None: log.error(repr(error))
def centralManager_didFailToConnectPeripheral_error_( self, manager, peripheral, error): log.error(repr(error))
# 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)()
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):