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]))
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
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()
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)
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
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
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):
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