Last active
December 12, 2025 19:43
-
-
Save literallylara/ce89e0eba56a46ae527de21a4bf2beb8 to your computer and use it in GitHub Desktop.
Linux CEC Setup + Monitoring + Remote Control Keymap for Pulse-Eight CEC Adapter or DisplayPort
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/sh | |
| # Resources: | |
| # - https://wiki.archlinux.org/title/HDMI-CEC | |
| # - https://man.archlinux.org/man/extra/v4l-utils/rc_keymap.5.en | |
| # - https://man.archlinux.org/man/extra/v4l-utils/ir-keytable.1.en | |
| # - https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html | |
| # - https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html | |
| # Make sure to replace card1-HDMI-A-2 with your own connector in: cec0-daemon-autostart.rules | |
| if [ "$(id -u)" -ne 0 ]; then | |
| echo "This script must be run as root" | |
| exit 1 | |
| fi | |
| # Install dependencies | |
| dnf install -y v4l-utils linuxconsoletools rc-tools | |
| # Install cec-daemon | |
| install -vDm755 cec-daemon /usr/local/bin | |
| # Setup Pulse-Eight CEC Adapter (if present) | |
| # - Attaches /dev/ttyACM0 to /dev/cec0 | |
| # - CEC via display port is attached automatically on linux | |
| if lsusb -v | grep --silent Pulse-Eight; then | |
| install -vDm644 [email protected] /etc/systemd/system/ | |
| install -vDm644 pulse8-cec-autoattach.rules /etc/udev/rules.d/ | |
| fi | |
| # Configure CEC | |
| { | |
| install -vDm644 [email protected] /etc/systemd/system/ | |
| install -vDm644 cec0-daemon-autostart.rules /etc/udev/rules.d/ | |
| } | |
| # Setup remote control keymap | |
| { | |
| install -vDm644 cec.toml /etc/rc_keymaps/cec.toml | |
| ir-keytable --sysdev rc0 --read | | |
| sed -E 's/^scancode //;s/ \(.+\)$//;s/(KEY_[A-Z0-9]+)/"\1"/' 2>/dev/null \ | |
| >>/etc/rc_keymaps/cec.toml | |
| install -vDm644 rc0-keymap-autoapply.rules /etc/udev/rules.d/ | |
| } | |
| # reload systemd and udev | |
| { | |
| systemctl daemon-reload | |
| udevadm control --reload-rules | |
| udevadm trigger | |
| } | |
| # Monitor cec daemon with: | |
| # journalctl -f -u [email protected]" | |
| # Monitor adapter with: | |
| # journalctl -f -u [email protected]" | |
| # Monitor remote control events with: | |
| # ir-keytable --sysdev rc0 --test |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| if [ "$(id -u)" -ne 0 ]; then | |
| echo "This script must be run as root" | |
| exit 1 | |
| fi | |
| rules=( | |
| /etc/udev/rules.d/pulse8-cec-autoattach.rules | |
| /etc/udev/rules.d/cec0-daemon-autostart.rules | |
| /etc/rc_keymaps/cec.toml | |
| /etc/udev/rules.d/rc0-keymap-autoapply.rules | |
| ) | |
| services=( | |
| /etc/systemd/system/[email protected] | |
| /etc/systemd/system/[email protected] | |
| ) | |
| binaries=( | |
| /usr/local/bin/cec-daemon | |
| ) | |
| # remove rules | |
| for path in "${rules[@]}"; do | |
| if [ -f "$path" ]; then | |
| echo "Removing: $path" | |
| rm -f "$path" | |
| else | |
| echo "Skipping (not found): $path" | |
| fi | |
| done | |
| # stop services | |
| for path in "${services[@]}"; do | |
| service_basename=$(basename "$path") | |
| if [[ "$path" == *"@.service" ]]; then | |
| search="${service_basename/@.service/@*.service}" | |
| base="${service_basename/@.service/}" | |
| names=$( | |
| systemctl list-units "$search" --all | | |
| grep "$base" | xargs | cut -d ' ' -f1 | |
| ) | |
| for name in $names; do | |
| echo "Stopping $name" | |
| systemctl stop "$name" | |
| done | |
| else | |
| echo "Stopping $name" | |
| systemctl stop "$name" | |
| fi | |
| echo "Removing: $path" | |
| rm -f "$path" | |
| done | |
| # remove binaries | |
| for path in "${binaries[@]}"; do | |
| if [ -f "$path" ]; then | |
| echo "Removing: $path" | |
| rm -f "$path" | |
| else | |
| echo "Skipping (not found): $path" | |
| fi | |
| done | |
| # reload systemd and udev | |
| systemctl daemon-reload | |
| udevadm control --reload-rules |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Configures the CEC adapter as playback device | |
| # and monitors output to set it as the active source whenever the root device selects it | |
| CONNECTOR=$1 | |
| HOSTNAME=$(hostnamectl hostname) | |
| EVENTS=( | |
| "SET_STREAM_PATH" | |
| "STANDBY" | |
| ) | |
| function register_as_playback() { | |
| cec-ctl "--osd-name=$HOSTNAME" --playback "--phys-addr-from-edid=/sys/class/drm/$CONNECTOR/edid" | |
| } | |
| function get_phys_addr() { | |
| cec-ctl --skip-info --physical-address | |
| } | |
| function set_as_active_source() { | |
| cec-ctl --skip-info --active-source "phys-addr=$1" >/dev/null | |
| } | |
| function on_standby() { | |
| echo "-- STANDBY --" | |
| } | |
| function on_active() { | |
| echo "-- ACTIVE --" | |
| } | |
| function on_unactive() { | |
| echo "-- UNACTIVE --" | |
| } | |
| function on_set_stream_path() { | |
| if [[ $1 != $(get_phys_addr) ]]; then | |
| on_unactive | |
| return | |
| fi | |
| set_as_active_source "$1" | |
| on_active | |
| } | |
| function handle_event() { | |
| local line=$1 | |
| local event=$2 | |
| if [[ "$line" != *"$event"* ]]; then return 1; fi | |
| case $event in | |
| "STANDBY") | |
| on_standby | |
| ;; | |
| "SET_STREAM_PATH") | |
| IFS= read -r line | |
| addr=$(echo "$line" | cut -d ':' -f2 | xargs) | |
| _line="" | |
| on_set_stream_path "$addr" | |
| ;; | |
| *) | |
| echo "Unknown event: $event" | |
| ;; | |
| esac | |
| return 0 | |
| } | |
| function monitor() { | |
| _last_line="" | |
| cec-ctl --skip-info --monitor --ignore all,poll | while IFS= read -r _line; do | |
| if [[ "$_line" != "$_last_line" ]]; then | |
| for event in "${EVENTS[@]}"; do | |
| handle_event "$_line" "$event" && break | |
| done | |
| fi | |
| _last_line=$_line | |
| done | |
| } | |
| register_as_playback | |
| monitor |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [[protocols]] | |
| name = "CEC Remote Control" | |
| protocol = "cec" | |
| [protocols.scancodes] | |
| # Execute the following command and insert the output here: | |
| # | |
| # ir-keytable --sysdev rc0 --read 2>/dev/null | sed -E 's/^scancode //;s/ \(.+\)$//;s/(KEY_[A-Z0-9]+)/"\1"/' | |
| # | |
| # It should look like this: | |
| # 0x0000 = "KEY_ENTER" | |
| # 0x0001 = "KEY_UP" | |
| # 0x0002 = "KEY_DOWN" | |
| # 0x0003 = "KEY_LEFT" | |
| # 0x0004 = "KEY_RIGHT" | |
| # ... | |
| # | |
| # You can then modify the events as needed |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| SUBSYSTEM=="cec" KERNEL=="cec0" ACTION=="add" TAG+="systemd" ENV{SYSTEMD_WANTS}="[email protected]" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [Unit] | |
| # Should be called as "[email protected]" or similar | |
| Description=Configure CEC adapter as playback device and monitor output to set it as the active source whenever the root device selects it | |
| AssertPathExists=/sys/class/drm/%i/edid | |
| BindsTo=dev-cec0.device | |
| [Service] | |
| Type=exec | |
| ExecStart=/usr/local/bin/cec-daemon "%i" | |
| Restart=always | |
| RestartSec=5 | |
| ProtectSystem=strict | |
| ProtectHome=true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [Unit] | |
| # Should be called as "[email protected]" or similar | |
| Description=Configure USB Pulse-Eight serial device at %I | |
| ConditionPathExists=%I | |
| [Service] | |
| Type=forking | |
| ExecStart=/usr/bin/inputattach --daemon --pulse8-cec %I | |
| ProtectSystem=strict | |
| ProtectHome=true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| SUBSYSTEM=="tty" ACTION=="add" ATTRS{manufacturer}=="Pulse-Eight" ATTRS{product}=="CEC Adapter" TAG+="systemd" ENV{SYSTEMD_WANTS}="pulse8-cec-attach@$devnode.service" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| SUBSYSTEM=="rc" ACTION=="add" ENV{DRV_NAME}=="cec" RUN+="/usr/bin/ir-keytable --sysdev $devpath --write /etc/rc_keymaps/cec.toml" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment