杰瑞科技汇

CoreBluetooth Python如何实现?

Of course! Let's dive into using Core Bluetooth with Python.

CoreBluetooth Python如何实现?-图1
(图片来源网络,侵删)

The Short Answer: It's Not Native

First and foremost, a crucial point: there is no native, built-in Python library for CoreBluetooth on macOS.

CoreBluetooth is a native framework provided by Apple and is only accessible through Objective-C or Swift. This means you cannot run a Python script on your Mac and have it directly interact with Bluetooth Low Energy (BLE) devices using CoreBluetooth.

The Solution: The PyObjC Bridge

The standard and most powerful way to use native macOS frameworks (like CoreBluetooth, AppKit, AVFoundation, etc.) in Python is through PyObjC.

PyObjC is a Python bridge to the Objective-C runtime. It allows you to:

CoreBluetooth Python如何实现?-图2
(图片来源网络,侵删)
  1. Import and use Objective-C frameworks directly in your Python code.
  2. Create Python classes that inherit from Objective-C classes (like CBCentralManager or CBPeripheral).
  3. Write callback methods in Python that will be called by the native Objective-C code.

Step-by-Step Guide to a CoreBluetooth Python Script

Here is a complete, practical example of how to create a Python script that scans for BLE devices. This script will:

  1. Set up a CBCentralManager.
  2. Start scanning for all discoverable services.
  3. Print the names and identifiers of any peripherals it finds.
  4. Stop scanning after 10 seconds.

Prerequisites

  1. macOS: You need a Mac. CoreBluetooth is a macOS framework.
  2. Python 3: A modern Python 3 installation.
  3. PyObjC: You need to install the PyObjC package. It's best to install it using pip.
pip install pyobjc-core pyobjc-framework-CoreBluetooth
  • pyobjc-core: The core bridge.
  • pyobjc-framework-CoreBluetooth: The bindings for the CoreBluetooth framework specifically.

The Python Code (ble_scanner.py)

This code is heavily commented to explain each part, especially the PyObjC-specific syntax.

import time
from PyObjC import frameworks
from PyObjC import objc
from PyObjC import managers
from Foundation import NSObject, NSLog, NSArray, NSDictionary, NSString, NSData
from CoreBluetooth import CBCentralManager, CBPeripheral, CBUUID
# --- Helper for Logging ---
# We'll use NSLog for logging, which goes to the system log.
# You can also just use print(), but NSLog is more idiomatic for macOS apps.
def log(message):
    """A simple logging function using NSLog."""
    NSLog(f"[BLE Scanner] {message}")
# --- The CoreBluetooth Delegate ---
# In CoreBluetooth, you provide a delegate object to handle events.
# We create a Python class that inherits from NSObject and implements
# the required delegate methods.
class BLEScannerDelegate(NSObject):
    """
    This class acts as the delegate for CBCentralManager.
    It will be notified of BLE events like discovering peripherals,
    connection status changes, etc.
    """
    @objc.python_method
    def init(self):
        """Initialize the delegate."""
        # We must call the NSObject's initializer
        self = objc.super(BLEScannerDelegate, self).init()
        if self is None:
            return None
        # We'll store discovered peripherals here
        self.discovered_peripherals = {}
        log("BLE Scanner Delegate initialized.")
        return self
    # --- CBCentralManagerDelegate Methods ---
    @objc.python_method
    def centralManagerDidUpdateState_(self, central):
        """
        This is a required delegate method.
        It's called when the central manager's state changes.
        We need to check if Bluetooth is powered on.
        """
        state = central.state()
        state_names = {
            CBCentralManager.StateUnknown: "Unknown",
            CBCentralManager.StateResetting: "Resetting",
            CBCentralManager.StateUnsupported: "Unsupported",
            CBCentralManager.StateUnauthorized: "Unauthorized",
            CBCentralManager.StatePoweredOff: "Powered Off",
            CBCentralManager.StatePoweredOn: "Powered On"
        }
        log(f"Central Manager State: {state_names.get(state, 'Unknown State')}")
        if state == CBCentralManager.StatePoweredOn:
            log("Bluetooth is powered on. Starting scan...")
            # Scan for all services (nil) with no options
            central.scanForPeripheralsWithServices_options_(None, None)
        else:
            log("Cannot start scan. Bluetooth is not powered on.")
            # In a real app, you might want to exit or show an error
            # For this example, we'll just stop.
            central.stopScan()
    @objc.python_method
    def centralManager_didDiscoverPeripheral_advertisementData_RSSI_(self, central, peripheral, advertisement_data, rssi):
        """
        This method is called when a new peripheral is discovered.
        """
        # Get the peripheral's name if available
        name = peripheral.name() or "Unknown"
        uuid_str = peripheral.identifier().UUIDString()
        log(f"Discovered Peripheral: {name} (UUID: {uuid_str}), RSSI: {rssi}")
        # Store the peripheral so we can connect to it later if needed
        if uuid_str not in self.discovered_peripherals:
            self.discovered_peripherals[uuid_str] = peripheral
            log(f"  -> New peripheral added. Total discovered: {len(self.discovered_peripherals)}")
