Metadata-Version: 2.4
Name: huawei-esm48100
Version: 0.1.0
Summary: Async protocol library for Huawei ESM-48100 batteries
Project-URL: Documentation, https://github.com/GImDX/huawei-esm48100/blob/main/PROTOCOL.md
Project-URL: Issues, https://github.com/GImDX/huawei-esm48100/issues
Project-URL: Repository, https://github.com/GImDX/huawei-esm48100
Author: Huawei ESM-48100 contributors
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Framework :: AsyncIO
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Home Automation
Requires-Python: >=3.12
Requires-Dist: serialx<2,>=1.8.2
Provides-Extra: test
Requires-Dist: pytest-asyncio>=0.25; extra == 'test'
Requires-Dist: pytest>=8.3; extra == 'test'
Requires-Dist: ruff>=0.12; extra == 'test'
Description-Content-Type: text/markdown

# huawei-esm48100

Huawei ESM-48100 电池的异步 Python 通信库，实时传输默认严格只读。

当前仓库已根据两台 ESM-48100B1 的实机抓包实现：

- Modbus RTU CRC16、读保持寄存器请求和响应解析；
- USB/本地串口传输（基于 `serialx`），按通信参数计算 Modbus RTU
  3.5 字符静默时间，并为 USB 转向保留至少 10 ms；
- TCP 透明串口服务器传输；
- 多个从站共享同一传输连接的异步客户端；
- 长时间空闲后的只读唤醒、CRC/超时重试和异常尾随数据清理；
- 电压、电流、SOC、SOH、统计量、告警和 1–24 节单体数据解码；
- 设置寄存器的只读回读；
- 华为电子标签 `0x41/0x06` 只读分块采集与 ASCII 档案解析；
- 支持扫描、连续读取、原始帧和完整性摘要的命令行工具。

> [!WARNING]
> 实时传输默认只允许功能码 `0x03`，以及严格匹配格式的只读电子标签
> `0x41/0x05/0x04` 元数据查询和 `0x41/0x06/0x04` 分块读取。`0x06`、
> `0x10` 和其他厂商私有功能默认拒绝发送。只有调用方显式启用不安全请求后，
> 库才允许五个经过抓包确认、带范围检查及写后读回的高级设置。不同型号或
> 固件仍需独立抓包验证。

完整的证据、寄存器表、私有电子标签帧和安全说明见
[PROTOCOL.md](PROTOCOL.md)。

## 开发安装

虚拟环境只用于开发和独立硬件测试，不能复制到另一台机器，也不参与
Home Assistant 生产部署。每台机器应从本机 Python 重新创建环境。

Windows PowerShell：

```powershell
.\scripts\setup-venv.ps1
.\.venv\Scripts\python.exe -m pytest
```

如果本机执行策略禁止直接运行 `.ps1`，可以只对这一次启动绕过策略：

```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass `
  -File ".\scripts\setup-venv.ps1"
```

自动发现失败时可指定 Python：

```powershell
.\scripts\setup-venv.ps1 `
  -Python "C:\Program Files\Python313\python.exe"
```

若已有环境来自另一台机器，脚本会把它移动到
`.venv-stale-<时间戳>` 后重建。只有明确使用 `-Recreate` 时才直接删除旧环境。

普通 Linux 开发环境：

```bash
bash scripts/setup-venv.sh
.venv/bin/python -m pytest
```

只安装运行依赖：

```powershell
.\scripts\setup-venv.ps1 -RuntimeOnly
```

```bash
bash scripts/setup-venv.sh --runtime-only
```

两个脚本都要求 Python 3.12 或更高版本，并在结束前验证包可以导入。

## Home Assistant 生产安装

HACS 只安装 `custom_components/huawei_esm48100`。Home Assistant 随后读取
集成的 `manifest.json`，自动安装固定版本的 `huawei-esm48100` 包。HA OS、
Supervised 和 Container 生产环境都不运行本仓库的 `setup-venv` 脚本。

因此正式 HACS 发布前，必须先把与 `manifest.json` 对应的协议库版本发布到
PyPI。

## CI 与 PyPI 发布

`.github/workflows/tests.yml` 在 Ubuntu 上测试 Python 3.12、3.13 和 3.14，
并构建 wheel/sdist 后执行 `twine check`。

`.github/workflows/publish.yml` 在 GitHub Release 发布时：

1. 检查 Release tag（允许 `v` 前缀）与 `pyproject.toml` 版本一致；
2. 构建并检查 wheel 和 source distribution；
3. 使用 PyPI Trusted Publishing 的短期 OIDC 凭据发布，不使用仓库 API
   Token。

首次发布前，需要在 PyPI 创建 Pending Trusted Publisher：

```text
PyPI project: huawei-esm48100
GitHub owner: 实际仓库所有者
GitHub repository: https://github.com/GImDX/huawei-esm48100
Workflow: publish.yml
Environment: pypi
```

同时在 GitHub 仓库创建名为 `pypi` 的 Environment，并建议为它启用人工审批。
之后将 `pyproject.toml` 版本更新为目标版本，创建相同版本的 Release tag，
例如 `v0.1.0`，即可触发发布。

