mirror of
https://github.com/smittix/intercept.git
synced 2026-06-11 15:33:32 -07:00
Fix BT Locate startup/map rendering and CelesTrak import reliability
This commit is contained in:
+71
-41
@@ -18,13 +18,35 @@ from utils.bluetooth.models import BTDeviceAggregate
|
||||
from utils.bluetooth.scanner import BluetoothScanner, get_bluetooth_scanner
|
||||
from utils.gps import get_current_position
|
||||
|
||||
logger = logging.getLogger('intercept.bt_locate')
|
||||
logger = logging.getLogger('intercept.bt_locate')
|
||||
|
||||
# Maximum trail points to retain
|
||||
MAX_TRAIL_POINTS = 500
|
||||
|
||||
# EMA smoothing factor for RSSI
|
||||
EMA_ALPHA = 0.3
|
||||
EMA_ALPHA = 0.3
|
||||
|
||||
|
||||
def _normalize_mac(address: str | None) -> str | None:
|
||||
"""Normalize MAC string to colon-separated uppercase form when possible."""
|
||||
if not address:
|
||||
return None
|
||||
|
||||
text = str(address).strip().upper().replace('-', ':')
|
||||
if not text:
|
||||
return None
|
||||
|
||||
# Handle raw 12-hex form: AABBCCDDEEFF
|
||||
raw = ''.join(ch for ch in text if ch in '0123456789ABCDEF')
|
||||
if ':' not in text and len(raw) == 12:
|
||||
text = ':'.join(raw[i:i + 2] for i in range(0, 12, 2))
|
||||
|
||||
parts = text.split(':')
|
||||
if len(parts) == 6 and all(len(p) == 2 and all(c in '0123456789ABCDEF' for c in p) for p in parts):
|
||||
return ':'.join(parts)
|
||||
|
||||
# Return cleaned original when not a strict MAC (caller may still use exact matching)
|
||||
return text
|
||||
|
||||
|
||||
class Environment(Enum):
|
||||
@@ -112,11 +134,11 @@ class LocateTarget:
|
||||
if target_addr_part and dev_addr == target_addr_part:
|
||||
return True
|
||||
|
||||
# Match by MAC/address (case-insensitive, normalize separators)
|
||||
# Match by MAC/address (case-insensitive, normalize separators)
|
||||
if self.mac_address:
|
||||
dev_addr = (device.address or '').upper().replace('-', ':')
|
||||
target_addr = self.mac_address.upper().replace('-', ':')
|
||||
if dev_addr == target_addr:
|
||||
dev_addr = _normalize_mac(device.address)
|
||||
target_addr = _normalize_mac(self.mac_address)
|
||||
if dev_addr and target_addr and dev_addr == target_addr:
|
||||
return True
|
||||
|
||||
# Match by payload fingerprint (guard against low-stability generic fingerprints)
|
||||
@@ -268,27 +290,33 @@ class LocateSession:
|
||||
# Track last RSSI per device to detect changes
|
||||
self._last_cb_rssi: dict[str, int] = {} # Dedup for rapid callbacks only
|
||||
|
||||
def start(self) -> bool:
|
||||
"""Start the locate session.
|
||||
|
||||
Subscribes to scanner callbacks AND runs a polling thread that
|
||||
checks the aggregator directly (handles bleak scan timeout).
|
||||
"""
|
||||
self._scanner = get_bluetooth_scanner()
|
||||
self._scanner.add_device_callback(self._on_device)
|
||||
|
||||
# Ensure BLE scanning is active
|
||||
if not self._scanner.is_scanning:
|
||||
logger.info("BT scanner not running, starting scan for locate session")
|
||||
self._scanner_started_by_us = True
|
||||
if not self._scanner.start_scan(mode='auto'):
|
||||
logger.warning("Failed to start BT scanner for locate session")
|
||||
else:
|
||||
self._scanner_started_by_us = False
|
||||
|
||||
self.active = True
|
||||
self.started_at = datetime.now()
|
||||
self._stop_event.clear()
|
||||
def start(self) -> bool:
|
||||
"""Start the locate session.
|
||||
|
||||
Subscribes to scanner callbacks AND runs a polling thread that
|
||||
checks the aggregator directly (handles bleak scan timeout).
|
||||
"""
|
||||
self._scanner = get_bluetooth_scanner()
|
||||
self._scanner.add_device_callback(self._on_device)
|
||||
self._scanner_started_by_us = False
|
||||
|
||||
# Ensure BLE scanning is active
|
||||
if not self._scanner.is_scanning:
|
||||
logger.info("BT scanner not running, starting scan for locate session")
|
||||
self._scanner_started_by_us = True
|
||||
if not self._scanner.start_scan(mode='auto'):
|
||||
# Surface startup failure to caller and avoid leaving stale callbacks.
|
||||
status = self._scanner.get_status()
|
||||
reason = status.error or "unknown error"
|
||||
logger.warning(f"Failed to start BT scanner for locate session: {reason}")
|
||||
self._scanner.remove_device_callback(self._on_device)
|
||||
self._scanner = None
|
||||
self._scanner_started_by_us = False
|
||||
return False
|
||||
|
||||
self.active = True
|
||||
self.started_at = datetime.now()
|
||||
self._stop_event.clear()
|
||||
|
||||
# Start polling thread as reliable fallback
|
||||
self._poll_thread = threading.Thread(
|
||||
@@ -550,25 +578,27 @@ _session: LocateSession | None = None
|
||||
_session_lock = threading.Lock()
|
||||
|
||||
|
||||
def start_locate_session(
|
||||
target: LocateTarget,
|
||||
environment: Environment = Environment.OUTDOOR,
|
||||
custom_exponent: float | None = None,
|
||||
fallback_lat: float | None = None,
|
||||
def start_locate_session(
|
||||
target: LocateTarget,
|
||||
environment: Environment = Environment.OUTDOOR,
|
||||
custom_exponent: float | None = None,
|
||||
fallback_lat: float | None = None,
|
||||
fallback_lon: float | None = None,
|
||||
) -> LocateSession:
|
||||
"""Start a new locate session, stopping any existing one."""
|
||||
global _session
|
||||
|
||||
with _session_lock:
|
||||
if _session and _session.active:
|
||||
_session.stop()
|
||||
|
||||
_session = LocateSession(
|
||||
target, environment, custom_exponent, fallback_lat, fallback_lon
|
||||
)
|
||||
_session.start()
|
||||
return _session
|
||||
with _session_lock:
|
||||
if _session and _session.active:
|
||||
_session.stop()
|
||||
|
||||
_session = LocateSession(
|
||||
target, environment, custom_exponent, fallback_lat, fallback_lon
|
||||
)
|
||||
if not _session.start():
|
||||
_session = None
|
||||
raise RuntimeError("Bluetooth scanner failed to start")
|
||||
return _session
|
||||
|
||||
|
||||
def stop_locate_session() -> None:
|
||||
|
||||
Reference in New Issue
Block a user