Files
soundtower-controller/controller/ble_probe.py
T

55 lines
2.3 KiB
Python

"""Inspect MX-T40 BLE services without reading/writing control values."""
import argparse
import asyncio
from controller.discovery import is_candidate
async def inspect(timeout):
from bleak import BleakClient, BleakScanner
print(f"Scanning BLE advertisements for {timeout:g} seconds…", flush=True)
devices = await BleakScanner.discover(timeout=timeout, return_adv=True)
candidates = [(device, adv) for device, adv in devices.values() if is_candidate(device, adv)]
print(f"Nearby BLE devices: {len(devices)}; MX-T40 candidates: {len(candidates)}")
if not candidates:
print("No matching advertisement. This does not rule out BLE support.")
print("Named nearby devices:")
for device, adv in devices.values():
name = adv.local_name or device.name
if name:
print(f" {name} [{device.address}]")
return 1
failures = False
for device, adv in candidates:
print(f"\nDevice: {adv.local_name or device.name} [{device.address}]")
print("Advertised services:", ", ".join(adv.service_uuids) or "(none)")
try:
# Use the scanned device object so Bleak retains Windows address
# type information; the classic audio MAC may not be the BLE MAC.
async with BleakClient(device, timeout=15) as client:
for service in client.services:
print(f"Service: {service.uuid} {service.description}")
for char in service.characteristics:
print(f" Characteristic: {char.uuid} [{', '.join(char.properties)}]")
except Exception as exc:
failures = True
print(f"GATT inspection failed: {type(exc).__name__}: {exc}")
print("\nNo control values were read or written; no notifications enabled.")
return 1 if failures else 0
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--timeout", type=float, default=15)
args = parser.parse_args()
try:
raise SystemExit(asyncio.run(inspect(args.timeout)))
except ImportError:
print("Run: uv.exe run --python 3.14 --with bleak -m controller.ble_probe")
raise SystemExit(2)
except Exception as exc:
print(f"BLE scan failed: {type(exc).__name__}: {exc}")
raise SystemExit(1)