Phase 1: 守护程序、TUI配置工具、安装脚本
核心变更: - padsleep.py: 完整重写,实现守护进程主循环 - ConfigManager: JSON配置加载/保存/变更检测 - TimeChecker: 亮屏时间段判断(支持跨天) - AdbManager: ADB操作封装,自动检测设备,命令后验证 - AuditLogger: 审计日志,超50MB自动裁剪保留后10% - IpcServer: Unix Socket JSON协议IPC服务端 - 自动恢复: 亮屏时段内检测到屏幕熄灭立即点亮 - 外部状态检测: 记录用户手动开关屏事件 - padsleep_tui.py: curses TUI配置工具 - 亮屏时间段增删改查 - ADB设备列表查看/选择 - 守护进程状态监控 - 审计日志查看 - padsleep.service: systemd服务单元(路径自适应) - install.sh: 安装到 /opt/apps/padsleep,自动设置权限 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
4d0db4c390
commit
458b67213f
+78
-10
@@ -21,7 +21,7 @@ from pathlib import Path
|
||||
# ── 常量 ────────────────────────────────────────────────────────
|
||||
|
||||
SOCKET_PATH = "/tmp/padsleep.sock"
|
||||
SCRIPT_DIR = Path(__file__).parent.resolve()
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
CONFIG_PATH = SCRIPT_DIR / "config.json"
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
@@ -201,6 +201,10 @@ class TuiApp:
|
||||
elif key == ord("h") or key == ord("H"):
|
||||
self._show_help()
|
||||
|
||||
elif key == ord("v") or key == ord("V"):
|
||||
if self.connected:
|
||||
self._dlg_view_log()
|
||||
|
||||
# 定时自动刷新
|
||||
if key == -1 and self.connected:
|
||||
self.refresh()
|
||||
@@ -297,11 +301,24 @@ class TuiApp:
|
||||
y = self._draw_separator(y, "[2] ADB 配置")
|
||||
|
||||
if self.status:
|
||||
dev = self.status.get("device") or "未选择"
|
||||
try:
|
||||
self.stdscr.addstr(y, 4, f"当前设备: {dev}")
|
||||
except curses.error:
|
||||
pass
|
||||
serial_cfg = self.status.get("device_serial", "")
|
||||
detected = self.status.get("device", "")
|
||||
if serial_cfg:
|
||||
dev_str = f"{serial_cfg} (已指定)"
|
||||
elif detected and detected != "无":
|
||||
dev_str = f"{detected} (自动检测)"
|
||||
|
||||
if detected and detected != "无":
|
||||
try:
|
||||
self.stdscr.addstr(y, 4, f"当前设备: {dev_str or detected}")
|
||||
self.stdscr.addstr(y, 4 + len(f"当前设备: {dev_str or detected}"), " ✓", curses.color_pair(3))
|
||||
except curses.error:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
self.stdscr.addstr(y, 4, "当前设备: 未连接", curses.color_pair(2))
|
||||
except curses.error:
|
||||
pass
|
||||
y += 1
|
||||
|
||||
if self.devices:
|
||||
@@ -319,7 +336,7 @@ class TuiApp:
|
||||
else:
|
||||
if y < h:
|
||||
try:
|
||||
self.stdscr.addstr(y, 4, "(无设备 — 请连接平板)")
|
||||
self.stdscr.addstr(y, 4, "(无设备 — 请连接平板)", curses.color_pair(2))
|
||||
except curses.error:
|
||||
pass
|
||||
y += 1
|
||||
@@ -369,7 +386,12 @@ class TuiApp:
|
||||
self.stdscr.addstr(f"{ar}", curses.color_pair(ar_color) | curses.A_BOLD)
|
||||
except curses.error:
|
||||
pass
|
||||
y += 1
|
||||
y += 1
|
||||
try:
|
||||
self.stdscr.addstr(y, 4, "配置: ")
|
||||
self.stdscr.addstr(f"{s.get('config_path', '?')}", curses.A_DIM)
|
||||
except curses.error:
|
||||
pass
|
||||
else:
|
||||
if y < h:
|
||||
try:
|
||||
@@ -380,7 +402,7 @@ class TuiApp:
|
||||
|
||||
if y < h:
|
||||
try:
|
||||
self.stdscr.addstr(y, 4, "[L]重载 [F]息屏 [N]亮屏 [T]重连 [H]帮助 [Q]退出",
|
||||
self.stdscr.addstr(y, 4, "[L]重载 [F]息屏 [N]亮屏 [V]日志 [T]重连 [H]帮助 [Q]退出",
|
||||
curses.A_DIM)
|
||||
except curses.error:
|
||||
pass
|
||||
@@ -403,7 +425,7 @@ class TuiApp:
|
||||
pass # show message below separator
|
||||
else:
|
||||
if self.connected:
|
||||
msg = "已连接 | ↑↓/jk 选择 A新增 E编辑 D删除 S选设备 H帮助"
|
||||
msg = "已连接 | ↑↓/jk 选择 A新增 E编辑 D删除 S选设备 V日志 H帮助"
|
||||
else:
|
||||
msg = "未检测到守护进程 | T 尝试连接"
|
||||
try:
|
||||
@@ -592,6 +614,51 @@ class TuiApp:
|
||||
except (ValueError, IndexError):
|
||||
return False
|
||||
|
||||
# ── 日志查看 ──
|
||||
|
||||
def _dlg_view_log(self):
|
||||
"""查看审计日志"""
|
||||
r = self.client.send("log_tail", lines=100)
|
||||
if not r.get("ok"):
|
||||
self.msg(f"获取日志失败: {r.get('error', '')}")
|
||||
return
|
||||
log_text = r["data"].get("log", "")
|
||||
|
||||
h, w = self.height, self.width
|
||||
bh, bw = min(h - 2, 30), min(w - 4, 80)
|
||||
by = max(1, (h - bh) // 2)
|
||||
bx = max(1, (w - bw) // 2)
|
||||
|
||||
win = curses.newwin(bh, bw, by, bx)
|
||||
lines = log_text.split("\n")
|
||||
|
||||
scroll = max(0, len(lines) - (bh - 3))
|
||||
while True:
|
||||
win.box()
|
||||
win.addstr(0, 2, " 审计日志 (最近100行) ", curses.A_BOLD)
|
||||
visible = lines[scroll:scroll + bh - 3]
|
||||
for i, line in enumerate(visible):
|
||||
if i >= bh - 3:
|
||||
break
|
||||
try:
|
||||
win.addstr(i + 1, 2, line[:bw - 4])
|
||||
except curses.error:
|
||||
pass
|
||||
footer = f" 行 {scroll+1}-{scroll+len(visible)}/{len(lines)} ↑↓滚动 ESC返回 "
|
||||
try:
|
||||
win.addstr(bh - 1, 2, footer[:bw - 4], curses.A_DIM)
|
||||
except curses.error:
|
||||
pass
|
||||
win.refresh()
|
||||
|
||||
k = win.getch()
|
||||
if k == 27 or k == ord("q"): # ESC/q
|
||||
break
|
||||
elif k in (curses.KEY_UP, ord("k")) and scroll > 0:
|
||||
scroll -= 1
|
||||
elif k in (curses.KEY_DOWN, ord("j")) and scroll < len(lines) - (bh - 3):
|
||||
scroll += 1
|
||||
|
||||
# ── 帮助 ──
|
||||
|
||||
def _show_help(self):
|
||||
@@ -613,6 +680,7 @@ class TuiApp:
|
||||
" S 选择 ADB 设备",
|
||||
" R 刷新状态",
|
||||
" L 重载守护进程配置",
|
||||
" V 查看审计日志",
|
||||
" F 强制息屏",
|
||||
" N 强制亮屏",
|
||||
" T 重新连接守护进程",
|
||||
|
||||
Reference in New Issue
Block a user