Automatically turn on your TV and switch to the correct HDMI input when:
- Your PC boots
- Your display wakes from blanking
- You press the Xbox button on your controller
Tested on Bazzite (KDE Plasma) with an LG TV.
- Bazzite Linux (KDE Plasma)
- UGREEN 8K@60Hz DisplayPort 1.4 to HDMI 2.1 Adapter (model 85564/DP134)
- A CEC-compatible TV (LG Simplink, Samsung Anynet+, Sony Bravia Sync, Philips EasyLink, etc.)
- Xbox Wireless Adapter (optional, for Xbox button trigger)
Most desktop GPUs don't support HDMI CEC, but the UGREEN DP134 adapter has the CEC pin wired, enabling CEC tunneling over DisplayPort. This exposes /dev/cec0 on Linux, allowing you to control your TV.
Credit to Lawstorant's Reddit post for discovering this capability.
ls /dev/cec*You should see /dev/cec0. If not, your adapter may not support CEC or the kernel module isn't loaded.
Configure your device and discover your physical address:
sudo cec-ctl -d /dev/cec0 --playback -SLook for Physical Address : X.0.0.0 in the output (e.g., 4.0.0.0). This corresponds to the HDMI port number on your TV.
Test waking the TV and switching input:
sudo cec-ctl -d /dev/cec0 --to 0 --image-view-on
sudo cec-ctl -d /dev/cec0 --active-source phys-addr=4.0.0.0Your TV should turn on and switch to the correct input.
Add yourself to the required groups for device access:
sudo usermod -aG video,input $USERLog out and back in for the group membership to take effect.
If you want the Xbox button listener, you need evtest. Bazzite is an immutable Fedora Atomic image, so use rpm-ostree:
sudo rpm-ostree install evtest
sudo systemctl rebootSkip this step if you only want the boot and display wake triggers.
This script wakes the TV and switches input, but only if the PC isn't already the active source.
sudo tee /usr/local/bin/cec-wake-tv << 'EOF'
#!/bin/bash
sleep 2
CONFIG_OUTPUT=$(cec-ctl -d /dev/cec0 --playback -S 2>&1)
PHYS_ADDR=$(echo "$CONFIG_OUTPUT" | grep -oP 'Physical Address\s*:\s*\K\d+\.\d+\.\d+\.\d+')
ALREADY_ACTIVE=$(echo "$CONFIG_OUTPUT" | grep -A5 "Playback Device" | grep -q "active source: yes" && echo "yes")
if [ -n "$PHYS_ADDR" ] && [ "$ALREADY_ACTIVE" != "yes" ]; then
cec-ctl -d /dev/cec0 --to 0 --image-view-on
cec-ctl -d /dev/cec0 --active-source phys-addr="$PHYS_ADDR"
fi
EOF
sudo chmod +x /usr/local/bin/cec-wake-tvThis service runs at system boot as root. It hooks into graphical.target, which is a system-level target and requires a system service.
sudo tee /etc/systemd/system/cec-wake-tv.service << 'EOF'
[Unit]
Description=Wake TV and switch input via CEC
After=graphical.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/cec-wake-tv
[Install]
WantedBy=graphical.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable cec-wake-tv.serviceThese services run as your user and respond to session events. User services don't require sudo and live in your home directory.
mkdir -p ~/.local/bin
mkdir -p ~/.config/systemd/userListens for D-Bus events when the display wakes from blanking.
tee ~/.local/bin/cec-display-listener << 'EOF'
#!/bin/bash
dbus-monitor --session "interface='org.freedesktop.ScreenSaver',member='ActiveChanged'" 2>/dev/null | while read line; do
if echo "$line" | grep -q "boolean false"; then
/usr/local/bin/cec-wake-tv
fi
done
EOF
chmod +x ~/.local/bin/cec-display-listenertee ~/.config/systemd/user/cec-display-listener.service << 'EOF'
[Unit]
Description=Listen for display wake to switch TV input
[Service]
Type=simple
ExecStart=%h/.local/bin/cec-display-listener
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
EOFTriggers input switch when you press the Xbox button, even if the display is already awake.
Requires evtest (see Step 4).
tee ~/.local/bin/cec-xbox-listener << 'EOF'
#!/bin/bash
CONTROLLER_NAME="Xbox"
BUTTON_CODE=316
DEVICE=$(grep -l "$CONTROLLER_NAME" /sys/class/input/event*/device/name 2>/dev/null | head -1 | sed 's|/device/name|/uevent|' | xargs grep -h DEVNAME | cut -d= -f2)
if [ -z "$DEVICE" ]; then
echo "Controller not found"
exit 1
fi
evtest "/dev/$DEVICE" 2>/dev/null | while read line; do
if echo "$line" | grep -q "code $BUTTON_CODE.*value 1"; then
/usr/local/bin/cec-wake-tv
fi
done
EOF
chmod +x ~/.local/bin/cec-xbox-listenertee ~/.config/systemd/user/cec-xbox-listener.service << 'EOF'
[Unit]
Description=Listen for Xbox button to switch TV input
[Service]
Type=simple
ExecStart=%h/.local/bin/cec-xbox-listener
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
EOF# Reload user daemon
systemctl --user daemon-reload
# Enable display wake listener
systemctl --user enable --now cec-display-listener.service
# Enable Xbox button listener (optional, requires evtest)
systemctl --user enable --now cec-xbox-listener.serviceTest boot trigger:
- Reboot your PC
- TV should turn on and switch to PC input
Test display wake:
- Let your display blank (or lock the screen)
- Move the mouse or press a key
- TV should switch to PC input
Test Xbox button:
- While display is on and TV is on another input
- Press the Xbox button
- TV should switch to PC input
| Service | Level | Location | Why |
|---|---|---|---|
| Boot trigger | System | /etc/systemd/system/ |
Hooks graphical.target (system-level) |
| Display listener | User | ~/.config/systemd/user/ |
D-Bus session events are user-level |
| Xbox listener | User | ~/.config/systemd/user/ |
Runs as user, needs no system privileges |
| Trigger | Mechanism | When It Fires |
|---|---|---|
| Boot | graphical.target |
Desktop environment ready |
| Display wake | D-Bus org.freedesktop.ScreenSaver.ActiveChanged |
Screen unblanks/unlocks |
| Xbox button | evtest listening for BTN_MODE (316) | Xbox button pressed |
All three triggers call the same cec-wake-tv script, which:
- Configures the PC as a CEC playback device
- Reads the physical address from EDID (auto-detects HDMI port)
- Checks if already the active source (skips if so)
- Sends
image-view-onto wake the TV - Sends
active-sourceto switch input
Permission denied on /dev/cec0 or /dev/input/*:
Ensure you're in the correct groups:
groupsShould include video and input. If not:
sudo usermod -aG video,input $USERThen log out and back in.
System service not running:
sudo systemctl status cec-wake-tv.service
journalctl -u cec-wake-tv.serviceUser services not running:
systemctl --user status cec-display-listener.service
systemctl --user status cec-xbox-listener.service
journalctl --user -u cec-display-listener.service
journalctl --user -u cec-xbox-listener.serviceTV not responding:
Make sure CEC is enabled in your TV's settings:
- LG: Simplink
- Samsung: Anynet+
- Sony: Bravia Sync
- Philips: EasyLink
- Vizio: CEC
- TCL/Roku TV: System → Control other devices
Xbox controller not detected:
Check the controller name in your system:
cat /sys/class/input/event*/device/name | grep -i xboxUpdate CONTROLLER_NAME in the script if it differs.
Find the Xbox button code:
sudo evtestSelect your controller and press the Xbox button. Look for the code number and update BUTTON_CODE if needed.
If something isn't working, use these steps to isolate the problem.
Before relying on services, run each script directly to catch errors:
# Test main CEC script
sudo /usr/local/bin/cec-wake-tv
# Test display listener (Ctrl+C to stop)
~/.local/bin/cec-display-listener
# Test Xbox listener (Ctrl+C to stop)
~/.local/bin/cec-xbox-listenerIf a script fails, you'll see the error immediately.
bash -n /usr/local/bin/cec-wake-tv
bash -n ~/.local/bin/cec-display-listener
bash -n ~/.local/bin/cec-xbox-listenerNo output means no syntax errors.
# Does the device exist?
ls -la /dev/cec0
# Can you configure it?
cec-ctl -d /dev/cec0 --playback -S
# Can you send commands?
cec-ctl -d /dev/cec0 --to 0 --image-view-on
cec-ctl -d /dev/cec0 --active-source phys-addr=4.0.0.0Open a terminal and monitor D-Bus:
dbus-monitor --session "interface='org.freedesktop.ScreenSaver',member='ActiveChanged'"Then lock/unlock your screen or let it blank and wake. You should see:
boolean true # screen locked/blanked
boolean false # screen unlocked/woke
If nothing appears, KDE's screen saver might use a different interface.
# List input devices
cat /sys/class/input/event*/device/name
# Find your controller
cat /sys/class/input/event*/device/name | grep -i xbox
# Monitor button presses (replace eventX with your device)
sudo evtest /dev/input/eventXPress the Xbox button and note the code. It should be 316 (BTN_MODE).
# System service
sudo systemctl status cec-wake-tv.service
# User services
systemctl --user status cec-display-listener.service
systemctl --user status cec-xbox-listener.serviceLook for:
Active: active (running)— service is runningActive: failed— service crashedActive: inactive (dead)— service not started
# System service logs (follow in real time)
journalctl -u cec-wake-tv.service -f
# User service logs (follow in real time)
journalctl --user -u cec-display-listener.service -f
journalctl --user -u cec-xbox-listener.service -fTrigger the event and watch for output.
Temporarily add logging to see what's happening. Edit the script and add these lines at the top:
#!/bin/bash
exec >> /tmp/cec-wake-tv.log 2>&1
echo "=== $(date) ==="
set -x # Print each command as it runsThen trigger the script and check the log:
cat /tmp/cec-wake-tv.logRemove the debug lines once you've fixed the issue.
| Symptom | Likely Cause | Fix |
|---|---|---|
Permission denied on /dev/cec0 |
Not in video group |
sudo usermod -aG video $USER, then log out/in |
Permission denied on /dev/input/* |
Not in input group |
sudo usermod -aG input $USER, then log out/in |
| Script works manually but service fails | Path issue or environment difference | Use absolute paths, check journalctl for errors |
Controller not found |
Controller name mismatch | Check cat /sys/class/input/event*/device/name and update CONTROLLER_NAME |
| No D-Bus events | Wrong interface for your desktop | Try dbus-monitor --session without filters to see what's available |
grep: Invalid regular expression |
Typo in grep pattern | Check the -oP regex syntax |
| CEC command times out | TV CEC disabled or adapter issue | Enable CEC in TV settings, try different HDMI port |
Physical Address not found |
grep pattern doesn't match output | Run cec-ctl --playback -S and check exact output format |
After fixing a script:
# System service
sudo systemctl restart cec-wake-tv.service
# User services
systemctl --user restart cec-display-listener.service
systemctl --user restart cec-xbox-listener.servicesudo systemctl disable --now cec-wake-tv.service
sudo rm /etc/systemd/system/cec-wake-tv.service
sudo systemctl daemon-reloadsystemctl --user disable --now cec-display-listener.service
systemctl --user disable --now cec-xbox-listener.service
rm ~/.config/systemd/user/cec-display-listener.service
rm ~/.config/systemd/user/cec-xbox-listener.service
systemctl --user daemon-reloadsudo rm /usr/local/bin/cec-wake-tv
rm ~/.local/bin/cec-display-listener
rm ~/.local/bin/cec-xbox-listenersudo rpm-ostree uninstall evtest
sudo systemctl rebootIf system suspend/hibernate is working on your system, update the system service to also trigger on resume:
sudo tee /etc/systemd/system/cec-wake-tv.service << 'EOF'
[Unit]
Description=Wake TV and switch input via CEC
After=suspend.target graphical.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/cec-wake-tv
[Install]
WantedBy=suspend.target graphical.target
EOF
sudo systemctl daemon-reload
sudo systemctl reenable cec-wake-tv.service- Lawstorant for discovering the UGREEN adapter's CEC capability
- cec-toolbox for inspiration
- Linux Kernel CEC documentation