diff --git a/sdk/app/services/connection_priority.py b/sdk/app/services/connection_priority.py index 4b5625000d..f87eb4d0c0 100644 --- a/sdk/app/services/connection_priority.py +++ b/sdk/app/services/connection_priority.py @@ -190,8 +190,11 @@ class ConnectionPriorityManager: @staticmethod def _check_hook(device_id: str, hook_service) -> bool: try: - modules = hook_service.get_device_modules(device_id) if hasattr(hook_service, "get_device_modules") else [] - return bool(modules) + if not hasattr(hook_service, "_read_json"): + return False + state = hook_service._read_json(hook_service.device_state_file, {}) + item = state.get(device_id, {}) + return bool(item.get("supports_hook", False)) except Exception: return False diff --git a/sdk/app/static/hub.html b/sdk/app/static/hub.html index 21a2f2f1f3..dd1a6d240d 100644 --- a/sdk/app/static/hub.html +++ b/sdk/app/static/hub.html @@ -501,25 +501,37 @@ function renderOverview(){

设备快照

-

参考奥创后台和存客宝工作手机页,把设备状态、当前 App、网络和心跳统一收口。

+

实时展示设备状态、连接模式和心跳。点击设备行可查看详情。

+
+
+
- + - ${devices.length ? devices.slice(0, 8).map(d => ` - - - + ${devices.length ? devices.slice(0, 8).map(d => { + const best = d.best_mode; + const modes = (d.connection_modes || []).filter(m => m.available); + const statusClass = d.status === 'online' ? 'online' : (d.status === 'adb' ? 'adb' : ''); + return ` + + + + - - - - `).join('') : ``} + + `; + }).join('') : ``}
设备状态项目当前 APP网络心跳
设备状态连接模式项目当前 APP心跳
${esc(d.model || d.device_id || '-')}${esc(d.status || '-')}
${esc(d.model || d.device_id || '-')}${esc(d.status || '-')} +
+ ${best ? `${esc(best.name)}` : ''} + ${modes.filter(m => !best || m.id !== best.id).slice(0,2).map(m => `${esc(m.id)}`).join('')} +
+
${esc(d.project_id || '-')} ${esc(d.current_app || '-')}${esc(d.network_type || d.quick_status?.network_type || '-')}${typeof d.heartbeat_age_seconds === 'number' ? d.heartbeat_age_seconds + 's' : '-'}
暂无设备数据
${typeof d.heartbeat_age_seconds === 'number' ? d.heartbeat_age_seconds + 's' : (d.last_heartbeat ? '活跃' : '-')}
暂无设备数据
@@ -636,16 +648,24 @@ function renderDevicePanel(){

设备列表

- ${devices.length ? devices.map(device => ` + ${devices.length ? devices.map(device => { + const best = device.best_mode; + const avail = (device.connection_modes || []).filter(m => m.available); + return `
📱
${esc(device.model || device.device_id)}
-
${esc(device.device_id)} · ${esc(device.status || '-')} · ${esc(device.project_id || '未绑定项目')}
+
${esc(device.device_id?.substring(0,12))}... · ${esc(device.status || '-')} · ${esc(device.project_id || '未绑定')}
+
+ ${best ? `${esc(best.name)}` : ''} + ${avail.filter(m => !best || m.id !== best.id).slice(0,3).map(m => `${esc(m.id)}`).join('')} + ${avail.length === 0 ? '离线' : ''} +
-
- `).join('') : `
暂无设备,请先接入 Agent 或连接 ADB 设备
`} +
`; + }).join('') : `
暂无设备,请先接入 Agent 或连接 ADB 设备
`}
@@ -717,16 +737,30 @@ function renderDevicePanel(){

设备详情

${current ? ` - + - - + + + -
设备 ID${esc(current.device_id)}
设备 ID${esc(current.device_id)}
型号${esc(current.model || '-')}
品牌${esc(current.brand || '-')}
Android${esc(current.android_version || '-')}
状态${esc(current.status || '-')}
Android${esc(current.android_version || current.sdk_version || '-')}
状态${esc(current.status || '-')}
当前模式${current.best_mode ? `${esc(current.best_mode.name)}` : '-'}
当前 APP${esc(current.current_app || current.quick_status?.current_app || '-')}
网络${esc(current.network_type || current.quick_status?.network_type || '-')}
能力${esc((current.capabilities || []).join(', ') || '-')}
已装 APP${esc((current.installed_apps || current.apps || []).join(', ') || '-')}
+ ${(current.connection_modes || []).length ? ` +

连接模式 (${(current.connection_modes||[]).filter(m=>m.available).length}/5 可用)

+
+ ${(current.connection_modes || []).map(m => { + const icons = {hook:'🪝', agent:'🔌', adb:'📱', ai:'🤖', scrcpy:'🖥️'}; + const cls = m.available ? (current.best_mode?.id === m.id ? 'best-active' : 'active') : ''; + return `
+
${icons[m.id] || '⚡'}
+
${esc(m.name)}
+
${m.available ? '✓ 可用' : '✗ 不可用'}
+
P${m.priority} · ${esc(m.detail?.substring(0,20) || '')}
+
`; + }).join('')} +
` : ''} ` : `
未选中设备
`} @@ -1191,6 +1225,30 @@ async function refreshDevices(){ if (state.currentDevice) await loadDeviceDetail(state.currentDevice); } +async function refreshAll(){ + await Promise.all([refreshOverview(), refreshConnection(), refreshDocs()]); + log('全部数据已刷新', 'info'); +} + +async function refreshConnectionModes(){ + try { + const res = await apiGet('/api/v3/connection/modes'); + const modeData = res.data || []; + for (const item of modeData) { + state.devices = (state.devices || []).map(d => { + if (d.device_id === item.device_id) { + return {...d, connection_modes: item.modes, best_mode: item.best_mode}; + } + return d; + }); + } + log(`连接模式检测完成:${modeData.length} 台设备`, 'info'); + renderCurrentView(); + } catch(error) { + log(`连接模式检测失败:${error.message}`, 'error'); + } +} + function selectDevice(deviceId){ state.currentDevice = deviceId; renderCurrentView(); @@ -1201,9 +1259,14 @@ function selectDevice(deviceId){ async function loadDeviceDetail(deviceId){ try{ - const res = await apiGet(`/api/v3/devices/${deviceId}`); - const detail = res.data || {}; - state.devices = (state.devices || []).map(item => item.device_id === deviceId ? {...item, ...detail} : item); + const [detailRes, modesRes] = await Promise.all([ + apiGet(`/api/v3/devices/${deviceId}`), + apiGet(`/api/v3/connection/modes/${deviceId}`).catch(() => null) + ]); + const detail = detailRes.data || {}; + const modes = modesRes?.data || {}; + const merged = {...detail, connection_modes: modes.modes || [], best_mode: modes.best_mode || null}; + state.devices = (state.devices || []).map(item => item.device_id === deviceId ? {...item, ...merged} : item); if (state.current === 'devices') renderCurrentView(); }catch(error){ log(`设备详情加载失败:${error.message}`, 'error');