Metadata-Version: 2.5
Name: ydsx-dp-emulator
Version: 0.2.0
Summary: 远大视讯 DP 锁屏宝的 CH341/CHB086 EDID 控制 SDK
Project-URL: Homepage, https://gitee.com/sannmizu/ydsx-dp-emulator-sdk
Project-URL: Repository, https://gitee.com/sannmizu/ydsx-dp-emulator-sdk
Project-URL: Issues, https://gitee.com/sannmizu/ydsx-dp-emulator-sdk/issues
Author: sannmizu
License-Expression: MIT
License-File: LICENSE
Keywords: CH341,CHB086,DisplayPort,EDID,SDK
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# 远大视讯 DP 锁屏宝 Python SDK

`ydsx-dp-emulator` 是面向 Windows Python 项目的 CH341/CHB086 EDID 控制 SDK。它通过 WCH `CH341DLL.DLL` / `CH341DLLA64.DLL` 直接与 DP 锁屏器通信，不依赖自动操作原厂 GUI。

- PyPI 包名：`ydsx-dp-emulator`
- Python 导入名：`dp_edid`
- 命令行：`dp-edid` 或 `ydsx-dp-emulator`
- 当前版本：`0.2.0`

> 写入会改变设备保存的 EDID。请先读取并保存备份；当前 `WV4:` 写入状态机已通过离线测试和逆向交叉验证，但尚未完成真实设备首次写入验证。

## 功能

- 使用 `DpEdidDevice` 统一管理设备生命周期；
- 自动选择与 Python 位数匹配的 CH341 DLL；
- 探测 CH341 芯片与 CHB086 固件版本；
- 读取设备邮箱中的 256 字节 EDID；
- 校验 EDID 头、扩展块数量和块校验和；
- 按 16 包、每包 16 字节写入 EDID；
- 写入后默认重新读取并逐字节验证；
- 解析二进制 `.bin` 和十六进制文本 EDID；
- 提供可脚本化的 CLI 和进度回调。

## 安装

使用 uv：

```powershell
uv add ydsx-dp-emulator
```

或使用 pip：

```powershell
python -m pip install ydsx-dp-emulator
```

运行环境：

- Windows 10/11；
- Python 3.10 或更高版本；
- 已安装 WCH CH341 并口/I2C 驱动；
- 锁屏器已通过 USB 连接，并被 CH341 驱动正确识别。

SDK 默认查找以下 DLL：

- 64 位 Python：`C:\Windows\System32\CH341DLLA64.DLL`；
- 32 位 Python：`C:\Windows\SysWOW64\CH341DLL.DLL`。

Python 进程与 DLL 位数必须一致。DLL 位于其他目录时，可通过 `dll_path` 显式指定。

## 快速开始

### 探测并备份 EDID

```python
from pathlib import Path

from dp_edid import DpEdidDevice, inspect_edid

with DpEdidDevice(index=0) as device:
    device_info = device.probe()
    edid = device.read_edid()

Path("device-edid-backup.bin").write_bytes(edid)
edid_info = inspect_edid(edid)

print(f"CH341：0x{device_info.ch341_version:08X}")
print(f"CHB086：{device_info.firmware_hex}")
print(f"EDID 长度：{edid_info.length}")
```

