Filter likely BullTron BLE devices
This commit is contained in:
+45
-3
@@ -55,6 +55,47 @@ WRITE_CHAR = "0000fff2-0000-1000-8000-00805f9b34fb"
|
||||
AT_CHAR = "0000fff3-0000-1000-8000-00805f9b34fb"
|
||||
SECRET_KEY_CHAR = "02f00000-0000-0000-0000-00000000ff05"
|
||||
VERSION_CHAR = "02f00000-0000-0000-0000-00000000ff04"
|
||||
BULLTRON_NAME_MARKERS = ("dl", "b35")
|
||||
BULLTRON_ADV_MARKERS = (b"DL", b"PU", b"JHB")
|
||||
|
||||
|
||||
def iter_advertisement_payloads(adv: Any) -> list[bytes]:
|
||||
"""Best-effort extraction of payload-like byte blobs from bleak metadata."""
|
||||
payloads: list[bytes] = []
|
||||
for data in getattr(adv, "manufacturer_data", {}).values():
|
||||
if isinstance(data, bytes):
|
||||
payloads.append(data)
|
||||
for data in getattr(adv, "service_data", {}).values():
|
||||
if isinstance(data, bytes):
|
||||
payloads.append(data)
|
||||
for item in getattr(adv, "platform_data", ()) or ():
|
||||
if isinstance(item, bytes):
|
||||
payloads.append(item)
|
||||
elif isinstance(item, (tuple, list)):
|
||||
payloads.extend(part for part in item if isinstance(part, bytes))
|
||||
return payloads
|
||||
|
||||
|
||||
def bulltron_match_reason(name: str, service_uuids: list[str], payloads: list[bytes] | None = None) -> str | None:
|
||||
lowered = name.lower()
|
||||
if "bull" in lowered:
|
||||
return "name:BullTron"
|
||||
for marker in BULLTRON_NAME_MARKERS:
|
||||
if marker in lowered:
|
||||
return f"name:{marker.upper()}"
|
||||
if MAIN_SERVICE in service_uuids:
|
||||
return "service:fff0"
|
||||
for payload in payloads or []:
|
||||
# The Android app checks the legacy scanRecord at byte offsets 14..16
|
||||
# for ASCII DL/PU/JHB markers, then requires byte 16 to be 0x00.
|
||||
if len(payload) > 17 and payload[16] == 0x00:
|
||||
for marker in BULLTRON_ADV_MARKERS:
|
||||
if payload[14 : 14 + len(marker)].upper() == marker:
|
||||
return f"adv:{marker.decode('ascii')}"
|
||||
for marker in BULLTRON_ADV_MARKERS:
|
||||
if marker in payload.upper():
|
||||
return f"adv:{marker.decode('ascii')}"
|
||||
return None
|
||||
|
||||
|
||||
def crc16_modbus_swapped(data: bytes) -> int:
|
||||
@@ -296,8 +337,8 @@ class BleWorker:
|
||||
for _, (dev, adv) in devices.items():
|
||||
name = dev.name or adv.local_name or "(unnamed)"
|
||||
service_uuids = [u.lower() for u in (adv.service_uuids or [])]
|
||||
likely = "bull" in name.lower() or MAIN_SERVICE in service_uuids
|
||||
rows.append({"name": name, "address": dev.address, "rssi": adv.rssi, "likely": likely})
|
||||
reason = bulltron_match_reason(name, service_uuids, iter_advertisement_payloads(adv))
|
||||
rows.append({"name": name, "address": dev.address, "rssi": adv.rssi, "likely": bool(reason), "reason": reason or ""})
|
||||
rows.sort(key=lambda row: (not row["likely"], -(row["rssi"] or -999)))
|
||||
self.ui_queue.put(("scan", rows))
|
||||
self.ui_queue.put(("status", f"Found {len(rows)} BLE devices"))
|
||||
@@ -547,7 +588,8 @@ class BullTronGui(tk.Tk):
|
||||
self.device_list.delete(0, tk.END)
|
||||
for row in self.devices:
|
||||
mark = "*" if row["likely"] else " "
|
||||
self.device_list.insert(tk.END, f"{mark} {row['name']} {row['address']} RSSI {row['rssi']}")
|
||||
reason = f" [{row['reason']}]" if row["reason"] else ""
|
||||
self.device_list.insert(tk.END, f"{mark} {row['name']} {row['address']} RSSI {row['rssi']}{reason}")
|
||||
elif kind == "connected":
|
||||
self.status.set(f"Connected: {payload}")
|
||||
elif kind == "state":
|
||||
|
||||
Reference in New Issue
Block a user