Python if语句无法识别蓝牙信标

时间:2019-10-30 10:14:03

标签: python

这是BeaconScanner.py文件用来查找和列出ble信标的ScanUtility.py文件。

#This is a working prototype. DO NOT USE IT IN LIVE PROJECTS


import sys
import struct
import bluetooth._bluetooth as bluez

OGF_LE_CTL=0x08
OCF_LE_SET_SCAN_ENABLE=0x000C

def hci_enable_le_scan(sock):
    hci_toggle_le_scan(sock, 0x01)

def hci_disable_le_scan(sock):
    hci_toggle_le_scan(sock, 0x00)

def hci_toggle_le_scan(sock, enable):
    cmd_pkt = struct.pack("<BB", enable, 0x00)
    bluez.hci_send_cmd(sock, OGF_LE_CTL, OCF_LE_SET_SCAN_ENABLE, cmd_pkt)

def packetToString(packet):
    """
    Returns the string representation of a raw HCI packet.
    """
    if sys.version_info > (3, 0):
    return ''.join('%02x' % struct.unpack("B", bytes([x]))[0] for x in packet)
    else:
    return ''.join('%02x' % struct.unpack("B", x)[0] for x in packet)

def parse_events(sock, loop_count=100):
    old_filter = sock.getsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, 14)
    flt = bluez.hci_filter_new()
    bluez.hci_filter_all_events(flt)
    bluez.hci_filter_set_ptype(flt, bluez.HCI_EVENT_PKT)
    sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, flt )
    results = []
    for i in range(0, loop_count):
    packet = sock.recv(255)
    ptype, event, plen = struct.unpack("BBB", packet[:3])
    packetOffset = 0
    dataString = packetToString(packet)
    """
    If the bluetooth device is an beacon then show the beacon.
    """
    #print (dataString)
    if dataString[34:50] == '0303aafe1516aafe' or '0303AAFE1116AAFE':
        """
        Selects parts of the bluetooth packets.
        """
        broadcastType = dataString[50:52]
        if broadcastType == '00' :
        type = "Eddystone UID"
        namespace = dataString[54:74].upper()
        instance = dataString[74:86].upper()
        resultsArray = [
        {"type": type, "namespace": namespace, "instance": instance}]
        return resultsArray

        elif broadcastType == '10':
        type = "Eddystone URL"
        urlprefix = dataString[54:56]
        if urlprefix == '00':
            prefix = 'http://www.'
        elif urlprefix == '01':
            prefix = 'https://www.'
        elif urlprefix == '02':
            prefix = 'http://'
        elif urlprefix == '03':
            prefix = 'https://'
        hexUrl = dataString[56:][:-2]
        url = prefix + hexUrl.decode("hex")
        rssi, = struct.unpack("b", packet[packetOffset -1])
        resultsArray = [{"type": type, "url": url}]
        return resultsArray

        elif broadcastType == '20':
        type = "Eddystone TLM"
        resultsArray = [{"type": type}]
        return resultsArray

        elif broadcastType == '30':
        type = "Eddystone EID"
        resultsArray = [{"type": type}]
        return resultsArray

        elif broadcastType == '40':
        type = "Eddystone RESERVED"
        resultsArray = [{"type": type}]
        return resultsArray

    if dataString[38:46] == '4c000215':
        """
        Selects parts of the bluetooth packets.
        """
        type = "iBeacon"
        uuid = dataString[46:54] + "-" + dataString[54:58] + "-" + dataString[58:62] + "-" + dataString[62:66] + "-" + dataString[66:78]
        major = dataString[78:82]
        minor = dataString[82:86]
        majorVal = int("".join(major.split()[::-1]), 16)
        minorVal = int("".join(minor.split()[::-1]), 16)
        """
        Organises Mac Address to display properly
        """
        scrambledAddress = dataString[14:26]
        fixStructure = iter("".join(reversed([scrambledAddress[i:i+2] for i in range(0, len(scrambledAddress), 2)])))
        macAddress = ':'.join(a+b for a,b in zip(fixStructure, fixStructure))
        rssi, = struct.unpack("b", packet[packetOffset -1])

        resultsArray = [{"type": type, "uuid": uuid, "major": majorVal, "minor": minorVal, "rssi": rssi, "macAddress": macAddress}]

        return resultsArray

    return results

