OYB software, raspberrypi

Control your Raspberry Pi with your TV remote


This little script will allow you to control your Raspberry Pi using the buttons in your regular TV remote.


You can press the following buttons on the remote.

  • OK       – launch kodi
  • up       – launch browser
  • down  – change wallpaper (this is a custom script of mine)
  • left      – suspend your desktop computer
  • back    – halt Raspberry Pi
  • stop     – enter “mouse mode”, to move the mouse with the arrows. OK to double click
  • play     – exit “mouse mode”

These bindings are set as an example. Each person should change them to suit their own needs.


First, follow the instructions to compile and install libcec. More details below.

Then, get the code from my gist, and create an executable script

You can run picec.sh  from whatever “startup programs” configuration your desktop provides. In my case, I just add the following line at the end of /etc/rc.local

Optionally, you can install xdotool  for moving the mouse, and osd_cat  for showing information in a TV OSD fashion. More details below.


As a means to control our RPi, we will use the CEC (Consumer Electronics Control) feature of HDMI, which is a standard protocol to send commands back and forth through HDMI.

CEC allows consumer devices to control each other with or without user intervention. The GPU of the Raspberry Pi already supports the CEC protocol, as most modern TVs do. There is a compatibility list here.

An often missed requirement is that your HDMI cable must support CEC as well. Most cables do, but some of the cheapest ones cannot talk CEC, so be careful with this.

In order to speak to our TV using the CEC standard, we make use of the libcec library by Pulse Eight. This library is already included in Kodi out of the box, and it is the reason we can readily control our media center with our remote.

From their website, we can follow the instructions to compile and install libcec on the Raspberry Pi.

After this, we obtain the library, some python bindings and a wrapper utility in C called cec-client .

We can now list connected devices,

, turn the TV on and off from SSH,

, and even configure the TV. To see all available commands:

As a quick and dirty way of playing with this, I wrote this little script that just parses the output of the cec-client utility. I figured it would be faster to implement than using the C library and end up calling system()  or popen() anyway.

There is lots of room for improvement, but I hope this can give you ideas to get started.

The script makes use of osd_cat for the visual feedback, and xdotool  for the mouse control functionality. It is recommended to install them with apt, but it’s not required because the script  will detect wether they are available or not.

As you can see on this list, each manufacturer supports the protocol to a varying degree, so you will have to play a little bit to see what button you can detect, and wether you can get the duration of the button press. For instance, on my Philips remote, I do not have access to the numbers.

Moving the mouse is not really very useful, as the latency is terrible. However, there are other cool things that you might want to associate to your remote using xdtool, for example you can read PDFs or websites binding

, or even bind to <CTRL><TAB>  to navigate through browser tabs.

Author: nachoparker

Humbly sharing things that I find useful [ github dockerhub ]

