fix: skip Bluetooth scanning on Termux/Android
Bleak requires D-Bus which isn't available on Android. Detect Termux environment and skip both Classic BT and BLE scanning gracefully. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,12 +4,19 @@ import subprocess
|
||||
import re
|
||||
import json
|
||||
import asyncio
|
||||
import os
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass, asdict
|
||||
|
||||
from bleak import BleakScanner
|
||||
|
||||
|
||||
def is_termux() -> bool:
|
||||
"""Detect if running in Termux (Android)."""
|
||||
return os.environ.get('TERMUX_VERSION') is not None or \
|
||||
os.path.exists('/data/data/com.termux')
|
||||
|
||||
from .oui import OUILookup
|
||||
from .bluetooth_class import BluetoothClassDecoder
|
||||
from .distance import estimate_distance, rssi_to_quality, rssi_bar
|
||||
@@ -184,91 +191,97 @@ class RFScanner:
|
||||
"""
|
||||
devices = []
|
||||
|
||||
# Classic Bluetooth scan
|
||||
try:
|
||||
print(f"Scanning Classic Bluetooth ({timeout} seconds)...")
|
||||
result = subprocess.run(
|
||||
['sudo', 'hcitool', 'inq', '--flush'],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout + 10
|
||||
)
|
||||
|
||||
for line in result.stdout.split('\n'):
|
||||
match = re.match(
|
||||
r'\s*([0-9A-Fa-f:]+)\s+clock offset:\s*\S+\s+class:\s*(\S+)',
|
||||
line
|
||||
)
|
||||
if match:
|
||||
addr = match.group(1)
|
||||
device_class = match.group(2)
|
||||
name = self._get_bt_name(addr)
|
||||
rssi = self._get_bt_rssi(addr)
|
||||
dev_type, dev_subtype = self.bt_decoder.decode(device_class)
|
||||
|
||||
devices.append(BluetoothDevice(
|
||||
address=addr,
|
||||
name=name,
|
||||
rssi=rssi,
|
||||
device_class=device_class,
|
||||
device_type=f"{dev_type}" + (f" ({dev_subtype})" if dev_subtype else ""),
|
||||
manufacturer=self.oui_lookup.lookup(addr)
|
||||
))
|
||||
except Exception as e:
|
||||
print(f"Classic BT scan error: {e}")
|
||||
|
||||
# BLE scan using bleak
|
||||
try:
|
||||
print(f"Scanning BLE devices ({timeout} seconds)...")
|
||||
|
||||
async def _ble_scan():
|
||||
return await BleakScanner.discover(
|
||||
timeout=timeout,
|
||||
return_adv=True
|
||||
# Classic Bluetooth scan (not supported on Termux/Android)
|
||||
if is_termux():
|
||||
print("Skipping Classic BT scan (not supported on Termux)")
|
||||
else:
|
||||
try:
|
||||
print(f"Scanning Classic Bluetooth ({timeout} seconds)...")
|
||||
result = subprocess.run(
|
||||
['sudo', 'hcitool', 'inq', '--flush'],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout + 10
|
||||
)
|
||||
|
||||
ble_results = asyncio.run(_ble_scan())
|
||||
for line in result.stdout.split('\n'):
|
||||
match = re.match(
|
||||
r'\s*([0-9A-Fa-f:]+)\s+clock offset:\s*\S+\s+class:\s*(\S+)',
|
||||
line
|
||||
)
|
||||
if match:
|
||||
addr = match.group(1)
|
||||
device_class = match.group(2)
|
||||
name = self._get_bt_name(addr)
|
||||
rssi = self._get_bt_rssi(addr)
|
||||
dev_type, dev_subtype = self.bt_decoder.decode(device_class)
|
||||
|
||||
seen_addrs = {d.address for d in devices}
|
||||
for device, adv_data in ble_results.values():
|
||||
addr = device.address
|
||||
if addr not in seen_addrs and addr != 'LE':
|
||||
seen_addrs.add(addr)
|
||||
devices.append(BluetoothDevice(
|
||||
address=addr,
|
||||
name=name,
|
||||
rssi=rssi,
|
||||
device_class=device_class,
|
||||
device_type=f"{dev_type}" + (f" ({dev_subtype})" if dev_subtype else ""),
|
||||
manufacturer=self.oui_lookup.lookup(addr)
|
||||
))
|
||||
except Exception as e:
|
||||
print(f"Classic BT scan error: {e}")
|
||||
|
||||
name = device.name or adv_data.local_name or '<unknown>'
|
||||
rssi = adv_data.rssi # Real RSSI from advertisement
|
||||
manufacturer = self.oui_lookup.lookup(addr)
|
||||
# BLE scan using bleak (not supported on Termux/Android)
|
||||
if is_termux():
|
||||
print("Skipping BLE scan (not supported on Termux)")
|
||||
else:
|
||||
try:
|
||||
print(f"Scanning BLE devices ({timeout} seconds)...")
|
||||
|
||||
# Extract manufacturer data if available
|
||||
if adv_data.manufacturer_data and not manufacturer:
|
||||
# First 2 bytes are company ID
|
||||
for company_id in adv_data.manufacturer_data.keys():
|
||||
manufacturer = f"Company ID: {company_id}"
|
||||
break
|
||||
async def _ble_scan():
|
||||
return await BleakScanner.discover(
|
||||
timeout=timeout,
|
||||
return_adv=True
|
||||
)
|
||||
|
||||
# Infer device type
|
||||
inferred_type = infer_device_type_from_name(name)
|
||||
if not inferred_type:
|
||||
inferred_type = infer_device_type_from_manufacturer(manufacturer)
|
||||
ble_results = asyncio.run(_ble_scan())
|
||||
|
||||
if not inferred_type:
|
||||
if is_random_mac(addr):
|
||||
device_type = "BLE Device (Random MAC)"
|
||||
seen_addrs = {d.address for d in devices}
|
||||
for device, adv_data in ble_results.values():
|
||||
addr = device.address
|
||||
if addr not in seen_addrs and addr != 'LE':
|
||||
seen_addrs.add(addr)
|
||||
|
||||
name = device.name or adv_data.local_name or '<unknown>'
|
||||
rssi = adv_data.rssi # Real RSSI from advertisement
|
||||
manufacturer = self.oui_lookup.lookup(addr)
|
||||
|
||||
# Extract manufacturer data if available
|
||||
if adv_data.manufacturer_data and not manufacturer:
|
||||
# First 2 bytes are company ID
|
||||
for company_id in adv_data.manufacturer_data.keys():
|
||||
manufacturer = f"Company ID: {company_id}"
|
||||
break
|
||||
|
||||
# Infer device type
|
||||
inferred_type = infer_device_type_from_name(name)
|
||||
if not inferred_type:
|
||||
inferred_type = infer_device_type_from_manufacturer(manufacturer)
|
||||
|
||||
if not inferred_type:
|
||||
if is_random_mac(addr):
|
||||
device_type = "BLE Device (Random MAC)"
|
||||
else:
|
||||
device_type = "Low Energy Device"
|
||||
else:
|
||||
device_type = "Low Energy Device"
|
||||
else:
|
||||
device_type = inferred_type
|
||||
device_type = inferred_type
|
||||
|
||||
devices.append(BluetoothDevice(
|
||||
address=addr,
|
||||
name=name,
|
||||
rssi=rssi, # Real RSSI instead of -70
|
||||
device_class="BLE",
|
||||
device_type=device_type,
|
||||
manufacturer=manufacturer
|
||||
))
|
||||
except Exception as e:
|
||||
print(f"BLE scan error: {e}")
|
||||
devices.append(BluetoothDevice(
|
||||
address=addr,
|
||||
name=name,
|
||||
rssi=rssi, # Real RSSI instead of -70
|
||||
device_class="BLE",
|
||||
device_type=device_type,
|
||||
manufacturer=manufacturer
|
||||
))
|
||||
except Exception as e:
|
||||
print(f"BLE scan error: {e}")
|
||||
|
||||
# Auto-identify unknown devices
|
||||
if auto_identify and devices:
|
||||
|
||||
Reference in New Issue
Block a user