原始的Beaconscanner.py文件可以通过列出信标来正常工作。

import ScanUtility
import bluetooth._bluetooth as bluez

#Set bluetooth device. Default 0.
dev_id = 0
try:
    sock = bluez.hci_open_dev(dev_id)
    print ("\n *** Looking for BLE Beacons ***\n")
    print ("\n *** CTRL-C to Cancel ***\n")
except:
    print ("Error accessing bluetooth")

ScanUtility.hci_enable_le_scan(sock)
#Scans for iBeacons
try:
    while True:
        returnedList = ScanUtility.parse_events(sock, 10)
        for item in returnedList:
            print(item)
            print("")
except KeyboardInterrupt:
    pass

这是经过修改的BeaconScanner.py文件,如果扫描仪通过其mac地址找到了所需的信标,则应打印“ Works”。

import ScanUtility
import bluetooth._bluetooth as bluez

#Set bluetooth device. Default 0.
dev_id = 0
try:
    sock = bluez.hci_open_dev(dev_id)
    print ("\n *** Looking for BLE Beacons ***\n")
    print ("\n *** CTRL-C to Cancel ***\n")
except:
    print ("Error accessing bluetooth")

ScanUtility.hci_enable_le_scan(sock)
#Scans for iBeacons
try:
    while True:
        returnedList = ScanUtility.parse_events(sock, 10)
        for macAddress in returnedList:
            if macAddress == "e2:e3:23:d1:b0:54":
            print("Works")
            else:
            print("Nope")
except KeyboardInterrupt:
    pass

但是,修改后的文件始终打印“ Nope”。我认为if语句中的“ macAddress”部分不能用于标识信标。在代码中必须进行哪些更改,以便可以在if语句中通过其mac地址识别信标?

2 个答案:

答案 0 :(得分:0)

根据ScanUtility.py的来源,该函数似乎返回一个字典的列表,该字典有点奇怪。您应该按以下方式查询字典:

for item in returnedList:
    try:
        if item['macAddress'] == "e2:e3:23:d1:b0:54":
            print("Works")
        else:
            print("Nope")
    except KeyError:
        print('MAC Address is missing')

请注意,我添加了一条try / except语句来处理您的词典中没有macAddress键的情况。仅当您的dict不是defaultdict的子类时,此方法才有效。

答案 1 :(得分:0)

通过其mac地址搜索信标并不总是一个好的解决方案,因为信标可能正在使用random private addresses

您确定要查找的mac地址曾经被广播过吗?

您的代码似乎也只返回iBeacons的mac地址。

我注意到此代码的另一件事是,它通过直接调用hci套接字来绕过系统上运行的bluetoothd。通常这不是一个好主意,并且要求python脚本与root特权一起运行。

避免这种情况的一种方法是使用BlueZ D-Bus API。下面提供了一个示例,并在看到感兴趣的mac地址时打印出信标数据以及一条消息。此代码需要pydbusgi.repository

import argparse
from gi.repository import GLib
from pydbus import SystemBus
import uuid

DEVICE_INTERFACE = 'org.bluez.Device1'

remove_list = set()


def stop_scan():
    """Stop device discovery and quit event loop"""
    adapter.StopDiscovery()
    mainloop.quit()


def clean_beacons():
    """
    BlueZ D-Bus API does not show duplicates. This is a
    workaround that removes devices that have been found
    during discovery
    """
    not_found = set()
    for rm_dev in remove_list:
        try:
            adapter.RemoveDevice(rm_dev)
        except GLib.Error as err:
            not_found.add(rm_dev)
    for lost in not_found:
        remove_list.remove(lost)


