Fix BT/WiFi run-state health and BT Locate tracking continuity

This commit is contained in:
Smittix
2026-02-19 21:39:09 +00:00
parent 5c47e9f10a
commit 37ba12daaa
6 changed files with 256 additions and 144 deletions
+72 -47
View File
@@ -81,25 +81,31 @@ def resolve_rpa(irk: bytes, address: str) -> bool:
return computed_hash == expected_hash
@dataclass
class LocateTarget:
"""Target device specification for locate session."""
mac_address: str | None = None
name_pattern: str | None = None
irk_hex: str | None = None
device_id: str | None = None
# Hand-off metadata from Bluetooth mode
known_name: str | None = None
known_manufacturer: str | None = None
last_known_rssi: int | None = None
def matches(self, device: BTDeviceAggregate) -> bool:
"""Check if a device matches this target."""
# Match by device_id (exact)
if self.device_id and device.device_id == self.device_id:
return True
# Match by device_id address portion (without :address_type suffix)
@dataclass
class LocateTarget:
"""Target device specification for locate session."""
mac_address: str | None = None
name_pattern: str | None = None
irk_hex: str | None = None
device_id: str | None = None
device_key: str | None = None
fingerprint_id: str | None = None
# Hand-off metadata from Bluetooth mode
known_name: str | None = None
known_manufacturer: str | None = None
last_known_rssi: int | None = None
def matches(self, device: BTDeviceAggregate) -> bool:
"""Check if a device matches this target."""
# Match by stable device key (survives MAC randomization for many devices)
if self.device_key and getattr(device, 'device_key', None) == self.device_key:
return True
# Match by device_id (exact)
if self.device_id and device.device_id == self.device_id:
return True
# Match by device_id address portion (without :address_type suffix)
if self.device_id and ':' in self.device_id:
target_addr_part = self.device_id.rsplit(':', 1)[0].upper()
dev_addr = (device.address or '').upper()
@@ -107,38 +113,57 @@ class LocateTarget:
return True
# 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:
return True
# Match by RPA resolution
if self.irk_hex:
try:
irk = bytes.fromhex(self.irk_hex)
if len(irk) == 16 and device.address and resolve_rpa(irk, device.address):
if self.mac_address:
dev_addr = (device.address or '').upper().replace('-', ':')
target_addr = self.mac_address.upper().replace('-', ':')
if dev_addr == target_addr:
return True
# Match by payload fingerprint (guard against low-stability generic fingerprints)
if self.fingerprint_id:
dev_fp = getattr(device, 'payload_fingerprint_id', None)
dev_fp_stability = getattr(device, 'payload_fingerprint_stability', 0.0) or 0.0
if dev_fp and dev_fp == self.fingerprint_id and dev_fp_stability >= 0.35:
return True
# Match by RPA resolution
if self.irk_hex:
try:
irk = bytes.fromhex(self.irk_hex)
if len(irk) == 16 and device.address and resolve_rpa(irk, device.address):
return True
except (ValueError, TypeError):
pass
# Match by name pattern
if self.name_pattern and device.name and self.name_pattern.lower() in device.name.lower():
return True
# Match by known_name from handoff (exact name match)
return bool(self.known_name and device.name and self.known_name.lower() == device.name.lower())
def to_dict(self) -> dict:
return {
'mac_address': self.mac_address,
'name_pattern': self.name_pattern,
'irk_hex': self.irk_hex,
'device_id': self.device_id,
'known_name': self.known_name,
'known_manufacturer': self.known_manufacturer,
'last_known_rssi': self.last_known_rssi,
}
# Match by name pattern
if self.name_pattern and device.name and self.name_pattern.lower() in device.name.lower():
return True
# Match by known_name from handoff (exact or loose normalized match)
if self.known_name and device.name:
target_name = self.known_name.strip().lower()
device_name = device.name.strip().lower()
if target_name and (
target_name == device_name
or target_name in device_name
or device_name in target_name
):
return True
return False
def to_dict(self) -> dict:
return {
'mac_address': self.mac_address,
'name_pattern': self.name_pattern,
'irk_hex': self.irk_hex,
'device_id': self.device_id,
'device_key': self.device_key,
'fingerprint_id': self.fingerprint_id,
'known_name': self.known_name,
'known_manufacturer': self.known_manufacturer,
'last_known_rssi': self.last_known_rssi,
}
class DistanceEstimator: