合并远程变更: adbutils 改造 + 无线 ADB 支持 + TUI 优化
合并内容: - 远程: adbutils 库替换 subprocess、无线 ADB 连接、TUI 闪烁修复 - 本地: 审计日志、命令执行后验证重试、安装脚本 - 合并后保留双方特性: adbutils + 审计日志 + 无线 ADB + 命令验证
This commit is contained in:
+25
@@ -27,6 +27,10 @@ share/python-wheels/
|
|||||||
*.egg
|
*.egg
|
||||||
MANIFEST
|
MANIFEST
|
||||||
|
|
||||||
|
# PyPI
|
||||||
|
*.whl
|
||||||
|
*.tar.gz
|
||||||
|
|
||||||
# PyInstaller
|
# PyInstaller
|
||||||
# Usually these files are written by a python script from a template
|
# Usually these files are written by a python script from a template
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
@@ -174,3 +178,24 @@ cython_debug/
|
|||||||
# PyPI configuration file
|
# PyPI configuration file
|
||||||
.pypirc
|
.pypirc
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# 开发环境
|
||||||
|
.vscode
|
||||||
|
.mimocode
|
||||||
|
.codewhale
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,166 @@
|
|||||||
# padsleep_adb
|
# padsleep_adb
|
||||||
|
|
||||||
基于树莓派,通过adb控制安卓平板亮屏和息屏
|
基于树莓派,通过 ADB 控制安卓平板自动亮屏和息屏的守护程序。
|
||||||
|
|
||||||
## 项目原理
|
## 功能特性
|
||||||
|
|
||||||
当触发设定的条件时,通过adb命令,模拟锁屏按键,控制pad自动亮屏和息屏。实现ha终端的省电和延长屏幕寿命。
|
- **定时控制**:根据配置的时间段自动亮屏/息屏
|
||||||
|
- **超时保护**:屏幕亮起超过设定时长且不在亮屏时间段内 → 自动息屏
|
||||||
|
- **无线 ADB**:支持通过 TCP/IP 连接安卓设备(无线调试)
|
||||||
|
- **TUI 管理**:提供终端界面工具,方便查看状态和修改配置
|
||||||
|
- **IPC 通信**:通过 Unix Socket 与 TUI 工具通信
|
||||||
|
- **自动重连**:无线连接断开后自动尝试重新连接
|
||||||
|
|
||||||
## 开发阶段
|
## 系统要求
|
||||||
|
|
||||||
第一阶段:
|
- 树莓派(或其他 Linux 设备)
|
||||||
|
- Python 3.10+
|
||||||
|
- ADB (Android Debug Bridge)
|
||||||
|
- 安卓平板(已开启 USB 调试或无线调试)
|
||||||
|
|
||||||
根据时间,定制自动亮屏和自动息屏的时间;当检测到亮屏时间大于5分钟且不在设定的亮屏时间段内,则自动息屏。
|
## 安装
|
||||||
|
|
||||||
第二阶段:
|
### 1. 安装系统依赖
|
||||||
|
|
||||||
通过GPIO安装人体传感器,做到人来屏亮,人走屏灭。
|
```bash
|
||||||
|
# Debian/Ubuntu
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install adb python3-curses
|
||||||
|
|
||||||
## 开发细节
|
# 树莓派 OS
|
||||||
|
sudo apt install adb
|
||||||
|
```
|
||||||
|
|
||||||
- 在windows上开发,在树莓派上执行
|
### 2. 安装 Python 依赖
|
||||||
|
|
||||||
- 需要安装adb命令行
|
```bash
|
||||||
|
cd padsleep_adb
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
- 启动后,能自动以服务方式守护运行
|
依赖列表:
|
||||||
|
- `adbutils>=2.12.0` — Python ADB 库
|
||||||
|
|
||||||
- 首次启动时,需要处理adb授权有关问题
|
## 使用方法
|
||||||
|
|
||||||
|
### 启动守护程序
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python padsleep.py
|
||||||
|
```
|
||||||
|
|
||||||
|
守护程序会自动:
|
||||||
|
1. 加载配置文件 `config.json`
|
||||||
|
2. 检测 ADB 设备
|
||||||
|
3. 启动 IPC 服务(`/tmp/padsleep.sock`)
|
||||||
|
4. 进入主循环,定期检查屏幕状态
|
||||||
|
|
||||||
|
### 启动 TUI 管理工具
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 新终端窗口
|
||||||
|
python padsleep_tui.py
|
||||||
|
```
|
||||||
|
|
||||||
|
TUI 快捷键:
|
||||||
|
|
||||||
|
| 按键 | 功能 |
|
||||||
|
|------|------|
|
||||||
|
| `↑/k` `↓/j` | 选择亮屏时间段 |
|
||||||
|
| `A` | 新增亮屏时间段 |
|
||||||
|
| `E` | 编辑选中时间段 |
|
||||||
|
| `D` | 删除选中时间段 |
|
||||||
|
| `S` | 选择 ADB 设备 |
|
||||||
|
| `W` | 无线连接设备 |
|
||||||
|
| `R` | 刷新状态 |
|
||||||
|
| `L` | 重载守护进程配置 |
|
||||||
|
| `F` | 强制息屏 |
|
||||||
|
| `N` | 强制亮屏 |
|
||||||
|
| `T` | 重新连接守护进程 |
|
||||||
|
| `H` | 显示帮助 |
|
||||||
|
| `Q` | 退出 |
|
||||||
|
|
||||||
|
## 配置说明
|
||||||
|
|
||||||
|
配置文件 `config.json` 示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"screen_on_periods": [
|
||||||
|
{"start": "08:00", "end": "22:00"}
|
||||||
|
],
|
||||||
|
"check_interval_seconds": 30,
|
||||||
|
"auto_sleep_delay_minutes": 5,
|
||||||
|
"adb_device_serial": "",
|
||||||
|
"adb_wireless_host": "",
|
||||||
|
"adb_wireless_port": 5555,
|
||||||
|
"adb_wireless_auto_connect": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 字段说明
|
||||||
|
|
||||||
|
| 字段 | 类型 | 默认值 | 说明 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| `screen_on_periods` | array | `[{"start":"08:00","end":"22:00"}]` | 亮屏时间段列表,支持跨天(如 22:00~08:00) |
|
||||||
|
| `check_interval_seconds` | int | `30` | 检查间隔(秒) |
|
||||||
|
| `auto_sleep_delay_minutes` | int | `5` | 非亮屏时段屏幕亮起多久后自动息屏(分钟) |
|
||||||
|
| `adb_device_serial` | string | `""` | 指定 USB 设备 serial,空表示自动选择第一个 |
|
||||||
|
| `adb_wireless_host` | string | `""` | 无线设备 IP 地址,空表示不使用无线连接 |
|
||||||
|
| `adb_wireless_port` | int | `5555` | 无线设备端口 |
|
||||||
|
| `adb_wireless_auto_connect` | bool | `false` | 启动时是否自动连接无线设备 |
|
||||||
|
|
||||||
|
## 无线 ADB 连接
|
||||||
|
|
||||||
|
### 方式一:配置文件预设
|
||||||
|
|
||||||
|
在 `config.json` 中设置:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"adb_wireless_host": "192.168.1.100",
|
||||||
|
"adb_wireless_port": 5555,
|
||||||
|
"adb_wireless_auto_connect": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方式二:TUI 手动连接
|
||||||
|
|
||||||
|
1. 启动 TUI 工具
|
||||||
|
2. 按 `W` 键
|
||||||
|
3. 输入 IP 地址和端口
|
||||||
|
4. 确认连接
|
||||||
|
|
||||||
|
### 开启安卓设备无线调试
|
||||||
|
|
||||||
|
1. 连接 USB,执行 `adb tcpip 5555`
|
||||||
|
2. 或在设备上:设置 → 开发者选项 → 无线调试 → 开启
|
||||||
|
|
||||||
|
### 连接优先级
|
||||||
|
|
||||||
|
程序采用 **USB 优先** 策略:
|
||||||
|
1. 首先尝试 USB 连接
|
||||||
|
2. 如果 USB 不可用且配置了无线,尝试无线连接
|
||||||
|
3. 无线连接断开后自动重连(最多 3 次)
|
||||||
|
|
||||||
|
## 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
padsleep_adb/
|
||||||
|
├── padsleep.py # 守护程序主程序
|
||||||
|
├── padsleep_tui.py # TUI 管理工具
|
||||||
|
├── config.json # 配置文件(首次运行自动生成)
|
||||||
|
├── requirements.txt # Python 依赖
|
||||||
|
└── README.md # 本文档
|
||||||
|
```
|
||||||
|
|
||||||
|
## 开发说明
|
||||||
|
|
||||||
|
- 在 Windows 上开发,在树莓派上运行
|
||||||
|
- 使用 `adbutils` 库与 ADB Server 通信
|
||||||
|
- IPC 使用 Unix Socket + JSON 行协议
|
||||||
|
- TUI 使用 Python `curses` 库
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|||||||
+319
-124
@@ -16,14 +16,14 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
import socket
|
import socket
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from socketserver import ThreadingUnixStreamServer, StreamRequestHandler
|
from socketserver import ThreadingUnixStreamServer, StreamRequestHandler
|
||||||
|
|
||||||
|
from adbutils import AdbClient, AdbDevice
|
||||||
|
|
||||||
# ── 常量 ────────────────────────────────────────────────────────
|
# ── 常量 ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
# 注意: .resolve().parent 确保即使通过 symlink 调用也能正确找到真实目录
|
# 注意: .resolve().parent 确保即使通过 symlink 调用也能正确找到真实目录
|
||||||
@@ -39,7 +39,9 @@ DEFAULT_CONFIG = {
|
|||||||
"check_interval_seconds": 30,
|
"check_interval_seconds": 30,
|
||||||
"auto_sleep_delay_minutes": 5,
|
"auto_sleep_delay_minutes": 5,
|
||||||
"adb_device_serial": "",
|
"adb_device_serial": "",
|
||||||
"adb_path": "adb",
|
"adb_wireless_host": "",
|
||||||
|
"adb_wireless_port": 5555,
|
||||||
|
"adb_wireless_auto_connect": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── 日志 ────────────────────────────────────────────────────────
|
# ── 日志 ────────────────────────────────────────────────────────
|
||||||
@@ -256,74 +258,187 @@ class TimeChecker:
|
|||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════
|
||||||
# AdbManager — ADB 操作封装
|
# AdbManager — ADB 操作封装(使用 adbutils 库)
|
||||||
# ═══════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
class AdbManager:
|
class AdbManager:
|
||||||
"""封装所有 ADB CLI 操作,所有异常都被捕获,不会导致进程退出。"""
|
"""封装所有 ADB 操作,使用 adbutils 库实现持久连接。"""
|
||||||
|
|
||||||
def __init__(self, config_getter, audit_logger: AuditLogger | None = None):
|
def __init__(self, config_getter, audit_logger: AuditLogger | None = None):
|
||||||
self._config_getter = config_getter
|
self._config_getter = config_getter
|
||||||
self._audit = audit_logger
|
self._audit = audit_logger
|
||||||
|
self._client: AdbClient | None = None
|
||||||
|
self._device: AdbDevice | None = None
|
||||||
self._ready = False
|
self._ready = False
|
||||||
self._last_error = ""
|
self._last_error = ""
|
||||||
self._screen_on_since: float | None = None
|
self._screen_on_since: float | None = None
|
||||||
self._last_known_screen_on = False
|
self._last_known_screen_on = False
|
||||||
self._last_adb_check = 0 # 限频用
|
self._last_adb_check = 0 # 限频用
|
||||||
self._current_serial: str | None = None # 自动检测到的设备串号
|
self._current_serial: str | None = None # 自动检测到的设备串号
|
||||||
|
self._wireless_addr: str = "" # 当前无线连接地址 (IP:PORT)
|
||||||
|
self._wireless_retries: int = 0 # 无线重连尝试次数
|
||||||
|
|
||||||
# ── 底层命令执行 ──
|
# ── 连接管理 ──
|
||||||
|
|
||||||
def _exec(self, args: list, timeout: int = 10) -> tuple:
|
def _ensure_client(self) -> AdbClient:
|
||||||
"""执行 ADB 命令,返回 (returncode, stdout, stderr)。永不抛出。"""
|
"""懒初始化 ADB 客户端连接。"""
|
||||||
|
if self._client is None:
|
||||||
|
self._client = AdbClient(host="127.0.0.1", port=5037)
|
||||||
|
return self._client
|
||||||
|
|
||||||
|
def _ensure_device(self) -> AdbDevice | None:
|
||||||
|
"""获取目标设备,支持串号过滤。"""
|
||||||
config = self._config_getter()
|
config = self._config_getter()
|
||||||
adb_path = config.get("adb_path", "adb")
|
target_serial = config.get("adb_device_serial", "")
|
||||||
serial = config.get("adb_device_serial", "") or self._current_serial or ""
|
|
||||||
|
|
||||||
cmd = [adb_path]
|
|
||||||
# "devices" / "--version" 命令不加 -s
|
|
||||||
if serial and args and args[0] not in ("devices", "--version"):
|
|
||||||
cmd.extend(["-s", serial])
|
|
||||||
cmd.extend(args)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
|
client = self._ensure_client()
|
||||||
return r.returncode, r.stdout.strip(), r.stderr.strip()
|
if target_serial:
|
||||||
except FileNotFoundError:
|
self._device = client.device(target_serial)
|
||||||
self._ready = False
|
else:
|
||||||
self._last_error = f"ADB 未找到({adb_path}),请安装: sudo apt install adb"
|
devices = client.device_list()
|
||||||
except subprocess.TimeoutExpired:
|
self._device = devices[0] if devices else None
|
||||||
self._ready = False
|
return self._device
|
||||||
self._last_error = "ADB 命令超时"
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._ready = False
|
self._device = None
|
||||||
self._last_error = str(e)
|
self._last_error = str(e)
|
||||||
return -1, "", self._last_error
|
return None
|
||||||
|
|
||||||
|
# ── 无线连接管理 ──
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_wireless_serial(serial: str) -> bool:
|
||||||
|
"""判断 serial 是否为无线格式(IP:PORT)。"""
|
||||||
|
parts = serial.split(":")
|
||||||
|
if len(parts) != 2:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
port = int(parts[1])
|
||||||
|
ip_parts = parts[0].split(".")
|
||||||
|
if len(ip_parts) == 4 and all(0 <= int(p) <= 255 for p in ip_parts):
|
||||||
|
return True
|
||||||
|
except (ValueError, IndexError):
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
def connect_wireless(self, host: str, port: int = 5555, timeout: float = 5.0) -> tuple[bool, str]:
|
||||||
|
"""连接无线 ADB 设备。返回 (success, message)。"""
|
||||||
|
addr = f"{host}:{port}"
|
||||||
|
try:
|
||||||
|
client = self._ensure_client()
|
||||||
|
result = client.connect(addr, timeout=timeout)
|
||||||
|
if "connected" in result.lower():
|
||||||
|
self._wireless_addr = addr
|
||||||
|
self._wireless_retries = 0
|
||||||
|
log.info("无线 ADB 已连接: %s", addr)
|
||||||
|
return True, result
|
||||||
|
return False, result
|
||||||
|
except Exception as e:
|
||||||
|
self._last_error = str(e)
|
||||||
|
log.error("无线连接失败: %s", e)
|
||||||
|
return False, str(e)
|
||||||
|
|
||||||
|
def disconnect_wireless(self, addr: str = "") -> bool:
|
||||||
|
"""断开无线 ADB 连接。"""
|
||||||
|
target = addr or self._wireless_addr
|
||||||
|
if not target:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
client = self._ensure_client()
|
||||||
|
client.disconnect(target)
|
||||||
|
log.info("无线 ADB 已断开: %s", target)
|
||||||
|
if target == self._wireless_addr:
|
||||||
|
self._wireless_addr = ""
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
self._last_error = str(e)
|
||||||
|
log.error("断开无线连接失败: %s", e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_connection_type(self) -> str:
|
||||||
|
"""返回当前连接类型:'usb' / 'wireless' / 'none'。"""
|
||||||
|
if self._device is None:
|
||||||
|
return "none"
|
||||||
|
if self.is_wireless_serial(self._device.serial):
|
||||||
|
return "wireless"
|
||||||
|
return "usb"
|
||||||
|
|
||||||
|
def ensure_wireless_connection(self) -> bool:
|
||||||
|
"""确保无线连接存活,失败时尝试重连。返回是否成功。"""
|
||||||
|
config = self._config_getter()
|
||||||
|
wireless_host = config.get("adb_wireless_host", "")
|
||||||
|
wireless_port = config.get("adb_wireless_port", 5555)
|
||||||
|
|
||||||
|
if not wireless_host:
|
||||||
|
return False
|
||||||
|
|
||||||
|
addr = f"{wireless_host}:{wireless_port}"
|
||||||
|
|
||||||
|
# 检查是否已有连接
|
||||||
|
if self._device and self._device.serial == addr:
|
||||||
|
try:
|
||||||
|
self._device.getprop("ro.build.version.sdk")
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
# 连接已断开,尝试重连
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 尝试重连(最多 3 次)
|
||||||
|
max_retries = 3
|
||||||
|
for i in range(max_retries):
|
||||||
|
self._wireless_retries += 1
|
||||||
|
log.info("尝试重连无线设备 (%d/%d): %s", i + 1, max_retries, addr)
|
||||||
|
success, msg = self.connect_wireless(wireless_host, wireless_port, timeout=3.0)
|
||||||
|
if success:
|
||||||
|
# 重新获取设备
|
||||||
|
try:
|
||||||
|
client = self._ensure_client()
|
||||||
|
self._device = client.device(addr)
|
||||||
|
self._device.getprop("ro.build.version.sdk")
|
||||||
|
self._ready = True
|
||||||
|
self._last_error = ""
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
self._last_error = f"无线重连失败: {addr}"
|
||||||
|
return False
|
||||||
|
|
||||||
# ── 设备管理 ──
|
# ── 设备管理 ──
|
||||||
|
|
||||||
def check_available(self) -> bool:
|
def check_available(self) -> bool:
|
||||||
"""检查 ADB 是否可执行。"""
|
"""检查 ADB Server 是否可连接。"""
|
||||||
rc, _, _ = self._exec(["--version"], timeout=5)
|
try:
|
||||||
return rc == 0
|
client = self._ensure_client()
|
||||||
|
client.device_list()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
self._last_error = f"ADB Server 不可用: {e}"
|
||||||
|
return False
|
||||||
|
|
||||||
def list_devices(self) -> list[dict]:
|
def list_devices(self) -> list[dict]:
|
||||||
"""返回 [{"serial": "...", "status": "..."}]。"""
|
"""返回 [{"serial": "...", "status": "device", "connection_type": "usb"/"wireless"}]。"""
|
||||||
rc, out, _ = self._exec(["devices"])
|
try:
|
||||||
if rc != 0:
|
client = self._ensure_client()
|
||||||
|
devices = client.device_list()
|
||||||
|
result = []
|
||||||
|
for d in devices:
|
||||||
|
conn_type = "wireless" if self.is_wireless_serial(d.serial) else "usb"
|
||||||
|
result.append({
|
||||||
|
"serial": d.serial,
|
||||||
|
"status": "device",
|
||||||
|
"connection_type": conn_type,
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
self._last_error = str(e)
|
||||||
return []
|
return []
|
||||||
devices = []
|
|
||||||
for line in out.split("\n"):
|
|
||||||
parts = line.strip().split("\t")
|
|
||||||
if len(parts) >= 2 and not line.startswith("List"):
|
|
||||||
devices.append({"serial": parts[0], "status": parts[1]})
|
|
||||||
return devices
|
|
||||||
|
|
||||||
def get_ready_device(self) -> str | None:
|
def get_ready_device(self) -> str | None:
|
||||||
"""返回第一个状态为 device 的串号,无则返回 None。"""
|
"""返回当前已连接设备的串号,无则返回 None。"""
|
||||||
for d in self.list_devices():
|
if self._device is not None:
|
||||||
if d["status"] == "device":
|
return self._device.serial
|
||||||
return d["serial"]
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def is_ready(self) -> bool:
|
def is_ready(self) -> bool:
|
||||||
@@ -334,53 +449,86 @@ class AdbManager:
|
|||||||
self._last_adb_check = now
|
self._last_adb_check = now
|
||||||
|
|
||||||
config = self._config_getter()
|
config = self._config_getter()
|
||||||
target = config.get("adb_device_serial", "")
|
target_serial = config.get("adb_device_serial", "")
|
||||||
|
wireless_host = config.get("adb_wireless_host", "")
|
||||||
|
|
||||||
devices = self.list_devices()
|
try:
|
||||||
for d in devices:
|
client = self._ensure_client()
|
||||||
if d["status"] == "device":
|
devices = client.device_list()
|
||||||
if not target or d["serial"] == target:
|
|
||||||
self._ready = True
|
# USB 连接优先
|
||||||
self._current_serial = d["serial"]
|
for d in devices:
|
||||||
self._last_error = f"已就绪: {d['serial']}"
|
if not self.is_wireless_serial(d.serial):
|
||||||
|
if not target_serial or d.serial == target_serial:
|
||||||
|
d.getprop("ro.build.version.sdk")
|
||||||
|
self._device = d
|
||||||
|
self._current_serial = d.serial
|
||||||
|
self._ready = True
|
||||||
|
self._last_error = ""
|
||||||
|
return True
|
||||||
|
|
||||||
|
# 尝试无线连接
|
||||||
|
if wireless_host:
|
||||||
|
wireless_port = config.get("adb_wireless_port", 5555)
|
||||||
|
wireless_addr = f"{wireless_host}:{wireless_port}"
|
||||||
|
|
||||||
|
# 检查是否已在设备列表中
|
||||||
|
for d in devices:
|
||||||
|
if d.serial == wireless_addr:
|
||||||
|
d.getprop("ro.build.version.sdk")
|
||||||
|
self._device = d
|
||||||
|
self._ready = True
|
||||||
|
self._last_error = ""
|
||||||
|
self._wireless_addr = wireless_addr
|
||||||
|
return True
|
||||||
|
|
||||||
|
# 不在列表中,尝试连接
|
||||||
|
if self.ensure_wireless_connection():
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# 诊断
|
if not devices and not wireless_host:
|
||||||
unauthorized = [d for d in devices if d["status"] == "unauthorized"]
|
self._last_error = "未检测到 ADB 设备,请连接平板"
|
||||||
if unauthorized:
|
elif not devices:
|
||||||
self._last_error = f"设备未授权: {[d['serial'] for d in unauthorized]},请在平板上确认授权"
|
self._last_error = f"无线设备未连接: {wireless_host}:{config.get('adb_wireless_port', 5555)}"
|
||||||
elif not devices:
|
else:
|
||||||
self._last_error = "未检测到 ADB 设备,请连接平板"
|
self._last_error = f"未找到指定设备: {target_serial}"
|
||||||
else:
|
except Exception as e:
|
||||||
self._last_error = "没有已就绪的 ADB 设备"
|
self._last_error = str(e)
|
||||||
|
|
||||||
self._ready = False
|
self._ready = False
|
||||||
|
self._device = None
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# ── 屏幕状态 ──
|
# ── 屏幕状态 ──
|
||||||
|
|
||||||
def get_screen_state(self) -> tuple[bool, str]:
|
def get_screen_state(self) -> tuple[bool, str]:
|
||||||
"""返回 (is_on, wakefulness),失败时返回上次已知状态。"""
|
"""返回 (is_on, wakefulness),失败时返回上次已知状态。"""
|
||||||
rc, out, _ = self._exec(["shell", "dumpsys", "power"])
|
try:
|
||||||
if rc != 0:
|
device = self._ensure_device()
|
||||||
return self._last_known_screen_on, "unknown"
|
if device is None:
|
||||||
|
return self._last_known_screen_on, "unknown"
|
||||||
|
|
||||||
for line in out.split("\n"):
|
output = device.shell("dumpsys power")
|
||||||
line = line.strip()
|
for line in output.split("\n"):
|
||||||
if "mWakefulness" in line:
|
line = line.strip()
|
||||||
state = line.split("=")[-1].strip()
|
if "mWakefulness" in line:
|
||||||
is_on = state == "Awake"
|
state = line.split("=")[-1].strip()
|
||||||
self._last_known_screen_on = is_on
|
is_on = state == "Awake"
|
||||||
if is_on and self._screen_on_since is None:
|
self._last_known_screen_on = is_on
|
||||||
self._screen_on_since = time.time()
|
if is_on and self._screen_on_since is None:
|
||||||
elif not is_on:
|
self._screen_on_since = time.time()
|
||||||
self._screen_on_since = None
|
elif not is_on:
|
||||||
return is_on, state
|
self._screen_on_since = None
|
||||||
|
return is_on, state
|
||||||
|
|
||||||
|
# fallback
|
||||||
|
for line in output.split("\n"):
|
||||||
|
if "Display Power" in line or "mScreenOn" in line:
|
||||||
|
is_on = "ON" in line.upper() or "=true" in line.lower()
|
||||||
|
return is_on, "unknown"
|
||||||
|
except Exception as e:
|
||||||
|
self._last_error = str(e)
|
||||||
|
|
||||||
# fallback
|
|
||||||
for line in out.split("\n"):
|
|
||||||
if "Display Power" in line or "mScreenOn" in line:
|
|
||||||
is_on = "ON" in line.upper() or "=true" in line.lower()
|
|
||||||
return is_on, "unknown"
|
|
||||||
return self._last_known_screen_on, "unknown"
|
return self._last_known_screen_on, "unknown"
|
||||||
|
|
||||||
def get_screen_on_duration(self) -> float:
|
def get_screen_on_duration(self) -> float:
|
||||||
@@ -394,69 +542,82 @@ class AdbManager:
|
|||||||
def screen_off(self) -> bool:
|
def screen_off(self) -> bool:
|
||||||
"""发送息屏命令,等待 2 秒后验证,失败时重试一次。"""
|
"""发送息屏命令,等待 2 秒后验证,失败时重试一次。"""
|
||||||
log.info("执行息屏操作")
|
log.info("执行息屏操作")
|
||||||
if self._audit:
|
try:
|
||||||
self._audit.log("息屏", "发出息屏指令")
|
device = self._ensure_device()
|
||||||
rc, _, err = self._exec(["shell", "input", "keyevent", "26"])
|
if device is None:
|
||||||
if rc != 0:
|
log.error("息屏失败: 无可用设备")
|
||||||
log.error("息屏命令失败: %s", err)
|
return False
|
||||||
if self._audit:
|
if self._audit:
|
||||||
self._audit.log("息屏失败", err)
|
self._audit.log("息屏", "发出息屏指令")
|
||||||
return False
|
device.shell("input keyevent 26")
|
||||||
|
|
||||||
# 等待并验证
|
# 等待并验证
|
||||||
time.sleep(2)
|
|
||||||
is_on, _ = self.get_screen_state()
|
|
||||||
if is_on:
|
|
||||||
log.warning("息屏命令已发出但屏幕未熄灭,重试一次")
|
|
||||||
rc, _, err = self._exec(["shell", "input", "keyevent", "26"])
|
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
is_on, _ = self.get_screen_state()
|
is_on, _ = self.get_screen_state()
|
||||||
if is_on:
|
if is_on:
|
||||||
log.error("息屏重试后仍未熄灭")
|
log.warning("息屏命令已发出但屏幕未熄灭,重试一次")
|
||||||
if self._audit:
|
device.shell("input keyevent 26")
|
||||||
self._audit.log("息屏异常", "屏幕状态未变化")
|
time.sleep(2)
|
||||||
# 不更新 _screen_on_since
|
is_on, _ = self.get_screen_state()
|
||||||
return False
|
if is_on:
|
||||||
|
log.error("息屏重试后仍未熄灭")
|
||||||
|
self._last_error = "息屏验证失败"
|
||||||
|
if self._audit:
|
||||||
|
self._audit.log("息屏异常", "屏幕状态未变化")
|
||||||
|
return False
|
||||||
|
|
||||||
self._screen_on_since = None
|
self._screen_on_since = None
|
||||||
log.info("息屏成功")
|
log.info("息屏成功")
|
||||||
if self._audit:
|
if self._audit:
|
||||||
self._audit.log("息屏成功", "屏幕已熄灭")
|
self._audit.log("息屏成功", "屏幕已熄灭")
|
||||||
return True
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
self._last_error = str(e)
|
||||||
|
log.error("息屏失败: %s", e)
|
||||||
|
if self._audit:
|
||||||
|
self._audit.log("息屏失败", str(e))
|
||||||
|
return False
|
||||||
|
|
||||||
def screen_on(self) -> bool:
|
def screen_on(self) -> bool:
|
||||||
"""发送亮屏命令,等待 2 秒后验证,失败时重试一次。"""
|
"""发送亮屏命令,等待 2 秒后验证,失败时重试一次。"""
|
||||||
log.info("执行亮屏操作")
|
log.info("执行亮屏操作")
|
||||||
if self._audit:
|
try:
|
||||||
self._audit.log("亮屏", "发出亮屏指令")
|
device = self._ensure_device()
|
||||||
rc, _, err = self._exec(["shell", "input", "keyevent", "26"])
|
if device is None:
|
||||||
if rc != 0:
|
log.error("亮屏失败: 无可用设备")
|
||||||
log.error("亮屏命令失败: %s", err)
|
return False
|
||||||
if self._audit:
|
if self._audit:
|
||||||
self._audit.log("亮屏失败", err)
|
self._audit.log("亮屏", "发出亮屏指令")
|
||||||
return False
|
device.shell("input keyevent 26")
|
||||||
|
|
||||||
# 等待并验证
|
# 等待并验证
|
||||||
time.sleep(2)
|
|
||||||
is_on, _ = self.get_screen_state()
|
|
||||||
if not is_on:
|
|
||||||
log.warning("亮屏命令已发出但屏幕未点亮,重试一次")
|
|
||||||
rc, _, err = self._exec(["shell", "input", "keyevent", "26"])
|
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
is_on, _ = self.get_screen_state()
|
is_on, _ = self.get_screen_state()
|
||||||
if not is_on:
|
if not is_on:
|
||||||
log.error("亮屏重试后仍未点亮")
|
log.warning("亮屏命令已发出但屏幕未点亮,重试一次")
|
||||||
if self._audit:
|
device.shell("input keyevent 26")
|
||||||
self._audit.log("亮屏异常", "屏幕状态未变化")
|
time.sleep(2)
|
||||||
return False
|
is_on, _ = self.get_screen_state()
|
||||||
|
if not is_on:
|
||||||
|
log.error("亮屏重试后仍未点亮")
|
||||||
|
self._last_error = "亮屏验证失败"
|
||||||
|
if self._audit:
|
||||||
|
self._audit.log("亮屏异常", "屏幕状态未变化")
|
||||||
|
return False
|
||||||
|
|
||||||
self._screen_on_since = time.time()
|
self._screen_on_since = time.time()
|
||||||
# 解锁屏幕
|
# 解锁屏幕
|
||||||
self._exec(["shell", "input", "keyevent", "82"])
|
device.shell("input keyevent 82")
|
||||||
log.info("亮屏成功")
|
log.info("亮屏成功")
|
||||||
if self._audit:
|
if self._audit:
|
||||||
self._audit.log("亮屏成功", "屏幕已点亮")
|
self._audit.log("亮屏成功", "屏幕已点亮")
|
||||||
return True
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
self._last_error = str(e)
|
||||||
|
log.error("亮屏失败: %s", e)
|
||||||
|
if self._audit:
|
||||||
|
self._audit.log("亮屏失败", str(e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════
|
||||||
@@ -576,6 +737,9 @@ class PadSleepApp:
|
|||||||
status["check_interval"] = cfg.get("check_interval_seconds", 30)
|
status["check_interval"] = cfg.get("check_interval_seconds", 30)
|
||||||
status["config_path"] = str(self.config_mgr.path)
|
status["config_path"] = str(self.config_mgr.path)
|
||||||
status["log_path"] = str(LOG_PATH)
|
status["log_path"] = str(LOG_PATH)
|
||||||
|
status["connection_type"] = self.adb.get_connection_type()
|
||||||
|
status["wireless_host"] = cfg.get("adb_wireless_host", "")
|
||||||
|
status["wireless_port"] = cfg.get("adb_wireless_port", 5555)
|
||||||
return {"ok": True, "data": status}
|
return {"ok": True, "data": status}
|
||||||
|
|
||||||
if cmd == "get_config":
|
if cmd == "get_config":
|
||||||
@@ -621,6 +785,25 @@ class PadSleepApp:
|
|||||||
n = request.get("lines", 50)
|
n = request.get("lines", 50)
|
||||||
return {"ok": True, "data": {"log": self.audit.read_tail(n)}}
|
return {"ok": True, "data": {"log": self.audit.read_tail(n)}}
|
||||||
|
|
||||||
|
if cmd == "connect_wireless":
|
||||||
|
host = request.get("host", "")
|
||||||
|
port = request.get("port", 5555)
|
||||||
|
if not host:
|
||||||
|
return {"ok": False, "error": "请提供 IP 地址"}
|
||||||
|
success, msg = self.adb.connect_wireless(host, int(port))
|
||||||
|
if success:
|
||||||
|
# 更新配置
|
||||||
|
self.config_mgr.update({
|
||||||
|
"adb_wireless_host": host,
|
||||||
|
"adb_wireless_port": int(port),
|
||||||
|
})
|
||||||
|
return {"ok": success, "data": {"message": msg}}
|
||||||
|
|
||||||
|
if cmd == "disconnect_wireless":
|
||||||
|
addr = request.get("addr", "")
|
||||||
|
ok = self.adb.disconnect_wireless(addr)
|
||||||
|
return {"ok": ok}
|
||||||
|
|
||||||
return {"ok": False, "error": f"未知命令: {cmd}"}
|
return {"ok": False, "error": f"未知命令: {cmd}"}
|
||||||
|
|
||||||
# ── 主循环 ──
|
# ── 主循环 ──
|
||||||
@@ -643,6 +826,18 @@ class PadSleepApp:
|
|||||||
else:
|
else:
|
||||||
log.info("ADB 可执行文件已就绪")
|
log.info("ADB 可执行文件已就绪")
|
||||||
|
|
||||||
|
# 自动连接无线设备
|
||||||
|
config = self.config_mgr.get()
|
||||||
|
if config.get("adb_wireless_auto_connect") and config.get("adb_wireless_host"):
|
||||||
|
host = config["adb_wireless_host"]
|
||||||
|
port = config.get("adb_wireless_port", 5555)
|
||||||
|
log.info("尝试自动连接无线设备: %s:%d", host, port)
|
||||||
|
success, msg = self.adb.connect_wireless(host, port, timeout=5.0)
|
||||||
|
if success:
|
||||||
|
log.info("无线设备已连接: %s", msg)
|
||||||
|
else:
|
||||||
|
log.warning("无线设备连接失败: %s", msg)
|
||||||
|
|
||||||
# 启动 IPC
|
# 启动 IPC
|
||||||
self.ipc.start()
|
self.ipc.start()
|
||||||
|
|
||||||
|
|||||||
+95
-13
@@ -135,7 +135,6 @@ class TuiApp:
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
self.height, self.width = self.stdscr.getmaxyx()
|
self.height, self.width = self.stdscr.getmaxyx()
|
||||||
self.stdscr.clear()
|
|
||||||
self._draw()
|
self._draw()
|
||||||
self.stdscr.refresh()
|
self.stdscr.refresh()
|
||||||
|
|
||||||
@@ -198,6 +197,9 @@ class TuiApp:
|
|||||||
elif key == ord("s") or key == ord("S"):
|
elif key == ord("s") or key == ord("S"):
|
||||||
self._dlg_select_device()
|
self._dlg_select_device()
|
||||||
|
|
||||||
|
elif key == ord("w") or key == ord("W"):
|
||||||
|
self._dlg_connect_wireless()
|
||||||
|
|
||||||
elif key == ord("h") or key == ord("H"):
|
elif key == ord("h") or key == ord("H"):
|
||||||
self._show_help()
|
self._show_help()
|
||||||
|
|
||||||
@@ -228,6 +230,14 @@ class TuiApp:
|
|||||||
y = self._draw_status_panel(y)
|
y = self._draw_status_panel(y)
|
||||||
self._draw_bottombar()
|
self._draw_bottombar()
|
||||||
|
|
||||||
|
def _clear_line(self, y: int):
|
||||||
|
"""清空指定行,防止残留内容。"""
|
||||||
|
if 0 <= y < self.height:
|
||||||
|
try:
|
||||||
|
self.stdscr.addstr(y, 0, " " * self.width)
|
||||||
|
except curses.error:
|
||||||
|
pass
|
||||||
|
|
||||||
def _draw_titlebar(self):
|
def _draw_titlebar(self):
|
||||||
h, w = self.height, self.width
|
h, w = self.height, self.width
|
||||||
title = " padsleep 配置工具 "
|
title = " padsleep 配置工具 "
|
||||||
@@ -262,6 +272,7 @@ class TuiApp:
|
|||||||
periods = (self.config or {}).get("screen_on_periods", [])
|
periods = (self.config or {}).get("screen_on_periods", [])
|
||||||
if not periods:
|
if not periods:
|
||||||
if y < h:
|
if y < h:
|
||||||
|
self._clear_line(y)
|
||||||
try:
|
try:
|
||||||
self.stdscr.addstr(y, 4, "(暂无时间段 — 按 A 新增)")
|
self.stdscr.addstr(y, 4, "(暂无时间段 — 按 A 新增)")
|
||||||
except curses.error:
|
except curses.error:
|
||||||
@@ -270,6 +281,7 @@ class TuiApp:
|
|||||||
else:
|
else:
|
||||||
# 表头
|
# 表头
|
||||||
if y < h:
|
if y < h:
|
||||||
|
self._clear_line(y)
|
||||||
try:
|
try:
|
||||||
self.stdscr.addstr(y, 4, " # │ 开始 │ 结束 │", curses.A_UNDERLINE)
|
self.stdscr.addstr(y, 4, " # │ 开始 │ 结束 │", curses.A_UNDERLINE)
|
||||||
except curses.error:
|
except curses.error:
|
||||||
@@ -278,6 +290,7 @@ class TuiApp:
|
|||||||
for i, p in enumerate(periods):
|
for i, p in enumerate(periods):
|
||||||
if y >= h:
|
if y >= h:
|
||||||
break
|
break
|
||||||
|
self._clear_line(y)
|
||||||
sel = "▸" if i == self.selected else " "
|
sel = "▸" if i == self.selected else " "
|
||||||
line = f"{sel}{i+1:>2} │ {p['start']} │ {p['end']} "
|
line = f"{sel}{i+1:>2} │ {p['start']} │ {p['end']} "
|
||||||
try:
|
try:
|
||||||
@@ -290,6 +303,7 @@ class TuiApp:
|
|||||||
y += 1
|
y += 1
|
||||||
|
|
||||||
if y < h:
|
if y < h:
|
||||||
|
self._clear_line(y)
|
||||||
try:
|
try:
|
||||||
self.stdscr.addstr(y, 4, "[A]新增 [E]编辑 [D]删除 ↑↓选择", curses.A_DIM)
|
self.stdscr.addstr(y, 4, "[A]新增 [E]编辑 [D]删除 ↑↓选择", curses.A_DIM)
|
||||||
except curses.error:
|
except curses.error:
|
||||||
@@ -303,38 +317,59 @@ class TuiApp:
|
|||||||
if self.status:
|
if self.status:
|
||||||
serial_cfg = self.status.get("device_serial", "")
|
serial_cfg = self.status.get("device_serial", "")
|
||||||
detected = self.status.get("device", "")
|
detected = self.status.get("device", "")
|
||||||
|
conn_type = self.status.get("connection_type", "none")
|
||||||
|
conn_icon = "USB" if conn_type == "usb" else "WiFi" if conn_type == "wireless" else "---"
|
||||||
|
|
||||||
if serial_cfg:
|
if serial_cfg:
|
||||||
dev_str = f"{serial_cfg} (已指定)"
|
dev_str = f"{serial_cfg} (已指定)"
|
||||||
elif detected and detected != "无":
|
elif detected and detected != "无":
|
||||||
dev_str = f"{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:
|
else:
|
||||||
try:
|
dev_str = detected or "未连接"
|
||||||
|
|
||||||
|
has_device = conn_type != "none" or (detected and detected != "无")
|
||||||
|
self._clear_line(y)
|
||||||
|
try:
|
||||||
|
label = f"当前设备: {dev_str} [{conn_icon}]"
|
||||||
|
if has_device:
|
||||||
|
self.stdscr.addstr(y, 4, label)
|
||||||
|
self.stdscr.addstr(y, 4 + len(label), " ✓", curses.color_pair(3))
|
||||||
|
else:
|
||||||
self.stdscr.addstr(y, 4, "当前设备: 未连接", curses.color_pair(2))
|
self.stdscr.addstr(y, 4, "当前设备: 未连接", curses.color_pair(2))
|
||||||
|
except curses.error:
|
||||||
|
pass
|
||||||
|
y += 1
|
||||||
|
y += 1
|
||||||
|
|
||||||
|
# 显示无线连接配置
|
||||||
|
wireless_host = self.status.get("wireless_host", "")
|
||||||
|
wireless_port = self.status.get("wireless_port", 5555)
|
||||||
|
if wireless_host:
|
||||||
|
self._clear_line(y)
|
||||||
|
try:
|
||||||
|
self.stdscr.addstr(y, 4, f"无线配置: {wireless_host}:{wireless_port}", curses.A_DIM)
|
||||||
except curses.error:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
y += 1
|
y += 1
|
||||||
|
|
||||||
if self.devices:
|
if self.devices:
|
||||||
for d in self.devices:
|
for d in self.devices:
|
||||||
if y >= h:
|
if y >= h:
|
||||||
break
|
break
|
||||||
|
self._clear_line(y)
|
||||||
icon = "✓" if d["status"] == "device" else "✗"
|
icon = "✓" if d["status"] == "device" else "✗"
|
||||||
|
conn_type = d.get("connection_type", "usb")
|
||||||
|
conn_icon = "USB" if conn_type == "usb" else "WiFi"
|
||||||
pair = 3 if d["status"] == "device" else 2
|
pair = 3 if d["status"] == "device" else 2
|
||||||
try:
|
try:
|
||||||
self.stdscr.addstr(y, 4, f"{icon} {d['serial']}", curses.color_pair(pair))
|
self.stdscr.addstr(y, 4, f"{icon} {d['serial']}", curses.color_pair(pair))
|
||||||
self.stdscr.addstr(f" ({d['status']})")
|
self.stdscr.addstr(f" [{conn_icon}]")
|
||||||
except curses.error:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
y += 1
|
y += 1
|
||||||
else:
|
else:
|
||||||
if y < h:
|
if y < h:
|
||||||
|
self._clear_line(y)
|
||||||
try:
|
try:
|
||||||
self.stdscr.addstr(y, 4, "(无设备 — 请连接平板)", curses.color_pair(2))
|
self.stdscr.addstr(y, 4, "(无设备 — 请连接平板)", curses.color_pair(2))
|
||||||
except curses.error:
|
except curses.error:
|
||||||
@@ -342,8 +377,9 @@ class TuiApp:
|
|||||||
y += 1
|
y += 1
|
||||||
|
|
||||||
if y < h:
|
if y < h:
|
||||||
|
self._clear_line(y)
|
||||||
try:
|
try:
|
||||||
self.stdscr.addstr(y, 4, "[S]选择设备 [R]刷新", curses.A_DIM)
|
self.stdscr.addstr(y, 4, "[S]选择设备 [W]无线连接 [R]刷新", curses.A_DIM)
|
||||||
except curses.error:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
return y + 1
|
return y + 1
|
||||||
@@ -365,6 +401,7 @@ class TuiApp:
|
|||||||
wf = s.get("wakefulness", "?")
|
wf = s.get("wakefulness", "?")
|
||||||
|
|
||||||
if y < h:
|
if y < h:
|
||||||
|
self._clear_line(y)
|
||||||
try:
|
try:
|
||||||
self.stdscr.addstr(y, 4, "屏幕: ")
|
self.stdscr.addstr(y, 4, "屏幕: ")
|
||||||
self.stdscr.addstr(f"{sc}", curses.color_pair(sc_color) | curses.A_BOLD)
|
self.stdscr.addstr(f"{sc}", curses.color_pair(sc_color) | curses.A_BOLD)
|
||||||
@@ -373,6 +410,7 @@ class TuiApp:
|
|||||||
pass
|
pass
|
||||||
y += 1
|
y += 1
|
||||||
if y < h:
|
if y < h:
|
||||||
|
self._clear_line(y)
|
||||||
try:
|
try:
|
||||||
self.stdscr.addstr(y, 4, "时段内: ")
|
self.stdscr.addstr(y, 4, "时段内: ")
|
||||||
self.stdscr.addstr(f"{wp}", curses.color_pair(wp_color) | curses.A_BOLD)
|
self.stdscr.addstr(f"{wp}", curses.color_pair(wp_color) | curses.A_BOLD)
|
||||||
@@ -381,9 +419,13 @@ class TuiApp:
|
|||||||
pass
|
pass
|
||||||
y += 1
|
y += 1
|
||||||
if y < h:
|
if y < h:
|
||||||
|
self._clear_line(y)
|
||||||
try:
|
try:
|
||||||
self.stdscr.addstr(y, 4, "ADB: ")
|
self.stdscr.addstr(y, 4, "ADB: ")
|
||||||
self.stdscr.addstr(f"{ar}", curses.color_pair(ar_color) | curses.A_BOLD)
|
self.stdscr.addstr(f"{ar}", curses.color_pair(ar_color) | curses.A_BOLD)
|
||||||
|
conn_type = s.get("connection_type", "none")
|
||||||
|
if conn_type != "none":
|
||||||
|
self.stdscr.addstr(f" ({conn_type})")
|
||||||
except curses.error:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
y += 1
|
y += 1
|
||||||
@@ -394,6 +436,7 @@ class TuiApp:
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if y < h:
|
if y < h:
|
||||||
|
self._clear_line(y)
|
||||||
try:
|
try:
|
||||||
self.stdscr.addstr(y, 4, "(未连接守护进程 — 按 T 重连)")
|
self.stdscr.addstr(y, 4, "(未连接守护进程 — 按 T 重连)")
|
||||||
except curses.error:
|
except curses.error:
|
||||||
@@ -420,12 +463,13 @@ class TuiApp:
|
|||||||
bar_y += 1
|
bar_y += 1
|
||||||
if bar_y >= h:
|
if bar_y >= h:
|
||||||
return
|
return
|
||||||
|
self._clear_line(bar_y)
|
||||||
msg = self.message
|
msg = self.message
|
||||||
if msg and time.time() - self.msg_time < 5:
|
if msg and time.time() - self.msg_time < 5:
|
||||||
pass # show message below separator
|
pass # show message below separator
|
||||||
else:
|
else:
|
||||||
if self.connected:
|
if self.connected:
|
||||||
msg = "已连接 | ↑↓/jk 选择 A新增 E编辑 D删除 S选设备 V日志 H帮助"
|
msg = "已连接 | ↑↓/jk 选择 A新增 E编辑 D删除 S选设备 W无线 V日志 H帮助"
|
||||||
else:
|
else:
|
||||||
msg = "未检测到守护进程 | T 尝试连接"
|
msg = "未检测到守护进程 | T 尝试连接"
|
||||||
try:
|
try:
|
||||||
@@ -605,6 +649,43 @@ class TuiApp:
|
|||||||
else:
|
else:
|
||||||
self.msg(f"选择失败: {r.get('error', '')}")
|
self.msg(f"选择失败: {r.get('error', '')}")
|
||||||
|
|
||||||
|
def _dlg_connect_wireless(self):
|
||||||
|
"""无线连接对话框。"""
|
||||||
|
# 获取当前无线配置作为默认值
|
||||||
|
wireless_host = ""
|
||||||
|
wireless_port = "5555"
|
||||||
|
if self.status:
|
||||||
|
wireless_host = self.status.get("wireless_host", "")
|
||||||
|
wireless_port = str(self.status.get("wireless_port", 5555))
|
||||||
|
|
||||||
|
# 输入 IP 地址
|
||||||
|
host = self._dlg_input("无线设备 IP 地址:", wireless_host)
|
||||||
|
if not host:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 输入端口
|
||||||
|
port_str = self._dlg_input("端口号:", wireless_port)
|
||||||
|
if not port_str:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
port = int(port_str)
|
||||||
|
if not (1 <= port <= 65535):
|
||||||
|
self.msg("端口范围错误 (1-65535)")
|
||||||
|
return
|
||||||
|
except ValueError:
|
||||||
|
self.msg("端口格式错误")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 发送连接请求
|
||||||
|
self.msg(f"正在连接 {host}:{port} ...")
|
||||||
|
r = self.client.send("connect_wireless", host=host, port=port)
|
||||||
|
if r.get("ok"):
|
||||||
|
self.msg(f"无线设备已连接: {host}:{port}")
|
||||||
|
self.refresh()
|
||||||
|
else:
|
||||||
|
self.msg(f"连接失败: {r.get('error', '未知错误')}")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _validate_time(tstr: str) -> bool:
|
def _validate_time(tstr: str) -> bool:
|
||||||
try:
|
try:
|
||||||
@@ -678,6 +759,7 @@ class TuiApp:
|
|||||||
" E 编辑选中时间段",
|
" E 编辑选中时间段",
|
||||||
" D 删除选中时间段",
|
" D 删除选中时间段",
|
||||||
" S 选择 ADB 设备",
|
" S 选择 ADB 设备",
|
||||||
|
" W 无线连接设备",
|
||||||
" R 刷新状态",
|
" R 刷新状态",
|
||||||
" L 重载守护进程配置",
|
" L 重载守护进程配置",
|
||||||
" V 查看审计日志",
|
" V 查看审计日志",
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
# padsleep_adb 依赖
|
# padsleep_adb 依赖
|
||||||
# 本程序仅使用 Python 标准库,无需额外安装第三方包。
|
adbutils>=2.12.0
|
||||||
|
|
||||||
# 系统依赖:adb (Android Debug Bridge)
|
# 系统依赖:adb (Android Debug Bridge)
|
||||||
# 安装: sudo apt-get install adb
|
# 安装: sudo apt-get install adb
|
||||||
#
|
#
|
||||||
|
|||||||
Reference in New Issue
Block a user