## 读取原始寄存器

本地串口：

```bash
esm48100 read \
  --transport serial \
  --port /dev/serial/by-id/usb-... \
  --baudrate 9600 \
  --slave 0xd6 \
  --register 0x0000 \
  --count 7 \
  --raw
```

TCP 透明串口服务器：

```bash
esm48100 read \
  --transport tcp \
  --host 192.0.2.10 \
  --tcp-port 5020 \
  --slave 0xd6 \
  --register 0x0000 \
  --count 6
```

TCP 模式发送和接收的仍然是带 CRC 的 Modbus RTU 帧。它不是带 MBAP 头的
Modbus TCP。

串口首次打开默认等待 2 秒，然后用安全的 `0x0000 × 1` 读取唤醒设备。
`read` 单次响应超时默认 3 秒，初始恢复窗口默认 60 秒。连续测试示例：

```powershell
esm48100 read `
  --transport serial --port COM4 --baudrate 9600 --parity N `
  --slave 0xD6 --register 0x0000 --count 7 `
  --repeat 20 --interval 3 --raw
```

扫描华为上位机使用的默认地址集合：

```powershell
esm48100 scan --transport serial --port COM4 --raw
```

扫描不是对每个地址只读取一次，而是在 60 秒恢复窗口内轮询尚未响应的地址。
扫描的单次响应超时默认 0.5 秒，正常在线设备的实测响应为 45–90 ms；如透明
串口服务器延迟较高，可显式增大 `--timeout`。同一地址的重试轮次默认至少
间隔 3 秒；候选地址较多且一轮本身超过 3 秒时不会额外等待。

PowerShell 中需要手工等待总线空闲时，应使用：

```powershell
Start-Sleep -Seconds 600
```

`read` 每轮都会打印 `requested_count`、`received_count`、地址范围和连续性。
使用 `--json` 可生成一行一个完整结果的 JSON，避免依赖终端文本复制。

## 只读实机探针

仓库内的探针支持本地串口和透明 TCP 串口服务器。它保持同一个传输连接，
先静置，再测试安全唤醒并连续读取完整快照。探针不会启用危险请求，输出
JSONL 结果和逐帧调试日志。

本地串口：

```powershell
.\.venv\Scripts\python.exe .\scripts\hardware_probe.py `
  --transport serial `
  --port COM4 `
  --addresses 0xD6,0xD7 `
  --idle-seconds 600 `
  --rounds 20 `
  --interval 3
```

透明 TCP 串口服务器：

```powershell
.\.venv\Scripts\python.exe .\scripts\hardware_probe.py `
  --transport tcp `
  --host 192.0.2.10 `
  --tcp-port 1145 `
  --addresses 0xD6,0xD7 `
  --idle-seconds 0 `
  --rounds 10 `
  --interval 3
```

未指定 `--transport` 时仍默认使用 `serial`，因此旧的串口探针命令保持兼容。
TCP 模式下，波特率、校验位和停止位由串口服务器自身配置。

JSONL 是判断轮次和字段是否完整的依据；终端文本只用于观察进度，复制时缺行
不会被误判为协议解析缺失。

## 离线解析 Device Monitoring Studio 抓包

```powershell
esm48100 decode-capture C:\path\to\Data_view_writes.txt --direction request
esm48100 decode-capture C:\path\to\Data_view_reads.txt --direction response
```

每个输入记录输出一行 JSON，并列出 CRC 有效的 RTU 帧与无法识别的剩余字节。
此命令不打开串口。

## Python API

```python
from huawei_esm48100 import EsmClient
from huawei_esm48100.transports import TcpRtuTransport

transport = TcpRtuTransport("192.0.2.10", 5020)
client = EsmClient(transport, slave_address=0xD6)

snapshot = await client.read_snapshot()
print(snapshot.bus_voltage_v)
print(snapshot.pack_voltage_v)
print(snapshot.state_of_charge)
print(snapshot.cell_voltages_v)
await transport.close()
```

## 项目边界

- 一个 transport 代表一条物理或透明转发的 RS485 总线。
- 多个 `EsmClient` 可以共享同一个 transport，并由 transport 串行化请求。
- 一条 RS485 总线只支持一个主站；不支持同时运行华为上位机、串口助手或
  另一个轮询程序。传输层仍会丢弃完整的无关 RTU 帧，并在畸形帧后恢复边界，
  避免一次残留数据持续污染后续事务。
- 默认传输只允许功能码 `0x03`；HACS 只有在用户显式启用高级控制实体后才会
  打开危险请求开关。
- 高级设置只发送一次写请求，不会因响应异常盲目重发。标准 `0x06` 回显会被
  校验；若回显缺失或损坏，则恢复输入流并以目标寄存器读回作为最终确认。
- 电子标签只放行已验证的 `0x41/0x05/0x04` 元数据查询和
  `0x41/0x06/0x04` 分块读取请求，并读取索引 `0x0000–0x000A`；不会复现
  上位机的隐式时钟写入、结束帧或其他私有操作。