def process_eddystone(data):
    """Print Eddystone data in human readable format"""
    _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)
    # Eddystone UID Beacon format
    if data[0] == 0x00:
        namespace_id = int.from_bytes(data[2:12], 'big')
        instance_id = int.from_bytes(data[12:18], 'big')
        print(f'\t\tEddystone UID: {namespace_id} - {instance_id} \u2197 {tx_pwr}')
    # Eddystone URL beacon format
    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)
        print(f'\t\tEddystone URL: {full_url} \u2197 {tx_pwr}')


def process_ibeacon(data, beacon_type='iBeacon'):
    """Print iBeacon data in human readable format"""
    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)
    print(f'\t\t{beacon_type}: {beacon_uuid} - {major} - {minor} \u2197 {tx_pwr}')


def ble_16bit_match(uuid_16, srv_data):
    """Expand 16 bit UUID to full 128 bit UUID"""
    uuid_128 = f'0000{uuid_16}-0000-1000-8000-00805f9b34fb'
    return uuid_128 == list(srv_data.keys())[0]


def on_iface_added(owner, path, iface, signal, interfaces_and_properties):
    """
    Event handler for D-Bus interface added.
    Test to see if it is a new Bluetooth device
    """
    iface_path, iface_props = interfaces_and_properties
    if DEVICE_INTERFACE in iface_props:
        on_device_found(iface_path, iface_props[DEVICE_INTERFACE])


def on_device_found(device_path, device_props):
    """
    Handle new Bluetooth device being discover.
    If it is a beacon of type iBeacon, Eddystone, AltBeacon
    then process it
    """
    address = device_props.get('Address')
    address_type = device_props.get('AddressType')
    name = device_props.get('Name')
    alias = device_props.get('Alias')
    paired = device_props.get('Paired')
    trusted = device_props.get('Trusted')
    rssi = device_props.get('RSSI')
    service_data = device_props.get('ServiceData')
    manufacturer_data = device_props.get('ManufacturerData')
    if address.casefold() == 'e2:e3:23:d1:b0:54':
        print('Found mac address of interest')
    if service_data and ble_16bit_match('feaa', service_data):
        process_eddystone(service_data['0000feaa-0000-1000-8000-00805f9b34fb'])
        remove_list.add(device_path)
    elif manufacturer_data:
        for mfg_id in manufacturer_data:
            # iBeacon 0x004c
            if mfg_id == 0x004c and manufacturer_data[mfg_id][0] == 0x02:
                process_ibeacon(manufacturer_data[mfg_id])
                remove_list.add(device_path)
            # AltBeacon 0xacbe
            elif mfg_id == 0xffff and manufacturer_data[mfg_id][0:2] == [0xbe, 0xac]:
                process_ibeacon(manufacturer_data[mfg_id], beacon_type='AltBeacon')
                remove_list.add(device_path)
    clean_beacons()


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-d', '--duration', type=int, default=0,
                        help='Duration of scan [0 for continuous]')
    args = parser.parse_args()
    bus = SystemBus()
    adapter = bus.get('org.bluez', '/org/bluez/hci0')

    bus.subscribe(iface='org.freedesktop.DBus.ObjectManager',
                  signal='InterfacesAdded',
                  signal_fired=on_iface_added)

    mainloop = GLib.MainLoop()


    if args.duration > 0:
        GLib.timeout_add_seconds(args.duration, stop_scan)
    adapter.SetDiscoveryFilter({'DuplicateData': GLib.Variant.new_boolean(True)})
    adapter.StartDiscovery()

    try:
        print('\n\tUse CTRL-C to stop discovery\n')
        mainloop.run()
    except KeyboardInterrupt:
        stop_scan()

示例调用和输出:

$ python3 beacon_scanner.py 

        Use CTRL-C to stop discovery

                iBeacon: 1e9fdc8c-96e0-4d68-b34a-3b635cec0489 - 5555 - 99 ↗ -65
                Eddystone URL: http://www.bluetooth.com/ ↗ -69
Found mac address of interest