feat: 监控大屏(/wall 全屏深色控制室风格:设备卡片网格+缩略图轮询+统计+时钟,缩略图 360px/q55 每 2.5s 一帧 20 台仅 ~0.2 核 CPU)+ /api/screen/thumb 接口 + 导航栏大屏入口

This commit is contained in:
2026-08-19 14:13:15 +08:00
parent 5543bad0e6
commit c78f17d72f
5 changed files with 277 additions and 1 deletions
+43
View File
@@ -187,6 +187,19 @@ def index():
return resp
@app.route("/wall")
@login_required
def wall():
"""监控大屏(独立全屏页面,供挂墙/电视展示)。
深色控制室风格;设备缩略图按需轮询(每设备 ~2.5s 一帧),
20 台设备整体开销约 0.2 核 CPU + 100KB/s,普通电脑无压力。
"""
resp = make_response(render_template("admin/wall.html"))
resp.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0"
return resp
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
@@ -1020,6 +1033,36 @@ def _screen_get_device(serial):
return u2.connect(serial)
@app.route("/api/screen/thumb")
@perm_required(PERM_DEVICES)
def api_screen_thumb():
"""大屏缩略图:单张 JPEG(360px 宽,质量 55),供监控大屏轮询。
一次性请求(非流),与任务并发安全(与任务截图同走 u2 minicap)。
大屏按需轮询:每设备约 2.5s 一帧,20 台 ≈ 0.2 核 CPU + 100KB/s。
"""
serial = request.args.get("serial", "")
if not serial:
return jsonify({"ok": False, "error": "缺少 serial"}), 400
import io
import uiautomator2 as u2
try:
d = u2.connect(serial)
img = d.screenshot()
if img is None:
return jsonify({"ok": False, "error": "截图失败"}), 503
if img.width > 360:
ratio = 360 / img.width
img = img.resize((360, int(img.height * ratio)))
buf = io.BytesIO()
img.convert("RGB").save(buf, "JPEG", quality=55)
return Response(buf.getvalue(), mimetype="image/jpeg",
headers={"Cache-Control": "no-store"})
except Exception as e:
return jsonify({"ok": False, "error": str(e)}), 503
@app.route("/api/screen/tap", methods=["POST"])
@perm_required(PERM_DEVICES)
def api_screen_tap():