Skip to content

Instantly share code, notes, and snippets.

@aminnj
Last active November 28, 2023 17:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aminnj/39fcb48f5ff1398fd7c6b51d00f620ca to your computer and use it in GitHub Desktop.
Save aminnj/39fcb48f5ff1398fd7c6b51d00f620ca to your computer and use it in GitHub Desktop.
Get RSSI of nearby bluetooth peripherals using CoreBluetooth in python
import rich
from Foundation import NSObject, NSRunLoop, NSDefaultRunLoopMode, NSDate, NSArray
from CoreBluetooth import CBCentralManager, CBCentralManagerStatePoweredOn, CBUUID
import time
state = {}
# Create a delegate to handle CoreBluetooth events
class MyCentralManagerDelegate(NSObject):
def __init__(self):
self.central_manager = None
def startScanning(self):
self.central_manager.scanForPeripheralsWithServices_options_([], None)
print("Bluetooth is ON. Scanning for peripherals...")
def centralManagerDidUpdateState_(self, central):
if central.state() == CBCentralManagerStatePoweredOn:
self.startScanning()
else:
print("Bluetooth is either off or unavailable")
def centralManager_didDiscoverPeripheral_advertisementData_RSSI_(self, central, peripheral, advertisementData, rssi):
timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
identifier = str(peripheral.identifier())
name = peripheral.name()
# https://domoticx.com/bluetooth-company-identifiers/
# https://stackoverflow.com/questions/42140636/extract-data-from-kcbadvdatamanufacturerdata-on-swift
manufacturer_data = advertisementData.get("kCBAdvDataManufacturerData", None)
# print(advertisementData)
manufacturer_id = None
node_id = None
node_state = None
voltage = None
packet_counter = None
ibeacon_data = None
if manufacturer_data:
# L is 0x4c - 76 - apple
# https://stackoverflow.com/questions/18906988/what-is-the-ibeacon-bluetooth-profile/19040616#19040616
# https://stackoverflow.com/questions/36092497/ibeacons-understanding-minor-major-and-uuid
if b"L\x00\x02\x15" in bytes(manufacturer_data):
uuid = manufacturer_data[4:20].hex()
major = (int.from_bytes(manufacturer_data[20:22], byteorder="big"))
minor = (int.from_bytes(manufacturer_data[22:23], byteorder="big"))
ibeacon_data = dict(uuid=uuid, major=major, minor=minor)
manufacturer_id = int.from_bytes(manufacturer_data[:2], byteorder="little")
node_id = int.from_bytes(manufacturer_data[2:4], byteorder="little")
node_state = int.from_bytes(manufacturer_data[4:5], byteorder="little")
voltage = int.from_bytes(manufacturer_data[5:6], byteorder="little")
packet_counter = int.from_bytes(manufacturer_data[6:9], byteorder="little")
# NOTE:
# Can't get bdaddr for devices this way.
# `identifier` is a uuid that the device running the script assigned to the peripheral
# If another device runs this script, it'll get a different `identifier` for that same peripheral
# But I have not confirmed if the the same device will always get the same `identifier` for the same peripheral
state[identifier] = dict(timestamp=time.time(),
name=name,
rssi=rssi,
manufacturer_id=manufacturer_id,
node_id=node_id,
node_state=node_state,
voltage=voltage,
packet_counter=packet_counter,
ibeacon_data=ibeacon_data,
)
# print(state)
central_manager_delegate = MyCentralManagerDelegate.new()
central_manager = CBCentralManager.alloc().initWithDelegate_queue_(central_manager_delegate, None)
central_manager_delegate.central_manager = central_manager
# Start the central manager
while True:
run_loop = NSRunLoop.currentRunLoop()
central_manager_delegate.startScanning()
# does 5 seconds of scanning
# probably at some point want to scan continuously and update `state`
run_loop.runUntilDate_(NSDate.dateWithTimeIntervalSinceNow_(5))
print(len(state))
rich.print(state)
time.sleep(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment