Skip to content

pyCEC for CEC Monitoring

sramani-metro edited this page Aug 5, 2024 · 3 revisions

Setting Up and Using pyCEC for CEC Monitoring

Overview

pyCEC is a Python library for interfacing with HDMI CEC (Consumer Electronics Control) devices. This guide will help you set up pyCEC on a Raspberry Pi or similar device, monitor CEC messages, and decode them into a human-readable format.

Prerequisites

  • Hardware: Raspberry Pi4.
  • Software: Python 3.x, pyCEC library, and cec-utils.

Setup Instructions

1. Ensure libcec is Installed:

First, make sure that libcec is installed on your Raspberry Pi. You can install it using the following commands:

sudo apt-get update
sudo apt-get install libcec-dev cec-utils

2. Install pyCEC

First, you'll need to install pyCEC using pip. Open a terminal and run:

pip3 install cec

2. Verify CEC Availability

Ensure that your device has CEC capabilities and the necessary tools installed. You can check for CEC support using:

cec-ctl -d0 --status

3. Create a Monitoring Script

Create a Python script to monitor CEC messages. Below is an example script that initializes pyCEC, retrieves device information, and prints out decoded CEC messages.

import cec
import time


# Initialize CEC
adapters = cec.list_adapters()
if len(adapters) > 0:
   adapter = adapters[0]
   print("Using Adapter %s"%(adapter))
   cec.init(adapter)

# CEC logical address to string mapping
logical_address_map = {
    0: "TV",
    1: "Recording Device 1",
    2: "Recording Device 2",
    3: "Tuner 1",
    4: "Playback Device 1",
    5: "Audio System",
    6: "Tuner 2",
    7: "Tuner 3",
    8: "Playback Device 2",
    9: "Recording Device 3",
    10: "Tuner 4",
    11: "Playback Device 3",
    12: "Reserved 1",
    13: "Reserved 2",
    14: "Free use",
    15: "Broadcast"
}

# CEC opcodes to string mapping
opcode_map = {
    0x00: "Feature Abort",
    0x04: "Image View On",
    0x05: "Tuner Step Increment",
    0x06: "Tuner Step Decrement",
    0x07: "Tuner Device Status",
    0x08: "Give Tuner Device Status",
    0x09: "Record On",
    0x0A: "Record Status",
    0x0B: "Record Off",
    0x0D: "Text View On",
    0x0F: "Record TV Screen",
    0x1A: "Give Deck Status",
    0x1B: "Deck Status",
    0x32: "Set Menu Language",
    0x33: "Clear Analog Timer",
    0x34: "Set Analog Timer",
    0x35: "Timer Status",
    0x36: "Standby",
    0x41: "Play",
    0x42: "Deck Control",
    0x43: "Timer Cleared Status",
    0x44: "User Control Pressed",
    0x45: "User Control Released",
    0x46: "Give OSD Name",
    0x47: "Set OSD Name",
    0x64: "Set OSD String",
    0x67: "Set Timer Program Title",
    0x70: "System Audio Mode Request",
    0x71: "Give Audio Status",
    0x72: "Set System Audio Mode",
    0x7A: "Report Audio Status",
    0x7D: "Give System Audio Mode Status",
    0x7E: "System Audio Mode Status",
    0x80: "Routing Change",
    0x81: "Routing Information",
    0x82: "Active Source",
    0x83: "Give Physical Address",
    0x84: "Report Physical Address",
    0x85: "Request Active Source",
    0x86: "Set Stream Path",
    0x87: "Device Vendor ID",
    0x89: "Vendor Command",
    0x8A: "Vendor Remote Button Down",
    0x8B: "Vendor Remote Button Up",
    0x8C: "Give Device Vendor ID",
    0x8D: "Menu Request",
    0x8E: "Menu Status",
    0x8F: "Give Device Power Status",
    0x90: "Report Power Status",
    0x91: "Get Menu Language",
    0x92: "Select Analog Service",
    0x93: "Select Digital Service",
    0x97: "Set Digital Timer",
    0x99: "Clear Digital Timer",
    0x9A: "Set Audio Rate",
    0x9D: "Inactive Source",
    0x9E: "CEC Version",
    0x9F: "Get CEC Version",
    0xA0: "Vendor Command With ID",
    0xA1: "Clear External Timer",
    0xA2: "Set External Timer",
    0xC0: "Initiate ARC",
    0xC1: "Report ARC Initiated",
    0xC2: "Report ARC Terminated",
    0xC3: "Request ARC Initiation",
    0xC4: "Request ARC Termination",
    0xC5: "Terminate ARC",
    0xFF: "Abort"
}

# Dictionary to store logical address to device name mapping
device_name_map = {}

def get_device_names():
    devices = cec.list_devices()
    print("Devices found:")
    print("-----------------------------------------------------------------------------------------------------------------------------------------------------")
    for device in devices:
        d = cec.Device(device)
        logical_address = d.address
        name = d.osd_string if d.osd_string else f"Unknown Device {logical_address}"
        device_name_map[logical_address] = name
        print(f"Logical Address: {logical_address}, Name: {name}, Physical Address: {d.physical_address}, Vendor ID: {d.vendor}, CEC Version: {d.cec_version}, Language: {d.language}")
    print("-----------------------------------------------------------------------------------------------------------------------------------------------------")