完整示例见 [examples/read_device.py](https://gitee.com/sannmizu/ydsx-dp-emulator-sdk/blob/master/examples/read_device.py)。

### 校验并写入 EDID

```python
from dp_edid import DpEdidDevice, load_edid, validate_edid

target = load_edid("target-edid.bin")
validate_edid(target)


def show_progress(stage: str, done: int, total: int) -> None:
    print(f"{stage}: {done}/{total}")


with DpEdidDevice(index=0) as device:
    result = device.write_edid(target, progress=show_progress)

print(result.sha256)
print(f"写后验证：{result.verified}")
```

`write_edid()` 默认严格校验目标数据，执行 `WV4:` 写入，再通过 `RV4:` 读回并逐字节比较。读回不一致时抛出 `EdidVerificationError`，异常中包含首个差异位置及双方 SHA-256。

## SDK API

### `DpEdidDevice`

| 方法 | 返回值 | 用途 |
| --- | --- | --- |
| `open()` | `DpEdidDevice` | 打开设备；通常使用 `with` 代替 |
| `close()` | `None` | 关闭设备，可重复调用 |
| `probe()` | `DeviceInfo` | 读取 DLL、CH341 和 CHB086 版本信息 |
| `read_edid(strict=False, progress=None)` | `bytes` | 读取 256 字节 EDID |
| `write_edid(edid, validate=True, verify=True, progress=None)` | `WriteResult` | 写入并默认读回验证 |

构造参数：

| 参数 | 默认值 | 说明 |
| --- | --- | --- |
| `index` | `0` | CH341 设备索引 |
| `dll_path` | `None` | 自定义 DLL 路径；默认自动查找 |
| `stream_mode` | `1` | WCH I2C 模式，`1` 对应 100 kHz |
| `timing` | `None` | 自定义 `ProtocolTiming` |

进度回调签名为 `callback(stage, done, total)`，其中 `stage` 为 `read`、`write` 或 `verify`。

### EDID 工具函数

- `load_edid(path)`：加载二进制或十六进制文本；
- `parse_text_edid(text)`：解析字符串形式的十六进制 EDID；
- `inspect_edid(data)`：生成宽松摘要；
- `validate_edid(data)`：执行适用于本设备 256 字节容量的严格校验。

所有可预期的 SDK 异常均继承自 `DPEdidError`：

| 异常 | 含义 |
| --- | --- |
| `Ch341LibraryError` | DLL 不存在、无法加载或位数不匹配 |
| `Ch341DeviceError` | 设备无法打开或 SDK 尚未打开 |
| `I2CTransferError` | `CH341StreamI2C` 事务失败 |
| `ProtocolTimeoutError` | CHB086 状态寄存器轮询超时 |
| `EdidFormatError` | EDID 文件或结构非法 |
| `EdidVerificationError` | 写入后的读回值与目标不一致 |

## 命令行

CLI 与 SDK 使用同一高层实现：

```powershell
dp-edid probe
dp-edid read .\device-edid-backup.bin
dp-edid validate .\target-edid.bin
dp-edid write .\target-edid.bin --dry-run
dp-edid write .\target-edid.bin
```

`--dry-run` 不加载 DLL，也不访问设备。`--force-invalid-edid` 只跳过 EDID 结构校验，不跳过 256 字节长度限制和写后验证。

## 非标准 256 字节数据

只有明确了解设备数据格式时，才应关闭严格校验：

```python
with DpEdidDevice() as device:
    result = device.write_edid(
        nonstandard_256_bytes,
        validate=False,
        verify=True,
    )
```

当前实测设备读出的 base block 声明了两个扩展块，但协议只返回一个扩展块。该备份不代表声明的完整三块 EDID，回写前必须确认风险。

## 项目结构

```text
src/dp_edid/     可发布的 SDK 源码
tests/           不依赖真实硬件的自动测试
examples/        最小集成示例
docs/            协议与架构说明
references/      驱动、原厂工具和逆向参考资料（不发布到 PyPI）
```

业务代码应优先从 `dp_edid` 包根目录导入公共 API。`ch341.py` 与 `protocol.py` 属于硬件和协议底层，主要用于高级调试。

## 开发与构建

本项目使用 uv 管理 Python 环境：

```powershell
uv sync
uv run python -m unittest discover -s tests -v
uv build
```

wheel 和 sdist 输出到 `dist/`。构建白名单会排除 `references/`、任务进度文件和其他非 SDK 资产。

## 验证状态

- `probe` 与 `RV4:` 读取已在真实 CH341A/CHB086 设备验证；
- SDK、EDID 校验、协议状态机和 CLI 已通过离线自动测试；
- `WV4:` 写入流程已由参考源码和 EXE 反汇编交叉确认，尚未完成真实设备首次写入；
- `EDID Study` 的额外下游切换动作仍待验证。

协议细节见 [协议逆向说明](https://gitee.com/sannmizu/ydsx-dp-emulator-sdk/blob/master/docs/%E5%8D%8F%E8%AE%AE%E9%80%86%E5%90%91%E8%AF%B4%E6%98%8E.md)，架构迁移见 [SDK 升级说明](https://gitee.com/sannmizu/ydsx-dp-emulator-sdk/blob/master/docs/SDK%E5%8D%87%E7%BA%A7%E8%AF%B4%E6%98%8E.md)。

## License

本项目自行编写的 SDK 源码和文档采用 [MIT License](https://gitee.com/sannmizu/ydsx-dp-emulator-sdk/blob/master/LICENSE)。`references/` 中的第三方文件不在该授权范围内，也不会随 PyPI 包分发。
