米家智能插座实现笔记本电池 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# 守护要点(事件驱动,非轮询)
- 电量读
GetSystemPowerStatusP/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 扫码登录绕密码风控),账号密码不落他人手。
部署步骤
# 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 现代待机的两套自动休眠触发器。
守护必须在桌面会话运行(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 现代待机的两套自动休眠触发器。初始若从 100% 满电开始,断充后靠日常放电 2-3 天自然收敛到 80 停靠区间。