113 lines
4.2 KiB
Python
113 lines
4.2 KiB
Python
"""
|
||
设备列表融合:WebSocket + MongoDB + ADB(供 /devices 与多机注册聚合复用)
|
||
"""
|
||
|
||
from typing import Any, Dict, List
|
||
import asyncio
|
||
import re
|
||
|
||
from services.ws_hub import ws_hub
|
||
from services.device_manager import device_manager
|
||
from services.adb_device import adb_manager
|
||
from services.device_id_util import enrich_device_id_fields, sanitize_sensitive_fields
|
||
|
||
|
||
async def list_merged_local_devices() -> List[Dict[str, Any]]:
|
||
"""与 routers.devices.get_devices 逻辑一致,返回设备 dict 列表。"""
|
||
db_devices: List[dict] = []
|
||
try:
|
||
db_devices = await device_manager.get_all_devices()
|
||
except Exception:
|
||
pass
|
||
|
||
online_devices = {d["device_id"]: d for d in ws_hub.get_online_devices()}
|
||
online_serials = {
|
||
value
|
||
for d in online_devices.values()
|
||
for value in (d.get("device_id"), d.get("serial"), d.get("adb_serial"), d.get("wifi_serial"))
|
||
if value
|
||
}
|
||
online_fingerprints = {
|
||
d.get("fingerprint_hash")
|
||
for d in online_devices.values()
|
||
if d.get("fingerprint_hash")
|
||
}
|
||
online_models = {
|
||
d.get("model")
|
||
for d in online_devices.values()
|
||
if d.get("model")
|
||
}
|
||
try:
|
||
adb_serials = await asyncio.wait_for(adb_manager.async_scan_devices(), timeout=0.5)
|
||
except asyncio.TimeoutError:
|
||
adb_serials = list(adb_manager._scan_cache or [])
|
||
|
||
seen_ids: set = set()
|
||
devices: List[Dict[str, Any]] = []
|
||
|
||
for raw in db_devices:
|
||
device = dict(raw)
|
||
device_id = device["device_id"]
|
||
serial = device.get("serial") or device.get("adb_serial") or device.get("wifi_serial")
|
||
fingerprint = device.get("fingerprint_hash")
|
||
is_online_alias = (
|
||
device_id not in online_devices
|
||
and (
|
||
(serial and serial in online_serials)
|
||
or (fingerprint and fingerprint in online_fingerprints)
|
||
)
|
||
)
|
||
adb_serial = adb_manager.resolve_serial(device_id)
|
||
is_adb_alias = (
|
||
device_id not in online_devices
|
||
and adb_serial in adb_serials
|
||
and device_id != adb_serial
|
||
)
|
||
is_stale_synthetic_alias = (
|
||
device_id not in online_devices
|
||
and not serial
|
||
and isinstance(device_id, str)
|
||
and re.fullmatch(r"[0-9a-f]{32}", device_id)
|
||
and device.get("model") in online_models
|
||
)
|
||
if is_online_alias or is_adb_alias or is_stale_synthetic_alias:
|
||
continue
|
||
seen_ids.add(device_id)
|
||
if device_id in online_devices:
|
||
device = {**device, **online_devices[device_id]}
|
||
device["status"] = "online"
|
||
else:
|
||
device["status"] = "offline"
|
||
devices.append(enrich_device_id_fields(device))
|
||
|
||
for device_id, info in online_devices.items():
|
||
if device_id not in seen_ids:
|
||
seen_ids.add(device_id)
|
||
devices.append(enrich_device_id_fields(dict(info)))
|
||
|
||
for serial in adb_serials:
|
||
if serial not in seen_ids:
|
||
adb_dev = adb_manager.get_device(serial)
|
||
if adb_dev:
|
||
info = adb_dev.get_info()
|
||
real_serial = info.get("serial") or serial
|
||
fingerprint = device_manager.compute_fingerprint({"device_id": real_serial, **info})
|
||
is_wifi_adb_alias = isinstance(serial, str) and ":" in serial and info.get("model") in online_models
|
||
if real_serial in online_serials or fingerprint in online_fingerprints or is_wifi_adb_alias:
|
||
continue
|
||
devices.append(
|
||
enrich_device_id_fields(
|
||
{
|
||
"device_id": serial,
|
||
"model": info.get("model", "Unknown"),
|
||
"brand": info.get("brand", "Unknown"),
|
||
"android_version": info.get("android_version", "Unknown"),
|
||
"status": "adb",
|
||
"connection_type": "adb",
|
||
"display": info.get("display", {}),
|
||
}
|
||
)
|
||
)
|
||
|
||
return sanitize_sensitive_fields(devices)
|