Console-style Steam Remote Play between a Steam Machine and a Steam Deck. Wake the host from the couch, sleep it when you're done, and see at a glance whether it's awake.
Install · Pair · Daily use · Limitations · Architecture
The plugin auto-detects its role from the hardware (Steam Machine → host, Steam Deck → client) and pairs the two with a single "Allow" press on the host's screen. Steam Remote Play itself is untouched — this only takes care of what Steam doesn't.
| Deck (client) | Steam Machine (host) |
|---|---|
| Wake host (Wake-on-LAN) · Sleep host · Power off host | Wake-on-LAN armed on the NIC at every boot |
| Live status in the plugin title (awake / asleep / streaming) + toasts | Auto-sleep N min after the stream ends |
| "Leaving home" mode: keep the host awake for internet streaming | Sleep when idle (no stream and no local input for N min) |
| Last stream stats (fps, bitrate, ping, slow stages) | Local-input guard: never sleeps while someone plays at the TV |
| One-tap host discovery + approve-on-host pairing | Token-protected LAN API with rate limiting |
| Optional: wake host when the Deck resumes / when the Library opens | Optional fixed IP for faster resume; experimental stream display profile |
flowchart LR
subgraph Deck["🎮 Steam Deck — client"]
C1["Wake / Sleep / Power off"]
C2["Status in plugin title<br/>+ toasts"]
C3["Leaving-home mode"]
end
subgraph Host["🖥️ Steam Machine — host"]
H1["Wake-on-LAN armed<br/>on the NIC at boot"]
H2["Auto-sleep after stream<br/>Sleep when idle"]
H3["Local-input guard"]
end
C1 -- "magic packet (LAN broadcast)" --> H1
C1 -- "token-authenticated<br/>HTTP API :27889" --> H2
H2 -- "status, stream stats" --> C2
H3 -. "blocks sleep while<br/>someone plays at the TV" .-> H2
C3 -- "keep awake" --> H2
The Deck wakes the host with a Wake-on-LAN magic packet, then talks to it over a small token-protected LAN API. Steam Remote Play itself is untouched — the plugin only handles power and status around it.
- Install Decky Loader on both devices.
- Download CouchLink.zip onto each device.
- On each device, in Gaming Mode: Quick Access (⋯) → Decky (plug icon) → gear → General → Developer mode → Developer → Install Plugin from Zip → pick the zip.
On the Deck: Quick Access → Decky → CouchLink → Pair host… → Find hosts on this network → tap the host. The host's screen shows "Allow "Galileo (steamdeck)" … to wake, sleep and power off this machine?" → press Allow. Done. (Manual pairing with IP + the token shown on the host panel is available as a fallback.)
- Wake host – the Steam Machine resumes in ~10 s; Steam re-lists it a few seconds later and the Stream button comes back.
- Sleep host – suspends it (this is the recommended way to "turn it off": it wakes over the network; a full power-off does not — see below).
- Auto-sleep / Sleep when idle (host panel) – turn these on once; the host sleeps on its own after streams or when unused, but never while someone uses a controller or keyboard at the TV.
- Leaving home (Deck panel) – toggle Keep host awake before going out to stream over the internet; toggle it off when back, and your sleep settings come back.
- Wake from full power-off (S5) doesn't work on the Steam Machine, even with the BIOS "S4/S5 Wake on LAN"
option enabled. The firmware's ACPI tables only allow the Ethernet port's wake from S4 (
/proc/acpi/wakeupshowsGPP5 S4), and Linux arms wake sources for power-off only when they support S5. Windows "shut down" is really hibernate (S4), which is why it appears to work there. Tracked upstream in ValveSoftware/SteamOS#2735. Use Sleep. - No remote wake from outside your LAN: Wake-on-LAN is a LAN broadcast and the host API only accepts LAN peers. Use Leaving home mode before you go out. (Orange Livebox routers have no reliable Wake-on-WAN.)
- Steam's
SteamClient.RemotePlaysession events do not fire for plain streams on the host; stream detection uses Steam'sstreaming_log.txtmarkers, which is reliable but Steam-version dependent. - Decky's bundled Python exports
LD_LIBRARY_PATHpointing at its own OpenSSL, which breakssystemctlin child processes (OPENSSL_x not found); the plugin scrubs it (util.run).
pnpm i && pnpm run build # frontend → dist/index.js
python3 main.py # backend smoke test (no Decky needed; role auto-detected)
python3 -m py_modules.couchlink.wol status|enable|send <mac>
python3 -m py_modules.couchlink.streamwatch replay # replay Steam's log through the detector
python3 -m py_modules.couchlink.activity # list input devices the idle guard watches
PYTHONPATH=py_modules python3 -m couchlink.cli ping|status|wake|sleep|poweroff <ip> <token> [mac]
./deploy.sh zip # build out/CouchLink.zip for Decky's "Install from Zip"See docs/ARCHITECTURE.md, docs/TESTING.md and
CHANGELOG.md. Logs: ~/homebrew/logs/CouchLink/, journalctl -u plugin_loader.
Issues and pull requests are welcome — see CONTRIBUTING.md. If you hit a bug, the bug report template asks for the few details (device role, SteamOS version, plugin log) that make it reproducible.
BSD-3-Clause. Built with the Decky plugin template.