#!/usr/bin/env python3
"""
raspberry_pi_wol.py — wake the WIVEXX PC from a Raspberry Pi  (v1.7)
═══════════════════════════════════════════════════════════════════════
Copy this single file to the Raspberry Pi. No packages needed (stdlib only).

  Wake the PC:           python3 raspberry_pi_wol.py
  Wake some other MAC:   python3 raspberry_pi_wol.py AA:BB:CC:DD:EE:FF

The defaults below are pre-filled for the WIVEXX PC (PCDURITAS), verified
WOL-capable on 2026-06-11 — magic packet wake works even from full shutdown.

──────────────────────────────────────────────────────────────────────────
HOW TO USE IT FROM YOUR PHONE (the safe way)
──────────────────────────────────────────────────────────────────────────
1. On the Pi:  install Tailscale (https://tailscale.com — free):
       curl -fsSL https://tailscale.com/install.sh | sh
       sudo tailscale up
2. Install the Tailscale app on your phone, log into the same account.
3. From anywhere in the world: SSH to the Pi through Tailscale and run this
   script — or set up a one-tap shortcut (e.g. Termius snippet / iOS
   Shortcut over SSH) that runs:  python3 ~/raspberry_pi_wol.py
4. ~30 s later the PC is up, WIVEXX autostarts, and you can open the WIVEXX
   web page from the phone (also through Tailscale).

SECURITY — IMPORTANT
  • NEVER port-forward UDP 9 (WOL) or the WIVEXX web port on the router.
    Magic packets are unauthenticated; an exposed port means anyone on the
    internet can power your PC and reach WIVEXX. Routers usually drop
    forwarded broadcasts anyway, so it wouldn't even work reliably.
  • The Tailscale/WireGuard VPN path above exposes NOTHING — the Pi and
    phone talk over an encrypted private network, keys live on your devices.
  • Keep the Pi updated (sudo apt update && sudo apt full-upgrade) — it is
    the only device with the power to wake your PC remotely.
"""

import re
import socket
import sys

# ── WIVEXX PC (PCDURITAS) ──────────────────────────────────────────────────────
PC_MAC        = "50:EB:F6:80:1C:C0"     # Intel I225-V Ethernet
LAN_BROADCAST = "192.168.0.255"         # home subnet broadcast
WOL_PORT      = 9


def send_magic_packet(mac: str, broadcast: str = LAN_BROADCAST,
                      port: int = WOL_PORT, repeats: int = 5) -> None:
    clean = re.sub(r"[^0-9a-fA-F]", "", mac)
    if len(clean) != 12:
        raise SystemExit(f"Invalid MAC address: {mac!r}")
    payload = b"\xff" * 6 + bytes.fromhex(clean) * 16
    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
        s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        for _ in range(repeats):
            s.sendto(payload, (broadcast, port))
    print(f"Magic packet sent to {mac} via {broadcast}:{port} (x{repeats})")


if __name__ == "__main__":
    target = sys.argv[1] if len(sys.argv) > 1 else PC_MAC
    send_magic_packet(target)
