Skip to content

Instantly share code, notes, and snippets.

@wiverson
Last active February 8, 2026 15:52
Show Gist options
  • Select an option

  • Save wiverson/b3174341def87c3d1c335f12b39a5e5a to your computer and use it in GitHub Desktop.

Select an option

Save wiverson/b3174341def87c3d1c335f12b39a5e5a to your computer and use it in GitHub Desktop.
Setting up a Frame Desktop as a HTPC

HDMI CEC TV Control on Bazzite Linux with UGREEN DP-to-HDMI Adapter

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.

Requirements

  • 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)

Why This Works

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.


Step 1: Verify CEC Device Exists

ls /dev/cec*

You should see /dev/cec0. If not, your adapter may not support CEC or the kernel module isn't loaded.

Step 2: Test CEC Manually

Configure your device and discover your physical address:

sudo cec-ctl -d /dev/cec0 --playback -S

Look 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.0

Your TV should turn on and switch to the correct input.


Step 3: Set Up Permissions

Add yourself to the required groups for device access:

sudo usermod -aG video,input $USER

Log out and back in for the group membership to take effect.


Step 4: Install Dependencies (Xbox Button Listener Only)

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 reboot

Skip this step if you only want the boot and display wake triggers.


Step 5: Install the Main Script

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-tv

Step 6: Install the System Service (Boot Trigger)

This 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.service

Step 7: Install User Services (Display Wake & Xbox Button)

These services run as your user and respond to session events. User services don't require sudo and live in your home directory.

Create directories

mkdir -p ~/.local/bin
mkdir -p ~/.config/systemd/user

Display Wake Listener Script

Listens 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-listener

Display Wake Listener Service

tee ~/.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
EOF

Xbox Button Listener Script (Optional)

Triggers 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-listener

Xbox Button Listener Service (Optional)

tee ~/.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

Step 8: Enable Services

# 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.service

Step 9: Test

Test boot trigger:

  1. Reboot your PC
  2. TV should turn on and switch to PC input

Test display wake:

  1. Let your display blank (or lock the screen)
  2. Move the mouse or press a key
  3. TV should switch to PC input

Test Xbox button:

  1. While display is on and TV is on another input
  2. Press the Xbox button
  3. TV should switch to PC input

Architecture

Service Types

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

How It Works

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:

  1. Configures the PC as a CEC playback device
  2. Reads the physical address from EDID (auto-detects HDMI port)
  3. Checks if already the active source (skips if so)
  4. Sends image-view-on to wake the TV
  5. Sends active-source to switch input

Troubleshooting

Permission denied on /dev/cec0 or /dev/input/*:

Ensure you're in the correct groups:

groups

Should include video and input. If not:

sudo usermod -aG video,input $USER

Then log out and back in.

System service not running:

sudo systemctl status cec-wake-tv.service
journalctl -u cec-wake-tv.service

User 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.service

TV 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 xbox

Update CONTROLLER_NAME in the script if it differs.

Find the Xbox button code:

sudo evtest

Select your controller and press the Xbox button. Look for the code number and update BUTTON_CODE if needed.


Debugging

If something isn't working, use these steps to isolate the problem.

Test Scripts Manually First

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-listener

If a script fails, you'll see the error immediately.

Check for Syntax Errors

bash -n /usr/local/bin/cec-wake-tv
bash -n ~/.local/bin/cec-display-listener
bash -n ~/.local/bin/cec-xbox-listener

No output means no syntax errors.

Test CEC Commands Individually

# 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.0

Test D-Bus Events

Open 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.

Test Xbox Button Detection

# 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/eventX

Press the Xbox button and note the code. It should be 316 (BTN_MODE).

Check Service Status

# System service
sudo systemctl status cec-wake-tv.service

# User services
systemctl --user status cec-display-listener.service
systemctl --user status cec-xbox-listener.service

Look for:

  • Active: active (running) — service is running
  • Active: failed — service crashed
  • Active: inactive (dead) — service not started

Read Service Logs

# 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 -f

Trigger the event and watch for output.

Add Debug Output to Scripts

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 runs

Then trigger the script and check the log:

cat /tmp/cec-wake-tv.log

Remove the debug lines once you've fixed the issue.

Common Issues

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

Restart Services After Edits

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.service

Uninstall

System service

sudo systemctl disable --now cec-wake-tv.service
sudo rm /etc/systemd/system/cec-wake-tv.service
sudo systemctl daemon-reload

User services

systemctl --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-reload

Scripts

sudo rm /usr/local/bin/cec-wake-tv
rm ~/.local/bin/cec-display-listener
rm ~/.local/bin/cec-xbox-listener

evtest (if installed)

sudo rpm-ostree uninstall evtest
sudo systemctl reboot

Note on Suspend

If 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

Credits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment