# 米家智能插座实现笔记本电池 80% 保持充电

长期插电使用的笔记本，电池保护最优解是官方充电上限（充到 60-80% 后电源直供、电池不循环）。但部分机型（如同方系机械革命）的 EC 充电限制被固件阉割——控制中心三档全无效，寄存器机制存在但 Windows 侧无合法写入路径。物理层补法：**智能插座自动断充**。

场景：工作日每天用约 3 小时（插电、从不合盖），其余时间系统自动睡眠。目标：电池长期停在 80%，不碰 100%、几乎零循环。

## 为什么不是"80% 断 / 20% 充"的深放循环

用户直觉是充到 80 断、掉到 20 再充。这个规则在两个层面不成立：

- 睡眠期几乎不耗电 → 20% 下限永不触发，某次用低后电池会永远停着没人补
- 任何"断电后机器继续跑"的场景，日均等效满循环 = 24h × 平均功耗 ÷ 电池容量，**与阈值区间无关**——区间只决定周期长短。24/7 满载下电池几个月报废

正确形态是**保持型充电**（联想 conservation mode 同款行为）：电量 ≥80% 断插座、≤75% 开插座充回、区间内不动（5% 迟滞防抖）、系统睡眠事件立即断（防睡眠中偷偷充到 100%）。效果 = 电池 75-80 停靠，几乎零循环。

## 架构

三件套，控制闭环靠笔记本自己：

- **米家智能插座3**（真实 model = `cuco.plug.v3` ，MIoT 设备），适配器插它上面
- **python 桥**：python-miio 局域网控制插座，暴露本地 HTTP `GET /on /off /state` （127.0.0.1:8765）
- **C# 托盘守护**（Windows 原生，事件驱动）：监听电源事件 → 按电量决策 → 调桥断/通插座

关键：**验证不依赖插座状态回读**——断充后 20 秒内查笔记本自己 `ACLineStatus` 是否翻转（断=0/通=1），3 次失败才告警。天然兜底指令丢失。

## C# 守护要点（事件驱动，非轮询）

- 电量读 `GetSystemPowerStatus` P/Invoke（`ACLineStatus` / `BatteryLifePercent` ，255=未知），别用 WMI
- 事件用 `RegisterPowerSettingNotification` 注册两个 GUID：`GUID_BATTERY_PERCENTAGE_REMAINING` （每变 1% 触发）和 `GUID_ACDC_POWER_SOURCE` ；收 `WM_POWERBROADCAST`

```
case PBT_APMSUSPEND(0x0004):        // 系统将睡眠
    plug OFF;                        // 防睡眠中充到 100%
case PBT_APMRESUMEAUTOMATIC(0x12):  // 唤醒
    延迟 5s 后重评估;
case PBT_POWERSETTINGCHANGE(0x8013):
    解 POWERBROADCAST_SETTING 判断哪个 GUID → Evaluate();
// Evaluate: pct>=80 && AC 在 → OFF；pct<=75 → ON；75-80 不动
```

同一方向指令 30 秒防抖；托盘图标 PNG-in-ICO 真 alpha（`Icon.FromHandle` 黑底勿用），替换前 Dispose 旧 Icon（每电量事件重建，否则 GDI 泄漏）。

## python 桥要点

python-miio 0.5.12 对 cuco.plug.v3 无现成 plugin 类，走通用 MIoT：mapping `{'power': {'siid': 2, 'piid': 1}}` 。两个 API 坑：

- 读状态用 `get_properties_for_mapping()` ；`get_properties(['power'])` 会 -9999 undefined command
- `MiotDevice` 的 mapping 是 keyword-only 参数，位置传参静默失效

socket.json 存 `{ip, token}` 。token 提取：micloud 老库已被小米风控拒（403），用 Xiaomi Cloud Tokens Extractor（GitHub 活跃维护，支持 QR 扫码登录绕密码风控），账号密码不落他人手。

## 部署步骤

