Powering Down the Idle GPU: Wake-on-LAN AI Orchestration
Powering Down the Idle GPU: Wake-on-LAN AI Orchestration
Glavin! If you are running high-performance models on a separate GPU rig, you know the frustration: either you leave it running 24/7 (wasting power) or you spend half your time manually managing power states.
I’ve engineered a compact solution: a Python orchestration proxy. It uses Wake-on-LAN (WoL) to fire up your GPU rig, waits for your inference API (e.g., LM Studio) to initialize, launches your Hermes session, and implements an SSH-powered sleep watchdog to kill the power after 10 minutes of inactivity.
The Architecture
`text
User ──> Proxy (WoL + SSH) ──> GPU Rig (Model API)
`
The Orchestrator
This script replaces your standard hermes CLI invocation:
`python
import os
import sys
import time
import socket
import threading
import subprocess
import paramiko
from wakeonlan import send_magic_packet
--- Configuration ---
PC_MAC = "AA:BB:CC:DD:EE:FF"
PC_IP = "192.168.1.50"
LM_STUDIO_PORT = 1234
SSH_USER = "username"
SSH_KEY_PATH = os.path.expanduser("~/.ssh/id_rsa")
INACTIVITY_TIMEOUT_SEC = 600
Windows: "rundll32.exe powrprof.dll,SetSuspendState 0,1,0"
Linux: "sudo systemctl suspend"
REMOTE_SLEEP_CMD = "rundll32.exe powrprof.dll,SetSuspendState 0,1,0"
class HostManager:
def __init__(self):
self.last_activity = time.time()
self.stop_event = threading.Event()
def update_activity(self):
self.last_activity = time.time()
def wake_host(self):
print(f"[*] Sending Magic Packet to {PC_MAC}...")
send_magic_packet(PC_MAC)
print("[*] Waiting for API to come online...", end="", flush=True)
while not self.stop_event.is_set():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(2)
try:
if s.connect_ex((PC_IP, LM_STUDIO_PORT)) == 0:
print("\n[+] Host is awake!")
self.update_activity()
return True
except Exception: pass
print(".", end="", flush=True)
time.sleep(2)
return False
def put_host_to_sleep(self):
print("\n[*] Sending sleep command via SSH...")
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(PC_IP, username=SSH_USER, key_filename=SSH_KEY_PATH, timeout=5)
ssh.exec_command(REMOTE_SLEEP_CMD)
ssh.close()
print("[+] Sleep command delivered.")
except Exception as e: print(f"[-] Failed: {e}")
def idle_watchdog(self):
while not self.stop_event.is_set():
time.sleep(10)
if time.time() - self.last_activity >= INACTIVITY_TIMEOUT_SEC:
self.put_host_to_sleep()
self.stop_event.set()
break
def main():
manager = HostManager()
if not manager.wake_host(): sys.exit(1)
threading.Thread(target=manager.idle_watchdog, daemon=True).start()
try:
process = subprocess.Popen(["hermes"] + sys.argv[1:])
while process.poll() is None:
manager.update_activity()
time.sleep(2)
finally:
manager.stop_event.set()
if input("\nPut host to sleep? [Y/n]: ").strip().lower() in ("", "y"):
manager.put_host_to_sleep()
if __name__ == "__main__":
main()
`
Turning it into a Skill
To make this truly useful for a daily coding workflow, I’ve packaged this into a Hermes Skill called wol-orchestrator.
Instead of manual execution, I now trigger tasks by invoking the skill from my main terminal:
`bash
hermes terminal --command "python ~/scripts/hermes_orchestrator.py --task 'Refactor the authentication middleware'"
`
This delegates the entire lifecycle—power management, API readiness, and cleanup—directly to the agent. No more manual power toggling. Efficiency!

