feat: 监控页按名称排序 + 按钮改「看屏 / 打开设备端 Agent」;设备名匹配补 USB 设备
用户反馈的三件事:
1) **监控页设备排序**改成按**名称**(A01/A02/…),没名字的排最后按地址。
之前是 adb 返回的顺序,看着像按 IP 排,同型号多台时对不上号。
排序放在 `/api/status`(后端),大屏页一并受益。
2) **操作按钮改名 + 换实现**:
- 「截图」→「**看屏**」(行为不变,就是查看设备实时画面)
- 「定位」→「**打开设备端 Agent**」:改成让设备上的 Agent 弹身份大字页
(名称/IP/平台地址),替代原来"推 HTML 让设备浏览器打开定位页"那套
(那套要设备装浏览器、页面还依赖设备能访问平台,backlog A1/A2 一直没修)
* 新接口 `POST /api/agent/show-info {serial, close?}`;没装 Agent 时明确报出来
* **先点亮屏幕 + 解除锁屏再拉起** —— 设备息屏时 am start 会把大字页开在锁屏后面,
人看到的还是一片黑(实测踩过)
* 平台地址取操作者当前访问地址;若是 localhost 则换成本机局域网地址
(设备上显示 127.0.0.1 毫无意义)
3) **安装应用的选择设备界面仍显示裸序列号**:USB 设备在 adb 里的 serial 就是
`ro.serialno`,等于池里那条网络记录的**指纹** —— 之前只按 serial 匹配,插着 USB
时会冒出一个没名字的 `s8o7nrt8pbzhfadq`。现在按 serial 或指纹任一命中都取名称,
三处(剪贴板注入 / 安装弹窗 / 安装进度表)统一。
顺带修掉几个自己踩出来的坑:
- `device_pool.list_configured()` 返回的是 **serial 字符串列表**,我当成字典用了
(`.get("serial")` 抛异常被 except 吞掉 → 名字全空)→ 改用 `list_devices()`
- `web/device_agent_api.py` 用了 `PERM_DEVICES` 但没导入 → 服务直接起不来(502)
- Agent 自检里的版本号是写死的常量(升到 1.4 还显示 1.0)→ 改成读包信息
- `doc/API.md` 章节编号(插了一节没把后面整体下移,出现两个 §16)
验证:安装弹窗里 USB 那台现在显示「A07」(指纹匹配);监控页按钮为
「看屏 / 打开设备端 Agent」;对装/未装 Agent 的设备分别得到大字页 / 明确报错;
大字页实测显示 A07 + 192.168.20.250:18050 + Agent v1.4。
This commit is contained in:
+80
-2
@@ -28,7 +28,7 @@ from config import APK_DIR
|
||||
from core import device_pool
|
||||
from core.db_config import meta_get, meta_set
|
||||
from core.logger import get_logger
|
||||
from web.auth import PERM_APKS, perm_required
|
||||
from web.auth import PERM_APKS, PERM_DEVICES, perm_required
|
||||
|
||||
bp = Blueprint("device_agent", __name__)
|
||||
_log = get_logger("web.devagent")
|
||||
@@ -86,7 +86,7 @@ def _identify():
|
||||
if d:
|
||||
return fp, d.get("serial", serial), d.get("name") or "", True
|
||||
if serial:
|
||||
for d in device_pool.list_configured(enabled_only=False):
|
||||
for d in device_pool.list_devices(): # 返回字典(list_configured 只有 serial)
|
||||
if d.get("serial") == serial:
|
||||
return fp, serial, d.get("name") or "", True
|
||||
except Exception as e:
|
||||
@@ -264,6 +264,84 @@ def _prune_logs(keep=_MAX_LOG):
|
||||
|
||||
|
||||
# ================== 管理端接口(登录 + 权限)==================
|
||||
@bp.route("/api/agent/show-info", methods=["POST"])
|
||||
@perm_required(PERM_DEVICES)
|
||||
def api_agent_show_info():
|
||||
"""让设备上的 Agent 打开「身份大字页」(监控页那个按钮调它)。
|
||||
|
||||
替代原来那套「推 HTML 让设备浏览器打开定位页」——那套在 MIUI 上要装浏览器、
|
||||
页面还依赖设备能访问平台;Agent 是常驻 App,一条 `am start` 就出画面。
|
||||
|
||||
请求:{"serial": "…", "close": false}
|
||||
返回:{ok, msg};设备没装 Agent 时明确报出来(不静默失败)
|
||||
"""
|
||||
from core.adb_helper import _adb
|
||||
|
||||
data = request.json or {}
|
||||
serial = (data.get("serial") or "").strip()
|
||||
close = bool(data.get("close"))
|
||||
if not serial:
|
||||
return jsonify({"ok": False, "error": "缺少 serial"}), 400
|
||||
|
||||
if close:
|
||||
args = ["--es", "close", "1"]
|
||||
else:
|
||||
# 设备信息:名称/IP/平台地址 —— Agent 用它们显示大字页
|
||||
name, ip = "", ""
|
||||
ip = serial.split(":")[0] if ":" in serial else ""
|
||||
for d in device_pool.list_devices(): # 字典列表(list_configured 只有 serial)
|
||||
if d.get("serial") == serial:
|
||||
name = d.get("name") or ""
|
||||
break
|
||||
if not name:
|
||||
# USB 设备的 serial 就是指纹:池里那条记录的 serial 是网络地址,
|
||||
# 靠指纹才认得出来(否则大字页标题会显示一串裸序列号)
|
||||
for d in device_pool.list_devices():
|
||||
if d.get("fingerprint") and d["fingerprint"] == serial:
|
||||
name = d.get("name") or ""
|
||||
break
|
||||
if not ip:
|
||||
# USB 设备:平台不知道它的 IP,现问一下设备(只读命令,不影响设备)
|
||||
out = _adb("-s", serial, "shell", "ip", "-4", "addr", "show", "wlan0")
|
||||
import re as _re
|
||||
m = _re.search(r"inet\s+(\d+\.\d+\.\d+\.\d+)", out or "")
|
||||
ip = m.group(1) if m else ""
|
||||
# 平台地址:用操作者当前访问的地址;如果是 localhost,设备根本连不上 →
|
||||
# 换成局域网地址(设备上显示 127.0.0.1 毫无意义)
|
||||
server = request.host_url.rstrip("/")
|
||||
if "127.0.0.1" in server or "localhost" in server:
|
||||
from web.devices_api import _lan_ip
|
||||
server = "http://%s:18050" % _lan_ip()
|
||||
args = ["--es", "name", name or serial.split(":")[0],
|
||||
"--es", "serial", serial,
|
||||
"--es", "server", server]
|
||||
if ip:
|
||||
args += ["--es", "ip", ip]
|
||||
|
||||
# 先点亮屏幕、解除锁屏:设备息屏时 am start 会把大字页开在锁屏**后面**,
|
||||
# 人看到的还是一片黑(实测踩过)——这正是原来"定位"按钮里的"点亮屏幕"那一步
|
||||
if not close:
|
||||
try:
|
||||
_adb("-s", serial, "shell", "input", "keyevent", "KEYCODE_WAKEUP")
|
||||
_adb("-s", serial, "shell", "wm", "dismiss-keyguard")
|
||||
except Exception as e:
|
||||
_log.warning("点亮屏幕失败(不影响显示身份页): %s", e)
|
||||
|
||||
try:
|
||||
out = _adb("-s", serial, "shell", "am", "start", "-n",
|
||||
"com.example.deviceagent/.InfoActivity", *args) or ""
|
||||
except Exception as e:
|
||||
return jsonify({"ok": False, "error": f"adb 执行失败: {e}"}), 500
|
||||
|
||||
low = out.lower()
|
||||
if "unable to resolve intent" in low or "does not exist" in low or "error" in low:
|
||||
return jsonify({"ok": False,
|
||||
"error": "这台设备没装设备 Agent(com.example.deviceagent)——"
|
||||
"可以从「应用管理 → 设备端应用商店」装,或用扫码配置"}), 400
|
||||
_log.info("设备端 Agent %s: %s", "关闭身份页" if close else "打开身份页", serial)
|
||||
return jsonify({"ok": True, "msg": "已关闭" if close else "已让设备显示身份信息"})
|
||||
|
||||
|
||||
@bp.route("/api/agent-store/config", methods=["GET", "POST"])
|
||||
@perm_required(PERM_APKS)
|
||||
def api_agent_store_config():
|
||||
|
||||
Reference in New Issue
Block a user