自己的笔记本被自己改成了win server服务器,外加来电自启+小爱同学控制开关机,可以说很好了,但是笔记本却没有断电自动关机这个东西,原本想把电池扣了,换上usp的,但是有一点成本在的,果断放弃。

后来更改电源计划,99%的时候进行关机,但是盖上盖子,断开远程的时候,睡眠状态了就,不会进行关机操作。索性写了这个python代码

import sys
import win32api
import win32gui
import win32con
import pythoncom
import psutil
import threading
import time
import subprocess

SHUTDOWN_DELAY_SECONDS = 60

class PowerListenerWindow:
    def __init__(self):
        self.is_plugged_in = True # 初始假设为连接电源
        self.shutdown_timer = None
        self.last_unplugged_time = None

        wc = win32gui.WNDCLASS()
        wc.lpfnWndProc = self.wnd_proc
        wc.lpszClassName = 'MyPowerListenerClass'
        wc.hInstance = win32api.GetModuleHandle(None)
        class_atom = win32gui.RegisterClass(wc)

        # Create a hidden window
        self.hwnd = win32gui.CreateWindow(
            class_atom,
            'PowerListener',
            0, 0, 0, 0, 0,
            0, 0, wc.hInstance, None
        )

    def wnd_proc(self, hwnd, msg, wparam, lparam):
        if msg == win32con.WM_POWERBROADCAST:
            if wparam == win32con.PBT_APMPOWERSTATUSCHANGE:
                # print("[WM_POWERBROADCAST] 电源状态可能已更改 (PBT_APMPOWERSTATUSCHANGE)") # 简化日志
                self.check_and_print_status() # 调用更新后的检查函数

            elif wparam == win32con.PBT_ACPIRESUMEAUTOMATIC or wparam == win32con.PBT_ACPIRESUMESUSPEND:
                # print(f"[WM_POWERBROADCAST] 系统从睡眠/休眠恢复 (wParam={wparam})") # 简化日志
                pass # 可根据需要添加恢复逻辑

            elif wparam == win32con.PBT_APMSUSPEND:
                # print("[WM_POWERBROADCAST] 系统即将进入睡眠/休眠 (PBT_APMSUSPEND)") # 简化日志
                pass # 可根据需要添加休眠前逻辑

        return win32gui.DefWindowProc(hwnd, msg, wparam, lparam)

    def cancel_pending_shutdown(self):
        """取消任何待定的关机操作"""
        if self.shutdown_timer and self.shutdown_timer.is_alive():
             self.shutdown_timer.cancel()
             self.shutdown_timer = None

    def schedule_shutdown(self):
        """在指定延迟后执行关机"""
        print(f"警告:设备未充电,将在 {SHUTDOWN_DELAY_SECONDS} 秒后关机!")
        self.execute_shutdown()

    def execute_shutdown(self):
        """实际执行关机操作"""
        # 再次确认是否仍然未充电,避免在检查间隙插上了电源
        try:
            battery_check = psutil.sensors_battery()
            if battery_check and not battery_check.power_plugged:
                 print("--- 执行关机 ---")
                 # 使用 subprocess 调用 Windows 关机命令
                 # /s 关机, /t 5 延迟5秒, /c 添加消息
                 subprocess.run(["shutdown", "/s", "/t", "5", "/c", "Battery low and not charging. Shutting down in 5 seconds..."], check=True)
                 # 等待一下,让关机命令生效
                 time.sleep(10)
                 sys.exit(0) # 正常退出监听器
            else:
                 print("--- 电源状态已恢复,取消关机 ---")
        except psutil.Error as e:
             print(f"查询电源状态失败: {e}")
             # 查询失败,出于安全考虑,可以选择不关机或记录日志
        except subprocess.CalledProcessError as e:
             print(f"执行关机命令失败: {e}")
        except Exception as e:
             print(f"执行关机时发生未知错误: {e}")


    def check_and_print_status(self):
        """Helper function to query and print current status and manage shutdown timer"""
        try:
            battery = psutil.sensors_battery()
            was_plugged_in = self.is_plugged_in # 记录之前的状态
            if battery:
                self.is_plugged_in = battery.power_plugged
                # 简化状态打印,仅在状态变化时提示
                # plugged_status_str = "已充电/接入电源" if self.is_plugged_in else "未在充电"
                # print(f"当前电源状态: {plugged_status_str}, 电量: {battery.percent}%")

                if not self.is_plugged_in:
                    # 检测到未充电
                    if was_plugged_in or self.shutdown_timer is None:
                        # 如果之前是充电的,或者是第一次检测到未充电且无定时器
                        print(f"提示:笔记本电脑当前未在充电! 电量: {battery.percent}%. 将在 {SHUTDOWN_DELAY_SECONDS} 秒后关机.")
                        self.cancel_pending_shutdown() # 先取消旧的(理论上不应该有)
                        # 启动新的 60 秒关机定时器
                        self.shutdown_timer = threading.Timer(SHUTDOWN_DELAY_SECONDS, self.schedule_shutdown)
                        self.shutdown_timer.start()
                    # else: 已经在倒计时中,无需重复启动

                else:
                    # 检测到已充电
                    if not was_plugged_in: # 如果之前是未充电的
                         print("提示:电源已连接,取消关机计划。")
                    # 取消任何待定的关机
                    self.cancel_pending_shutdown()

            else:
                # print("无法获取电池信息,可能不是笔记本电脑或传感器不可用。") # 简化日志
                self.is_plugged_in = True # 默认为连接(安全起见)
                self.cancel_pending_shutdown() # 取消关机

        except Exception as e:
            print(f"查询电源状态时出错: {e}")
            # 出错时也应取消关机以防万一
            self.cancel_pending_shutdown()


    def start_message_loop(self):
        print("Windows 电源监听器已启动...")
        print("请插拔电源测试功能。按 Ctrl+C 退出。")
        # 初始检查一次状态
        self.check_and_print_status()
        try:
            while True:
                pythoncom.PumpWaitingMessages()
                win32api.Sleep(5000) # 每5秒处理一次消息队列

        except KeyboardInterrupt:
            print("\n收到退出指令,正在关闭...")
        finally:
            self.cancel_pending_shutdown() # 确保退出时取消定时器
            win32gui.DestroyWindow(self.hwnd)
            print("监听器已关闭。")

# 编译
# pyinstaller --noconfirm --onefile --hidden-import=win32timezone powerOffShutDown.py
if __name__ == "__main__":
    listener = PowerListenerWindow()
    listener.start_message_loop()
最后修改:2025 年 12 月 16 日
如果觉得我的文章对你有用,请随意赞赏