# --- Main Application Logic ---
def main():
    """
    Main function to set up and run the BLE scanner.
    """
    log("Starting BLE Scanner Application...")
    # 1. Create an instance of our delegate
    # We must allocate it and initialize it.
    delegate = BLEScannerDelegate.alloc().init()
    # 2. Create the CBCentralManager
    # The first argument is the delegate, the second is the queue (None for main queue).
    central_manager = CBCentralManager.alloc().initWithDelegate_queue_(delegate, None)
    # 3. Run the loop
    # The scan is initiated asynchronously by the delegate method.
    # We'll just wait for a bit to let it run.
    log("Scanning for 10 seconds...")
    time.sleep(10)
    # 4. Stop the scan
    log("Stopping scan.")
    central_manager.stopScan()
    # 5. Print a summary
    log(f"Scan finished. Discovered {len(delegate.discovered_peripherals)} peripherals.")
    for uuid, peripheral in delegate.discovered_peripherals.items():
        log(f"  - {peripheral.name() or 'Unnamed'} ({uuid})")
    log("Application finished.")
if __name__ == "__main__":
    # This is a simple way to run the PyObjC event loop.
    # It ensures that the delegate methods get called properly.
    from PyObjC import Foundation
    Foundation.NSAutoreleasePool.alloc().init()
    main()
    Foundation.NSRunLoop.currentRunLoop().runUntilDate_(Foundation.NSDate.dateWithTimeIntervalSinceNow_(0.1))

How to Run the Script

  1. Save the code above as ble_scanner.py.

  2. Open your Terminal.

    CoreBluetooth Python如何实现?-图3
    (图片来源网络,侵删)
  3. Navigate to the directory where you saved the file.

  4. Run the script:

    python3 ble_scanner.py

You will see output in your Terminal. For better debugging, you can also view the system log:

log stream --predicate 'senderImagePath contains "python3"'

You should see log messages from the script, including the state of Bluetooth and any devices it discovers.


Key Concepts and Syntax Explained

  1. @objc.python_method Decorator

    • This decorator is essential. It tells PyObjC that the following method is a Python method. Without it, PyObjC won't know how to call it from the Objective-C runtime.
    • Methods that are explicitly called by Objective-C (like delegate callbacks) must have this decorator.
  2. NSObject.alloc().init()

    • This is the standard Objective-C way to create and initialize an object. alloc() reserves memory, and init() sets up the object's state. PyObjC provides this syntax.
  3. CBCentralManager.alloc().initWithDelegate_queue_(delegate, None)

    • We create the central manager instance.
    • initWithDelegate_queue_: This is the Objective-C initializer method. PyObjC automatically converts the camelCase initWithDelegate:queue: to snake_case initWithDelegate_queue_.
    • We pass our Python delegate object as the first argument.
    • We pass None for the queue, which tells CoreBluetooth to use the main run loop (standard practice).
  4. Method Signatures

    • The delegate methods have long, specific names like centralManager_didDiscoverPeripheral_advertisementData_RSSI_.
    • This directly maps to the Objective-C method: -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI
    • PyObjC requires you to use this exact signature for the callback to be recognized.
  5. Type Bridging

    • PyObjC automatically converts common types between Python and Objective-C:
      • Python str
分享:
扫描分享到社交APP
上一篇
下一篇