Skip to content

About

Windows 一键远程开关机工具,基于 Home Assistant + WOL + MQTT,纯标准库无第三方依赖

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

RemotePower · 一个按钮开关家里那台电脑

没有公网 IP、不改路由器端口映射、不装付费内网穿透客户端付费版—— 用一台 24 小时开机的 NAS 上的 Home Assistant 当中转站,实现异地一键开机 + 关机。

Windows 小工具,单 exe,纯标准库,配置全在界面里改。


一、这个方案解决什么问题

场景很常见:家里主力机放在那,人在公司/外地,想用远程桌面连回去。

  • 关机状态连不上,得先叫醒它 —— WOL 魔术包只能在同一个广播域里发,隔着公网发不出去。
  • 用完忘了关,机器空转一整天 —— 需要一条能把"关机"送进去的通道。

两件事都卡在同一处:你人不在那个局域网里。

二、思路

把一台 7×24 在线的设备(NAS / 软路由 / 树莓派)放到那个局域网里,让它当"驻场代理":

   你(异地)                                         家里局域网
┌──────────┐   HTTPS / MQTT   ┌──────────────┐  WOL 广播  ┌──────────┐
│ 笔记本    │ ───────────────► │  NAS         │ ─────────► │ 目标电脑  │
│ 手机      │                  │  Home Assist │            │          │
└──────────┘ ◄─────────────── └──────────────┘ ◄───────── └──────────┘
              状态回传                        MQTT 关机指令
  • 开机:让 HA 在局域网内广播魔术包。它和目标机在同一个广播域,包发得出去。
  • 关机:目标机上跑一个 MQTT 客户端(推荐 HASS.Agent),订阅 HA 下发的命令。
  • 状态:目标机在线时自己上报,离线时消息自动过期,HA 拿到的就是真实状态。

HA 的网页/API 怎么从外网访问,是另一件事——IPv6 + DDNS、或者任意虚拟组网工具都行,本文不展开。

三、为什么不用别的方案

方案 问题
路由器端口映射 + 公网 IPv4 多数家宽没有公网 IPv4 了
IPv6 直连目标机 目标机休眠/关机时没有网络栈,连不上;而且开机这一步本来就要靠局域网
智能插座断电再上电 粗暴,伤硬盘;且断电后 BIOS 需要设"来电自启",很多主板不支持
向日葵/TeamViewer 的开机棒 要额外硬件,且绑定他们家生态
各类付费内网穿透 能用,但开机指令最终还是要落到局域网里的一台机器上

核心结论:开机指令必须由局域网内的设备发出,所以那台 24h 在线的设备是绕不开的,既然如此就让它顺便把关机也管了。

四、准备工作

4.1 目标机(Windows)

  1. 网卡开启 WOL 设备管理器 → 网络适配器 → 属性 → 高级 → Wake on Magic Packet / 唤醒魔数包 = 启用 同一窗口 → 电源管理 → 勾选「允许此设备唤醒计算机」

  2. BIOS 关闭 ErP/EuP 这个选项打开后,关机时网卡彻底断电,WOL 必然失效。

  3. 关掉 Windows 快速启动(关键,见踩坑第 3 条) 控制面板 → 电源选项 → 选择电源按钮的功能 → 更改当前不可用的设置 → 取消勾选「启用快速启动」

  4. 装 HASS.Agent 并启用 Shutdown 命令 它会在 HA 里生成一个 button.xxx_shutdown 实体,这就是我们的关机入口。 HASS.Agent 走 MQTT 与 HA 通信,机器关机后它自己会掉线,正好可以作为"已关机"的佐证。

4.2 NAS / Home Assistant

需要在 configuration.yaml 里加几段配置(见第五节),改完重启一次 HA。

五、HA 侧配置

5.1 WOL 开关与按钮

# configuration.yaml
wake_on_lan:

switch:
  - platform: wake_on_lan
    name: "电脑电源"
    mac: "AA:BB:CC:DD:EE:FF"     # 目标机网卡 MAC
    host: 192.168.1.100          # 目标机局域网 IP

重启后自动出现 button.wake_on_lan_xx_xx_...(按下即发魔术包)和 switch.pc。 建议用 button,语义清晰,也不会像 switch 那样出现"状态和实际不符"的困惑—— wake_on_lan 的 switch 是 assumed_state,它的 on/off 只代表"我按过",不代表机器真实状态。

5.2 状态传感器

两条路,选一条:

方案 A:HA ping 集成(HA 与目标机同网段时最简单)

binary_sensor:
  - platform: ping
    host: 192.168.1.100
    name: "电脑在线"
    count: 2
    scan_interval: 30

方案 B:MQTT 遗嘱消息(跨网段、或 ping 被防火墙挡时用)