# Function to decode CEC message
def decode_cec_message(event):
    opcode = event['opcode']
    initiator = event['initiator']
    destination = event['destination']
    parameters = event['parameters']
    initiator_name = device_name_map.get(initiator, f"Unknown Device {initiator}")
    destination_name = "Broadcast" if destination == 15 else device_name_map.get(destination, f"Unknown Device {destination}")
    opcode_name = opcode_map.get(opcode, f"Unknown Opcode 0x{opcode:02X}")
    parameters_str = ' '.join(f'0x{byte:02X}' for byte in parameters)
    print(f"CEC Message: {initiator_name} -> {destination_name} | Opcode: {opcode_name} | Parameters: {parameters_str}")


# Callback function to process received CEC messages
def process_cec_message(event, *param):
    for msg in param:
        decode_cec_message(msg)


# Get device names at startup
get_device_names()

print("*************Monitoring Messages**********************")
# Register the callback function
cec.add_callback(process_cec_message, cec.EVENT_COMMAND)

# Keep the script running to continue monitoring
try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    cec.close()

4. Run the Monitoring Script

Save the script as cec-monitor.py and run it using:

python3 cec-monitor.py

5. Interpreting the Output

The script will print out the details of connected devices and continuously monitor CEC messages. The decode_cec_message function translates the CEC opcodes into human-readable descriptions.

Using Adapter /dev/cec0
Devices found:
-----------------------------------------------------------------------------------------------------------------------------------------------------
Logical Address: 0, Name: TV, Physical Address: 0.0.0.0, Vendor ID: 080046, CEC Version: 1.4, Language: eng
Logical Address: 1, Name: python-cec, Physical Address: 1.2.0.0, Vendor ID: 001582, CEC Version: 1.4, Language: eng
Logical Address: 4, Name: SOUND SYSTEM, Physical Address: 1.0.0.0, Vendor ID: 080046, CEC Version: 1.4, Language: ???
Logical Address: 5, Name: SOUND SYSTEM, Physical Address: 1.0.0.0, Vendor ID: 080046, CEC Version: 1.4, Language: ???
Logical Address: 11, Name: Fire TV Stick, Physical Address: 2.0.0.0, Vendor ID: 000CE7, CEC Version: 1.4, Language: ???
-----------------------------------------------------------------------------------------------------------------------------------------------------
*************Monitoring Messages**********************
CEC Message: Fire TV Stick -> Broadcast | Opcode: Device Vendor ID | Parameters: 0x00 0x0C 0xE7
CEC Message: Fire TV Stick -> python-cec | Opcode: Give Physical Address | Parameters: 
CEC Message: TV -> python-cec | Opcode: Give OSD Name | Parameters: 
CEC Message: Fire TV Stick -> python-cec | Opcode: Give Device Vendor ID | Parameters: 
CEC Message: Fire TV Stick -> python-cec | Opcode: Give OSD Name | Parameters: 
CEC Message: Fire TV Stick -> python-cec | Opcode: Get CEC Version | Parameters: 
CEC Message: TV -> python-cec | Opcode: Give Device Vendor ID | Parameters: 
CEC Message: Fire TV Stick -> python-cec | Opcode: Feature Abort | Parameters: 0x9E 0x00
CEC Message: Fire TV Stick -> python-cec | Opcode: Give Physical Address | Parameters: 
CEC Message: Fire TV Stick -> python-cec | Opcode: Give Device Vendor ID | Parameters: 
CEC Message: Fire TV Stick -> python-cec | Opcode: Give OSD Name | Parameters: 
CEC Message: Fire TV Stick -> python-cec | Opcode: Get CEC Version | Parameters: 
CEC Message: TV -> python-cec | Opcode: Give OSD Name | Parameters: 
CEC Message: TV -> python-cec | Opcode: Give Device Vendor ID | Parameters: 
CEC Message: Fire TV Stick -> python-cec | Opcode: Get CEC Version | Parameters: 
CEC Message: Fire TV Stick -> python-cec | Opcode: Get CEC Version | Parameters: 
CEC Message: SOUND SYSTEM -> Broadcast | Opcode: Report Physical Address | Parameters: 0x10 0x00 0x04
CEC Message: SOUND SYSTEM -> Broadcast | Opcode: Device Vendor ID | Parameters: 0x08 0x00 0x46
CEC Message: SOUND SYSTEM -> Broadcast | Opcode: Device Vendor ID | Parameters: 0x08 0x00 0x46
CEC Message: SOUND SYSTEM -> Broadcast | Opcode: Report Physical Address | Parameters: 0x10 0x00 0x04
CEC Message: SOUND SYSTEM -> Broadcast | Opcode: Device Vendor ID | Parameters: 0x08 0x00 0x46
CEC Message: SOUND SYSTEM -> Broadcast | Opcode: Device Vendor ID | Parameters: 0x08 0x00 0x46
CEC Message: SOUND SYSTEM -> Broadcast | Opcode: Report Physical Address | Parameters: 0x10 0x00 0x05
CEC Message: SOUND SYSTEM -> Broadcast | Opcode: Device Vendor ID | Parameters: 0x08 0x00 0x46
CEC Message: SOUND SYSTEM -> Broadcast | Opcode: Device Vendor ID | Parameters: 0x08 0x00 0x46
CEC Message: SOUND SYSTEM -> Broadcast | Opcode: Report Physical Address | Parameters: 0x10 0x00 0x05
CEC Message: SOUND SYSTEM -> Broadcast | Opcode: Device Vendor ID | Parameters: 0x08 0x00 0x46
CEC Message: SOUND SYSTEM -> Broadcast | Opcode: Device Vendor ID | Parameters: 0x08 0x00 0x46