Checks / tests (ubuntu-latest, 3.11) (push) Canceled after 0s
Checks / tests (ubuntu-latest, 3.14) (push) Canceled after 0s
Checks / tests (windows-latest, 3.11) (push) Canceled after 0s
Checks / tests (windows-latest, 3.14) (push) Canceled after 0s
Checks / package (push) Canceled after 0s
66 lines
2.8 KiB
Python
66 lines
2.8 KiB
Python
"""Inspect MX-T40 BLE services without reading/writing control values."""
|
|
|
|
import argparse
|
|
import asyncio
|
|
|
|
# MX-T40 model advertisements from the Android APK's strings.xml. These
|
|
# identify discovery records; they are not assumed to be writable services.
|
|
MODEL_UUIDS = {
|
|
f"0000{short}-0000-1000-8000-00805f9b34fb" for short in ("ff40", "fe40", "ff41", "fe41")
|
|
}
|
|
|
|
|
|
def is_candidate(device, advertisement):
|
|
names = f"{device.name or ''} {advertisement.local_name or ''}".upper()
|
|
return "T40" in names or bool(
|
|
MODEL_UUIDS.intersection(value.lower() for value in advertisement.service_uuids)
|
|
)
|
|
|
|
|
|
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)
|