```bash
# Win11（管理员 CMD / PowerShell）
# 1. 项目环境（uv 独立 python，不污染系统）
mkdir C:\Users\<user>\Downloads\battery-socket && cd /d %CD%\battery-socket
uv python install 3.12 && uv venv --python 3.12

# 2. python-miio：不能直接装！netifaces 无 cp312 wheel 需 MSVC，绕开（它只被
#    discovery 模块用，直连 IP 不需要）：
uv pip install --python .venv\Scripts\python.exe python-miio --no-deps
uv pip install --python .venv\Scripts\python.exe PyYAML appdirs attrs click construct croniter cryptography defusedxml micloud pytz tqdm zeroconf android_backup

# 3. 提取插座 token（用户桌面跑 token_extractor.exe，QR 登录，地区 cn）
#    取 token+IP 写 socket.json

# 4. 编译守护（系统自带 csc，C#5 语法）
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /nologo /target:winexe /codepage:65001 /out:guard.exe /r:System.dll /r:System.Core.dll /r:System.Drawing.dll /r:System.Windows.Forms.dll guard.cs

# 5. 注册开机自启
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v BatterySocketGuard /t REG_SZ /d "\"C:\path\guard.exe\"" /f

# 6. 电源配套——S0 待机策略（2026-09-13 修订：DC 加休眠兜底）
powercfg /h on
# 定时休眠：AC 0（插电不打扰）/ DC 7200（电池待机满 2h → S4）
# 2026-09-13 掉电事件后加的兜底，见文末说明
powercfg /setacvalueindex SCHEME_CURRENT SUB_SLEEP 9d7815a6-7ee4-497e-8888-515a05f02364 0
powercfg /setdcvalueindex SCHEME_CURRENT SUB_SLEEP 9d7815a6-7ee4-497e-8888-515a05f02364 7200
# 待机电池预算放宽到 40%（默认 5%，几小时待机就耗穿 → 退出待机降级）
powercfg /setdcvalueindex SCHEME_CURRENT SUB_PRESENCE STANDBYBUDGETPERCENT 40
powercfg /setactive SCHEME_CURRENT
```

两个触发器的作用不同，别只配一个：`HIBERNATEIDLE` 是定时休眠（本次加的兜底）；`SUB_PRESENCE` 的待机电池预算默认 5%、几小时待机就耗穿，但**它超限只做降级（受限待机），不保证转休眠**。完整机制、2026-09-13 的 78% → 0 掉电实测与取证清单见 [[windows-modern-standby-adaptive-hibernate|Windows 现代待机的两套自动休眠触发器]]。

守护必须在**桌面会话**运行（SSH 会话无托盘，NotifyIcon 会崩）。手动开关插座、开机自启勾选都在托盘右键菜单。

## 行为验证

运行日志（guard.log）应呈现闭环：

```
evaluate: pct=100 ac=1 → send off → evaluate: pct=100 ac=0 → verify OK: ac=0
```

睡眠中不会补电（guard 挂起），唤醒后自动评估——电量低于 75% 才补，这是特性不是缺陷。

但**"睡眠中掉电"这件事 guard 管不了**：断充后如果系统进了问题待机（没真正进低功耗），guard 被 DAM 冻结、Windows 的预算/临界休眠又都不执行，电池会一路放到 0（2026-09-13 实测，78% → 0 用了 5.3 小时）。真正的兜底是电源策略侧的 DC 定时休眠，见 [[windows-modern-standby-adaptive-hibernate|Windows 现代待机的两套自动休眠触发器]]。初始若从 100% 满电开始，断充后靠日常放电 2-3 天自然收敛到 80 停靠区间。

## 参考

- [python-miio](https://github.com/rytilahti/python-miio)
- [Xiaomi Cloud Tokens Extractor](https://github.com/PiotrMachowski/Xiaomi-cloud-tokens-extractor)
- 相关：[[laptop-maintenance|笔记本保养]]


相关：[[nas-enshitification|nas-enshitification]]