目标机上的客户端连接 broker 时设置 LWT:

topic:   pc/status
payload: offline      # 遗嘱:客户端异常掉线时 broker 自动发出

连上后主动 publish online。HA 侧:

mqtt:
  binary_sensor:
    - name: "电脑在线"
      state_topic: "pc/status"
      payload_on: "online"
      payload_off: "offline"
      device_class: connectivity

如果你用了 HASS.Agent,它自带一批状态传感器,机器关机后这些实体统一变 unavailable,直接拿其中一个(比如电源状态)当判据也行。

5.3 总开关 + 两个自动化

用一个辅助布尔把"开/关"收敛成一个动作,后面写脚本、接语音助手、做卡片都方便。

input_boolean:
  pc_power:
    name: 电脑
    icon: mdi:desktop-tower

automation:
  # 翻上去 → 开机
  - alias: "电脑开机"
    id: pc_wake
    triggers:
      - trigger: state
        entity_id: input_boolean.pc_power
        from: "off"
        to: "on"
    actions:
      - action: button.press
        target:
          entity_id: button.pc_wake_on_lan    # 改成你自己的实体名
    mode: single

  # 翻下来 → 关机
  - alias: "电脑关机"
    id: pc_shutdown
    triggers:
      - trigger: state
        entity_id: input_boolean.pc_power
        from: "on"
        to: "off"
    actions:
      - action: button.press
        target:
          entity_id: button.pc_shutdown       # HASS.Agent 的关机按钮
    mode: single

  # 状态同步:机器起来了,把总开关扶正
  - alias: "电脑开机检测"
    id: pc_detect_on
    triggers:
      - trigger: state
        entity_id: binary_sensor.pc_online
        from: ["unavailable", "unknown", "off"]
        to: "on"
    actions:
      - action: input_boolean.turn_on
        target:
          entity_id: input_boolean.pc_power
    mode: single

  # 状态同步:机器关了,把总开关放平
  - alias: "电脑关机检测"
    id: pc_detect_off
    triggers:
      - trigger: state
        entity_id: binary_sensor.pc_online
        from: "on"
        to: ["unavailable", "unknown", "off"]
    actions:
      - action: input_boolean.turn_off
        target:
          entity_id: input_boolean.pc_power
    mode: single

注意:from / to 的写法随 HA 版本有变化。较新版本用 triggers / actions, 老版本用 trigger / action,两者目前都兼容,混用会报错,统一即可。

5.4 拿长期访问令牌

HA 网页 → 左下角头像 → 安全 → 长期访问令牌 → 创建,复制那串 eyJ...。 脚本需要它来调 REST API。

六、跑起来

方式一:直接用 exe

去 Releases 下载 RemotePower.exe,和 config.ini 放同一目录,双击运行。

方式二:从源码跑

git clone https://github.com/Z2549/RemotePower.git
cd RemotePower
pip install pyinstaller          # 只有打包时才需要
python main.py                   # 直接运行界面
bash build.sh                    # 打成单文件 exe

需要 Python 3.10+,且必须带 tkinter(Windows 官方安装包默认带; 某些精简版/Store 版没有,会报 No module named 'tkinter')。

配置

点界面「设置」,填这几项就够:

字段 填什么
目标机 IP 局域网 IP 或虚拟组网 IP
目标机 MAC 网卡物理地址,WOL 用
HA 地址 http://192.168.1.10:8123
HA 令牌 上一步拿到的 eyJ...
状态实体 binary_sensor.pc_online
开关实体 input_boolean.pc_power
开机按钮 button.pc_wake_on_lan(可选,留空则用 send_magic_packet)
关机按钮 button.pc_shutdown(可选)

填完点「自检」,全绿就可以用了。

[OK ] 目标机地址                192.168.1.100
[OK ] 目标机 MAC               AA:BB:CC:DD:EE:FF
[OK ] HA 连通 + 令牌           http://192.168.1.10:8123 -> HTTP 200,实体 141 个
[OK ] wake_on_lan 服务       可用
[OK ] mqtt.publish 服务      可用
[OK ] 状态实体                 binary_sensor.pc_online = off → 关机
[OK ] 总开关                  input_boolean.pc_power = off
[OK ] 目标机当前状态              离线

使用

界面点按钮,或者命令行:

on.bat          :: 开机
off.bat         :: 关机
status.bat      :: 查状态(在线退出码 0,离线 1,可被其它脚本调用)
check.bat       :: 自检

也可以直接传参给 exe:RemotePower.exe on|off|toggle|status|check|discover。

发完指令不是就完事了——脚本会轮询状态实体,确认机器真的起来了 / 真的关掉了才返回成功。

七、踩坑记录

这一节是本文最值钱的部分,都是实测踩出来的。

