Update and rename WakeOnLAN.py to Backup/WakeOnLAN.py

This commit is contained in:
hlt1995 authored and GitHub committed 2026-02-04 14:13:48 +08:00
1 parent 94536e7175
commit e9310718bb
1 file changed
+1 -1
+244
View File
@@ -0,0 +1,244 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# const $ = new Env('网络唤醒')
"""
Wake-on-LAN 网络唤醒 支持多网络多设备唤醒
变量示例
局域网单设备唤醒:
export WOL_MAC=AA-BB-CC-DD-EE-FF
局域网多设备唤醒:
export WOL_MAC=AA-BB-CC-DD-EE-FF&AA-BB-CC-DD-EE-GG
外网单设备唤醒:
export WOL_MAC=AA-BB-CC-DD-EE-FF
export WOL_IP=123.123.123.123(支持动态域名)
export WOL_PORT=9001(不填则为默认端口9)
外网多设备单网络唤醒:
export WOL_MAC=AA-BB-CC-DD-EE-FF&AA-BB-CC-DD-EE-GG
export WOL_IP=123.123.123.123(支持动态域名)
export WOL_PORT=9001&9002(不填则为默认端口9)
外网多设备多网络唤醒:
export WOL_MAC=AA-BB-CC-DD-EE-FF&AA-BB-CC-DD-EE-GG
export WOL_IP=123.123.123.123&124.124.124.124(支持动态域名)
export WOL_PORT=9001&9002(不填则为默认端口9)
"""
import socket
import struct
import argparse
import sys
import os
import re
from typing import Optional, List, Tuple
class WOLWakeOnLan:
def __init__(self, broadcast_address: str = '225.225.225.255', port: int = 9):
self.broadcast_address = broadcast_address
self.port = port
def create_magic_packet(self, mac_address: str) -> bytes:
mac_address = mac_address.replace(':', '').replace('-', '')
if len(mac_address) != 12:
raise ValueError("❌ MAC地址格式不正确")
try:
mac_bytes = bytes.fromhex(mac_address)
except ValueError:
raise ValueError("❌ MAC地址包含无效字符")
magic_packet = b'\xff' * 6 + mac_bytes * 16
return magic_packet
def wake_up(self, mac_address: str, interface: Optional[str] = None) -> bool:
try:
ip_pattern = re.compile(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
if not ip_pattern.match(self.broadcast_address):
print(f"🔍 正在解析域名: {self.broadcast_address}")
try:
resolved_ip = socket.gethostbyname(self.broadcast_address)
print(f"🔍 解析结果: {resolved_ip}")
target_address = resolved_ip
except socket.gaierror as e:
print(f"❌ 域名解析失败: {e}")
return False
else:
target_address = self.broadcast_address
magic_packet = self.create_magic_packet(mac_address)
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
if interface:
try:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, interface.encode())
except OSError:
print(f"⚠️ 无法绑定到接口 {interface},使用默认接口")
sock.sendto(magic_packet, (target_address, self.port))
print(f"📍 目标 MAC: {mac_address}")
print(f"📡 广播地址: {target_address}:{self.port}")
if interface:
print(f"🔌 网络接口: {interface}")
print(f"✅ 幻数据包已发送!")
return True
except Exception as e:
print(f"❌ 发送幻数据包失败: {e}")
return False
def is_multi_device_mode() -> bool:
macs_env = os.getenv('WOL_MAC', '')
return '&' in macs_env
def parse_device_configs() -> List[Tuple[str, str, int]]:
macs_env = os.getenv('WOL_MAC', '')
ips_env = os.getenv('WOL_IP', '225.225.225.255')
ports_env = os.getenv('WOL_PORT', '9')
if not macs_env:
print("❌ 请设置 WOL_MAC 环境变量")
sys.exit(1)
macs = [mac.strip() for mac in macs_env.split('&') if mac.strip()]
ips = [ip.strip() for ip in ips_env.split('&') if ip.strip()]
ports = [port.strip() for port in ports_env.split('&') if port.strip()]
print(f"🔍️ 检测到 {len(macs)} 台设备")
if len(ips) == 1 and len(macs) > 1:
print("📝 当前配置:所有设备处于同一IP地址")
ips = ips * len(macs)
if len(ports) == 1 and len(macs) > 1:
print("📝 当前配置:所有设备使用同一唤醒端口")
ports = ports * len(macs)
if len(macs) != len(ips) or len(macs) != len(ports):
print("❌ 环境变量配置不匹配")
print(f" MAC地址数量: {len(macs)}")
print(f" IP地址数量: {len(ips)}")
print(f" 端口数量: {len(ports)}")
print("💡 请确保:")
print(" - 单设备: WOL_MAC=AA-BB-CC-DD-EE-FF")
print(" - 多设备: WOL_MAC=AA-BB-CC-DD-EE-FF&BB-CC-DD-EE-FF-GG")
print(" - 多IP/端口: 使用相同数量的&分隔符")
sys.exit(1)
try:
ports = [int(port) for port in ports]
except ValueError:
print("❌ 端口号必须为整数")
sys.exit(1)
return list(zip(macs, ips, ports))
def get_single_device_config() -> Tuple[str, str, int]:
mac_env = os.getenv('WOL_MAC', '')
ip_env = os.getenv('WOL_IP', '225.225.225.255')
port_env = os.getenv('WOL_PORT', '9')
if not mac_env:
print("❌ 请设置 WOL_MAC 环境变量")
sys.exit(1)
mac = mac_env.split('&')[0].strip() if '&' in mac_env else mac_env
ip = ip_env.split('&')[0].strip() if '&' in ip_env else ip_env
try:
port = int(port_env.split('&')[0].strip()) if '&' in port_env else int(port_env)
except ValueError:
print("❌ 端口号必须为整数")
sys.exit(1)
return mac, ip, port
def wake_multiple_devices(interface: Optional[str] = None) -> bool:
devices = parse_device_configs()
print(f"🖥️ 正在唤醒设备...\n")
success_count = 0
for i, (mac, ip, port) in enumerate(devices, 1):
print(f"--- 设备 {i}/{len(devices)} ---")
wol = WOLWakeOnLan(broadcast_address=ip, port=port)
if wol.wake_up(mac, interface):
success_count += 1
print() # 空行分隔
return success_count > 0
def wake_single_device(mac: str = None, ip: str = None, port: int = None, interface: Optional[str] = None) -> bool:
if mac is None or ip is None or port is None:
mac, ip, port = get_single_device_config()
print(f"🖥️ 正在唤醒设备...\n")
wol = WOLWakeOnLan(broadcast_address=ip, port=port)
return wol.wake_up(mac, interface)
def main():
# 从环境变量读取配置
env_interface = os.getenv('WOL_INTERFACE')
parser = argparse.ArgumentParser(description='WOL远程开机脚本')
parser.add_argument('mac', nargs='?', help='目标设备的MAC地址 (格式: XX:XX:XX:XX:XX:XX)')
parser.add_argument('-b', '--broadcast',
help='广播地址 (默认从WOL_IP环境变量读取)')
parser.add_argument('-p', '--port', type=int,
help='端口号 (默认从WOL_PORT环境变量读取)')
parser.add_argument('-i', '--interface', default=env_interface,
help='网络接口名称')
parser.add_argument('--list-interfaces', action='store_true',
help='列出可用的网络接口')
parser.add_argument('--single', action='store_true',
help='强制单设备模式')
parser.add_argument('--multi', action='store_true',
help='强制多设备模式')
args = parser.parse_args()
if args.list_interfaces:
print("可用的网络接口:")
try:
import netifaces
interfaces = netifaces.interfaces()
for iface in interfaces:
addrs = netifaces.ifaddresses(iface)
if netifaces.AF_INET in addrs:
ip = addrs[netifaces.AF_INET][0]['addr']
print(f" {iface}: {ip}")
except ImportError:
print(" ℹ️ 需要安装netifaces库来显示接口信息")
print(" 💡 运行: pip install netifaces")
return
if args.multi:
print("🔧 强制多设备模式")
success = wake_multiple_devices(args.interface)
elif args.single:
print("🔧 强制单设备模式")
if args.mac:
ip = args.broadcast if args.broadcast else os.getenv('WOL_IP', '225.225.225.255')
port = args.port if args.port else int(os.getenv('WOL_PORT', '9'))
success = wake_single_device(args.mac, ip, port, args.interface)
else:
success = wake_single_device(interface=args.interface)
else:
if is_multi_device_mode():
success = wake_multiple_devices(args.interface)
else:
if args.mac:
ip = args.broadcast if args.broadcast else os.getenv('WOL_IP', '225.225.225.255')
port = args.port if args.port else int(os.getenv('WOL_PORT', '9'))
success = wake_single_device(args.mac, ip, port, args.interface)
else:
success = wake_single_device(interface=args.interface)
sys.exit(0 if success else 1)
if __name__ == "__main__":
main()