8 Comments on “Control your Raspberry Pi with your TV remote

  1. I see you own a similar remote to mine. Might be that you are using a Phillips TV. I have one and CEC doesn’t seem to work. EasyLink is enabled, but it does not respond to the echo "on 0000" | cec-client -d 1 -s "standby 0" RPI I send when the Pi boots. That should power up the TV

  2. hi, this doesn’t work on my Panasonic. i’ve enabled verbose mode, this is the output, do you have any ideas? thanks!!

    No device type given. Using ‘recording device’
    CEC Parser created – libCEC version 4.0.2
    no serial port given. trying autodetect:
    path: Raspberry Pi
    com port: RPI

    opening a connection to the CEC adapter…
    DEBUG: [ 53] Broadcast (F): osd name set to ‘Broadcast’
    DEBUG: [ 56] Open – vc_cec initialised
    DEBUG: [ 56] logical address changed to Free use (e)
    NOTICE: [ 56] connection opened
    DEBUG: [ 58] processor thread started
    DEBUG: [ 58] < TV (0): POLL
    DEBUG: [ 58] initiator ‘Broadcast’ is not supported by the CEC adapter. using ‘Free use’ instead
    TRAFFIC: [ 58] <> POLL sent
    DEBUG: [ 89] TV (0): device status changed into ‘present’
    DEBUG: [ 89] << requesting vendor ID of 'TV' (0)
    TRAFFIC: [ 89] <> 0f:87:00:80:45
    DEBUG: [ 298] TV (0): vendor = Panasonic (008045)
    DEBUG: [ 298] >> TV (0) -> Broadcast (F): device vendor id (87)
    DEBUG: [ 298] expected response received (87: device vendor id)
    DEBUG: [ 298] replacing the command handler for device ‘TV’ (0)
    NOTICE: [ 298] registering new CEC client – v4.0.2
    DEBUG: [ 298] detecting logical address for type ‘playback device’
    DEBUG: [ 298] trying logical address ‘Playback 1’
    DEBUG: [ 298] < Playback 1 (4): POLL
    TRAFFIC: [ 298] << 44
    TRAFFIC: [ 568] <> POLL not sent
    DEBUG: [ 839] using logical address ‘Playback 1’
    DEBUG: [ 839] Playback 1 (4): device status changed into ‘handled by libCEC’
    DEBUG: [ 839] Playback 1 (4): power status changed from ‘unknown’ to ‘on’
    DEBUG: [ 839] Playback 1 (4): vendor = Pulse Eight (001582)
    DEBUG: [ 839] Playback 1 (4): CEC version 1.4
    DEBUG: [ 839] AllocateLogicalAddresses – device ‘0’, type ‘playback device’, LA ‘4’
    DEBUG: [ 839] logical address changed to Playback 1 (4)
    DEBUG: [ 839] Playback 1 (4): osd name set to ‘CECTester’
    DEBUG: [ 839] Playback 1 (4): menu language set to ‘eng’
    DEBUG: [ 839] GetPhysicalAddress – physical address = 1000
    DEBUG: [ 839] AutodetectPhysicalAddress – autodetected physical address ‘1000’
    DEBUG: [ 839] Playback 1 (4): physical address changed from ffff to 1000
    DEBUG: [ 839] < broadcast (F): physical address 1000
    TRAFFIC: [ 839] << 4f:84:10:00:04
    NOTICE: [ 990] CEC client registered: libCEC version = 4.0.2, client version = 4.0.2, firmware version = 1, logical address(es) = Playback 1 (4) , physical address:, git revision: libcec-4.0.2+8-8563411~dirty, compiled on Sun Apr 2 17:04:01 UTC 2017 by root@hostname: Name or service not known on Linux 4.4.0-57-generic (armv7l), features: P8_USB, DRM, P8_detect, randr, RPi
    DEBUG: [ 990] Playback 1 (4): vendor = Panasonic (008045)
    DEBUG: [ 990] replacing the command handler for device 'Playback 1' (4)
    DEBUG: [ 990] < TV (0): OSD name ‘CECTester’
    TRAFFIC: [ 990] << 40:47:43:45:43:54:65:73:74:65:72
    DEBUG: [ 1290] << requesting power status of 'TV' (0)
    TRAFFIC: [ 1290] <> 04:00:47:00
    DEBUG: [ 1471] marking opcode ‘set osd name’ as unsupported feature for device ‘TV’
    DEBUG: [ 1471] >> TV (0) -> Playback 1 (4): feature abort ( 0)
    TRAFFIC: [ 1540] >> 04:90:00
    DEBUG: [ 1540] TV (0): power status changed from ‘unknown’ to ‘on’
    DEBUG: [ 1540] >> TV (0) -> Playback 1 (4): report power status (90)
    waiting for input
    DEBUG: [ 1540] expected response received (90: report power status)
    TRAFFIC: [ 2026] >> 04:8c
    DEBUG: [ 2026] < TV (0): vendor id Panasonic (8045)
    TRAFFIC: [ 2026] <> TV (0) -> Playback 1 (4): give device vendor id (8C)
    TRAFFIC: [ 2305] >> 04:89:10:01:05
    TRAFFIC: [ 2305] <> TV (0) -> Playback 1 (4): vendor command (89)
    TRAFFIC: [ 2852] >> 04:a0:00:80:45:06:05
    DEBUG: [ 2853] >> TV (0) -> Playback 1 (4): vendor command with id (A0)
    TRAFFIC: [ 4031] >> 04:a0:00:80:45:06:05
    DEBUG: [ 4031] >> TV (0) -> Playback 1 (4): vendor command with id (A0)
    TRAFFIC: [ 5206] >> 04:a0:00:80:45:06:05
    DEBUG: [ 5206] >> TV (0) -> Playback 1 (4): vendor command with id (A0)

  3. Hi – helpful! Should this work with the Pi Zero mini HDMI too? You know? And I’m just trying to build a RPi touchscreen that switches between to sources, TV and PC computer.

    Thanks for any ideas.

  4. Do you know if there’s any way to trigger this in Javascript within Chromium? Thinking about signage displays in particular, being able to schedule TV on/off with the signage application (DAKboard)

  5. The program works. However, when you make click with the OK button, it doesn’t work anymore. I realized that the text “break” from the code was the problem, if you have the same issue, just remove that text.

Leave a Reply

Your email address will not be published. Required fields are marked *