坑 1:虚拟组网网卡的假响应,让 ping 彻底不可信

如果你的机器通过 ZeroTier / 节点小宝 / 蒲公英 这类虚拟组网访问, 别用 ping 判断目标机是否开机。

实测:目标机明明已经关机,

ping 192.168.1.100     →  TTL=64(显示通!)
端口 22   →  CONNECT 成功
端口 445  →  CONNECT 成功
端口 3389 →  CONNECT 成功

原因是虚拟网卡驱动在本地就把握手回给你了,包根本没到对端。 后果很直接:关机动作用 ping 判断"它已经离线了,不用发指令",于是直接跳过, 机器永远关不掉。

正确做法:状态一律以 HA 实体为准(HA 在真实局域网里,看到的才是真的)。 本工具里 power_entity 就是干这个的,ping 只作最后的兜底。

怎么验证自己是不是中招了:在目标机确定关机的状态下,从你这台机器 ping 它一次、telnet IP 3389 一次。能通就说明你的判定逻辑必须换。

坑 2:HA 的 WOL 服务名不是 turn_on

网上不少老教程写 wake_on_lan.turn_on,那是早期版本。现在叫:

service: wake_on_lan.send_magic_packet
data:
  mac: "AA:BB:CC:DD:EE:FF"

调用前可以在 开发者工具 → 服务 里搜一下,确认服务名和参数。

坑 3:Windows 快速启动会让 WOL 失效

Win10/11 默认开启"快速启动",它的本质是关机时进休眠——网卡跟着断电, 魔术包收不到。表现是:关机前一切正常,关一次之后就再也唤不醒了。

关掉它:控制面板 → 电源选项 → 选择电源按钮的功能 → 更改当前不可用的设置 → 取消勾选。

顺带一个副作用:快速启动状态下,机器"关机"了但网卡还有电、系统半休眠, 状态判定也会变得很暧昧。

坑 4:HA 跑在 Docker 里必须是 host 网络

桥接网络下,容器发出的广播包出不了 docker0 网桥,send_magic_packet 会"成功" 但机器纹丝不动。

# docker-compose.yml
services:
  homeassistant:
    network_mode: host

坑 5:关机按钮是单点

用 HASS.Agent 的按钮作关机入口,意味着它挂了你就关不了机。 同理,那个 button.xxx_shutdown 在目标机关机后必然是 unavailable—— 这既是关机成功的证据,也是下次开机时"按钮不可用"的隐患。

判断方法:开机后看这个按钮有没有从 unavailable 变成有时间戳。变了说明 Agent 上线了。

八、排障清单

现象 先查什么
按了开机,机器不动 看 HA 的日志里 button.press 有没有触发;再确认 HA 是否 host 网络;最后查 BIOS 的 ErP
开机成功但状态一直显示离线 power_entity 填错,或者该实体没有及时更新;用 RemotePower.exe discover 找候选
关机没反应 目标机上 HASS.Agent 是否在跑;button.xxx_shutdown 是不是 unavailable
关机后马上又自己开了 状态同步自动化互相打架,检查 from/to 条件
自检报 mqtt/publish not found HA 没装 MQTT 集成,或者 broker 没连上
提示 No module named 'tkinter' 换用 python.org 的官方安装包(Store 版没有 tkinter)

九、目录结构

RemotePower.exe      单文件可执行(Windows)
RemotePower.bat      启动界面
on.bat / off.bat / status.bat / check.bat
main.py              入口:有参数走 CLI,无参数开 GUI
core.py              配置 / WOL / HA REST / 极简 MQTT 客户端 / 自检
gui.py               tkinter 界面(深色)
config.example.ini   配置模板
build.sh / build.bat 打包脚本

十、实现说明

  • 零第三方依赖:HA 用 urllib 走 REST,MQTT 手写 3.1.1 的 CONNECT/PUBLISH(QoS0), 不需要 requests、paho-mqtt。好处是打包体积小、不会有依赖冲突。
  • 通道降级:开机 6 条、关机 5 条,前一条失败自动试下一条,全失败才报错。
  • 确认式操作:发出指令后轮询 power_entity,确认状态真的翻转了才算成功, 不是"消息队列里塞进去就完事"。
  • 配置跟 exe 走:config.ini 放在 exe 同目录,扔 U 盘换机器也能用。

十一、扩展:接入小爱同学

配好之后可以让小爱同学接管:

「小爱同学,打开电脑」

通过巴法云把 HA 实体桥接到米家,再交给小爱语音控制。 完整步骤(含版本坑和设备类型对照表)见 → docs/小爱同学接入.md

License

MIT

About

Windows 一键远程开关机工具,基于 Home Assistant + WOL + MQTT,纯标准库无第三方依赖

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages