CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-cec

Python bindings for libcec to control CEC-compliant HDMI devices from Python scripts

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

CEC (Consumer Electronics Control)

Python bindings for libcec, enabling control of CEC-compliant HDMI devices (TVs, receivers, etc.) from Python scripts. The library provides a comprehensive API for device discovery, power management, volume control, input switching, and event-driven programming through callbacks.

Package Information

  • Package Name: cec
  • Language: Python (C++ extension)
  • Installation: pip install cec
  • Hardware Requirements: CEC-capable hardware (Pulse-Eight USB-CEC adapter, Raspberry Pi, etc.)

Core Imports

import cec

Basic Usage

import cec

# Initialize CEC library with default adapter
cec.init()

# Create a device object for the TV
tv = cec.Device(cec.CECDEVICE_TV)

# Control the TV
tv.power_on()
print("TV is on:", tv.is_on())

# Volume control
cec.volume_up()
cec.volume_down()
cec.toggle_mute()

# Clean shutdown (when supported)
# cec.close()  # Not implemented in current version

Capabilities

Adapter Management

Initialize and manage CEC adapters for communication with the CEC bus.

def list_adapters():
    """
    List available CEC adapters on the system.
    
    Returns:
        list: Available adapter names/paths
    """

def init():
    """
    Initialize CEC library with the default adapter.
    
    Returns:
        bool: True if initialization successful
        
    Raises:
        RuntimeError: If initialization fails
    """

def init(adapter):
    """
    Initialize CEC library with a specific adapter.
    
    Args:
        adapter (str): Adapter name/path from list_adapters()
        
    Returns:
        bool: True if initialization successful
        
    Raises:
        RuntimeError: If initialization fails
    """

Device Discovery

Discover and enumerate CEC devices on the bus.

def list_devices():
    """
    List all discovered CEC devices on the bus.
    
    Returns:
        dict: Dictionary mapping device logical addresses (int) to Device objects
    """

Event System

Register and manage event callbacks for CEC events like key presses, commands, and state changes.

def add_callback(handler, events):
    """
    Add an event callback handler.
    
    Args:
        handler (callable): Function to handle events, signature varies by event type
        events (int): Bitmask of event types to listen for
        
    Returns:
        bool: True if callback was registered successfully
    """

def remove_callback(handler, events):
    """
    Remove an event callback handler.
    
    Args:
        handler (callable): Previously registered callback function
        events (int): Bitmask of event types to stop listening for
        
    Returns:
        bool: True if callback was removed successfully
    """

Raw Communication

Send raw CEC commands directly to devices on the bus.

def transmit(destination, opcode, parameters=None, initiator=None):
    """
    Transmit a raw CEC command to a specific device.
    
    Args:
        destination (int): Target device logical address (0-15)
        opcode (int): CEC opcode for the command
        parameters (bytes, optional): Command parameters as byte string
        initiator (int, optional): Source logical address (0-15). If not specified, uses primary adapter address
        
    Returns:
        bool: True if transmission successful
    """

Active Source Management

Manage which device is the active source on the CEC bus.

def is_active_source(addr):
    """
    Check if device at the given address is the active source.
    
    Args:
        addr (int): Device logical address
        
    Returns:
        bool: True if device is active source
    """

def set_active_source():
    """
    Set the default device as the active source.
    
    Returns:
        bool: True if successful
    """

def set_active_source(device_type):
    """
    Set a specific device type as the active source.
    
    Args:
        device_type (int): CEC device type constant
        
    Returns:
        bool: True if successful
    """

Volume Control

Control audio volume and mute state through CEC.

def volume_up():
    """
    Increase the volume via CEC.
    
    Returns:
        bool: True if command sent successfully
    """

def volume_down():
    """
    Decrease the volume via CEC.
    
    Returns:
        bool: True if command sent successfully
    """

def toggle_mute():
    """
    Toggle mute state via CEC.
    Available in libcec 2.0+ only.
    
    Returns:
        bool: True if command sent successfully
    """

Physical Address Management

Configure HDMI physical addresses and stream paths.

def set_stream_path(path):
    """
    Set the HDMI stream path.
    
    Args:
        path (int): Physical address as integer
        
    Returns:
        bool: True if successful
    """

def set_physical_addr(addr):
    """
    Set the HDMI physical address.
    
    Args:
        addr (int): Physical address as integer
        
    Returns:
        bool: True if successful
    """

Configuration Persistence

Manage CEC configuration persistence to adapter hardware.

def can_persist_config():
    """
    Check if the current adapter supports configuration persistence.
    
    Returns:
        bool: True if adapter can persist configuration
    """

def persist_config():
    """
    Persist the current CEC configuration to the adapter.
    
    Returns:
        bool: True if configuration was persisted successfully
    """

def set_port(device, port):
    """
    Set upstream HDMI port for a device.
    
    Args:
        device (int): Device logical address
        port (int): HDMI port number
        
    Returns:
        bool: True if successful
    """

Device Objects

The Device class represents individual CEC devices and provides methods for device-specific control.

class Device:
    """
    Represents a CEC device on the bus.
    """
    
    def __init__(self, id):
        """
        Create a Device object for the given logical address.
        
        Args:
            id (int): Logical address of the device (0-15)
        """
    
    # Read-only properties
    @property
    def address(self):
        """
        Logical address of the device.
        
        Returns:
            int: Device logical address (0-15)
        """
    
    @property
    def physical_address(self):
        """
        Physical address of the device in HDMI topology.
        
        Returns:
            int: Physical address as integer
        """
    
    @property
    def vendor(self):
        """
        Vendor ID of the device.
        
        Returns:
            int: Vendor identification number
        """
    
    @property
    def osd_string(self):
        """
        On-Screen Display name of the device.
        
        Returns:
            str: Device OSD name
        """
    
    @property
    def cec_version(self):
        """
        CEC version supported by the device.
        
        Returns:
            int: CEC version number
        """
    
    @property
    def language(self):
        """
        Menu language of the device.
        
        Returns:
            str: ISO language code
        """
    
    # Methods
    def is_on(self):
        """
        Get the power status of the device.
        
        Returns:
            bool: True if device is powered on
        """
    
    def power_on(self):
        """
        Power on the device.
        
        Returns:
            bool: True if command sent successfully
        """
    
    def standby(self):
        """
        Put the device into standby mode.
        
        Returns:
            bool: True if command sent successfully
        """
    
    def is_active(self):
        """
        Check if this device is the active source.
        
        Returns:
            bool: True if device is active source
        """
    
    def set_av_input(self, input):
        """
        Select AV input on the device.
        
        Args:
            input (int): Input number/identifier
            
        Returns:
            bool: True if command sent successfully
        """
    
    def set_audio_input(self, input):
        """
        Select audio input on the device.
        
        Args:
            input (int): Audio input number/identifier
            
        Returns:
            bool: True if command sent successfully
        """
    
    def transmit(self, opcode, parameters):
        """
        Transmit a raw CEC command to this device.
        
        Args:
            opcode (int): CEC opcode for the command
            parameters (bytes): Command parameters as byte string
            
        Returns:
            bool: True if transmission successful
        """

Constants

Event Types

Event type constants for use with callback registration:

EVENT_LOG = 1           # Log message events
EVENT_KEYPRESS = 2      # Key press events
EVENT_COMMAND = 4       # CEC command events
EVENT_CONFIG_CHANGE = 8 # Configuration change events (not implemented)
EVENT_ALERT = 16        # Alert events
EVENT_MENU_CHANGED = 32 # Menu state change events
EVENT_ACTIVATED = 64    # Device activation events
EVENT_ALL = 127         # All event types combined

Alert Types

Alert type constants for EVENT_ALERT callbacks:

CEC_ALERT_SERVICE_DEVICE = 1         # Service device alert
CEC_ALERT_CONNECTION_LOST = 2        # Connection lost alert
CEC_ALERT_PERMISSION_ERROR = 3       # Permission error alert
CEC_ALERT_PORT_BUSY = 4              # Port busy alert
CEC_ALERT_PHYSICAL_ADDRESS_ERROR = 5 # Physical address error alert
CEC_ALERT_TV_POLL_FAILED = 6         # TV polling failed alert

Menu States

Menu state constants for EVENT_MENU_CHANGED callbacks:

CEC_MENU_STATE_ACTIVATED = 0    # Menu activated state
CEC_MENU_STATE_DEACTIVATED = 1  # Menu deactivated state

Device Types

Device type constants for device classification:

CEC_DEVICE_TYPE_TV = 0               # TV device type
CEC_DEVICE_TYPE_RECORDING_DEVICE = 1 # Recording device type
CEC_DEVICE_TYPE_RESERVED = 2         # Reserved device type
CEC_DEVICE_TYPE_TUNER = 3            # Tuner device type
CEC_DEVICE_TYPE_PLAYBACK_DEVICE = 4  # Playback device type
CEC_DEVICE_TYPE_AUDIO_SYSTEM = 5     # Audio system device type

Logical Addresses

Logical address constants for device identification:

CECDEVICE_UNKNOWN = -1          # Unknown device address
CECDEVICE_TV = 0                # TV logical address
CECDEVICE_RECORDINGDEVICE1 = 1  # Recording device 1 address
CECDEVICE_RECORDINGDEVICE2 = 2  # Recording device 2 address
CECDEVICE_TUNER1 = 3            # Tuner 1 address
CECDEVICE_PLAYBACKDEVICE1 = 4   # Playback device 1 address
CECDEVICE_AUDIOSYSTEM = 5       # Audio system address
CECDEVICE_TUNER2 = 6            # Tuner 2 address
CECDEVICE_TUNER3 = 7            # Tuner 3 address
CECDEVICE_PLAYBACKDEVICE2 = 8   # Playback device 2 address
CECDEVICE_RECORDINGDEVICE3 = 9  # Recording device 3 address
CECDEVICE_TUNER4 = 10           # Tuner 4 address
CECDEVICE_PLAYBACKDEVICE3 = 11  # Playback device 3 address
CECDEVICE_RESERVED1 = 12        # Reserved address 1
CECDEVICE_RESERVED2 = 13        # Reserved address 2
CECDEVICE_FREEUSE = 14          # Free use address
CECDEVICE_UNREGISTERED = 15     # Unregistered device address
CECDEVICE_BROADCAST = 15        # Broadcast address

CEC Opcodes

CEC command opcodes for raw communication (selection of commonly used opcodes):

# Power Management
CEC_OPCODE_STANDBY = 0x36                      # Put device in standby
CEC_OPCODE_IMAGE_VIEW_ON = 0x04                # Turn on and show image
CEC_OPCODE_TEXT_VIEW_ON = 0x0D                 # Turn on and show text
CEC_OPCODE_GIVE_DEVICE_POWER_STATUS = 0x8F     # Request power status
CEC_OPCODE_REPORT_POWER_STATUS = 0x90          # Report power status

# Active Source Management  
CEC_OPCODE_ACTIVE_SOURCE = 0x82                # Set active source
CEC_OPCODE_INACTIVE_SOURCE = 0x9D              # Set inactive source
CEC_OPCODE_REQUEST_ACTIVE_SOURCE = 0x85        # Request active source
CEC_OPCODE_SET_STREAM_PATH = 0x86              # Set stream path
CEC_OPCODE_ROUTING_CHANGE = 0x80               # Routing change
CEC_OPCODE_ROUTING_INFORMATION = 0x81          # Routing information

# Device Information
CEC_OPCODE_GIVE_PHYSICAL_ADDRESS = 0x83        # Request physical address
CEC_OPCODE_REPORT_PHYSICAL_ADDRESS = 0x84      # Report physical address  
CEC_OPCODE_GIVE_OSD_NAME = 0x46                # Request OSD name
CEC_OPCODE_SET_OSD_NAME = 0x47                 # Set OSD name
CEC_OPCODE_GIVE_DEVICE_VENDOR_ID = 0x8C        # Request vendor ID
CEC_OPCODE_DEVICE_VENDOR_ID = 0x87             # Report vendor ID
CEC_OPCODE_GET_CEC_VERSION = 0x9F              # Request CEC version
CEC_OPCODE_CEC_VERSION = 0x9E                  # Report CEC version

# Audio System
CEC_OPCODE_GIVE_AUDIO_STATUS = 0x71            # Request audio status
CEC_OPCODE_REPORT_AUDIO_STATUS = 0x7A          # Report audio status
CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS = 0x7D # Request system audio mode
CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS = 0x7E      # Report system audio mode
CEC_OPCODE_SET_SYSTEM_AUDIO_MODE = 0x72         # Set system audio mode
CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST = 0x70     # Request system audio mode

# User Control
CEC_OPCODE_USER_CONTROL_PRESSED = 0x44         # User pressed button
CEC_OPCODE_USER_CONTROL_RELEASE = 0x45         # User released button

# Menu Control
CEC_OPCODE_MENU_REQUEST = 0x8D                 # Menu request
CEC_OPCODE_MENU_STATUS = 0x8E                  # Menu status

# Recording/Timer Control
CEC_OPCODE_RECORD_OFF = 0x0B                   # Stop recording
CEC_OPCODE_RECORD_ON = 0x09                    # Start recording
CEC_OPCODE_RECORD_STATUS = 0x0A                # Report recording status
CEC_OPCODE_RECORD_TV_SCREEN = 0x0F             # Record TV screen
CEC_OPCODE_CLEAR_ANALOGUE_TIMER = 0x33         # Clear analogue timer
CEC_OPCODE_CLEAR_DIGITAL_TIMER = 0x99          # Clear digital timer
CEC_OPCODE_CLEAR_EXTERNAL_TIMER = 0xA1         # Clear external timer
CEC_OPCODE_SET_ANALOGUE_TIMER = 0x34           # Set analogue timer
CEC_OPCODE_SET_DIGITAL_TIMER = 0x97            # Set digital timer
CEC_OPCODE_SET_EXTERNAL_TIMER = 0xA2           # Set external timer
CEC_OPCODE_SET_TIMER_PROGRAM_TITLE = 0x67      # Set timer program title
CEC_OPCODE_TIMER_CLEARED_STATUS = 0x43         # Timer cleared status
CEC_OPCODE_TIMER_STATUS = 0x35                 # Timer status

# Deck Control
CEC_OPCODE_DECK_CONTROL = 0x42                 # Deck control command
CEC_OPCODE_DECK_STATUS = 0x1B                  # Deck status report
CEC_OPCODE_GIVE_DECK_STATUS = 0x1A             # Request deck status
CEC_OPCODE_PLAY = 0x41                         # Play command

# Tuner Control
CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS = 0x08     # Request tuner status
CEC_OPCODE_SELECT_ANALOGUE_SERVICE = 0x92      # Select analogue service
CEC_OPCODE_SELECT_DIGITAL_SERVICE = 0x93       # Select digital service
CEC_OPCODE_TUNER_DEVICE_STATUS = 0x07          # Report tuner status
CEC_OPCODE_TUNER_STEP_DECREMENT = 0x06         # Tuner step down
CEC_OPCODE_TUNER_STEP_INCREMENT = 0x05         # Tuner step up

# Menu/Language Control
CEC_OPCODE_GET_MENU_LANGUAGE = 0x91            # Request menu language
CEC_OPCODE_SET_MENU_LANGUAGE = 0x32            # Set menu language
CEC_OPCODE_SET_OSD_STRING = 0x64               # Set OSD string

# Vendor Commands
CEC_OPCODE_VENDOR_COMMAND = 0x89               # Vendor-specific command
CEC_OPCODE_VENDOR_COMMAND_WITH_ID = 0xA0       # Vendor command with ID
CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN = 0x8A    # Vendor remote button down
CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP = 0x8B      # Vendor remote button up

# Audio Return Channel (ARC)
CEC_OPCODE_START_ARC = 0xC0                    # Start audio return channel
CEC_OPCODE_REPORT_ARC_STARTED = 0xC1           # Report ARC started
CEC_OPCODE_REPORT_ARC_ENDED = 0xC2             # Report ARC ended
CEC_OPCODE_REQUEST_ARC_START = 0xC3            # Request ARC start
CEC_OPCODE_REQUEST_ARC_END = 0xC4              # Request ARC end
CEC_OPCODE_END_ARC = 0xC5                      # End audio return channel

# Audio Rate Control
CEC_OPCODE_SET_AUDIO_RATE = 0x9A               # Set audio sample rate

# Special/Advanced
CEC_OPCODE_CDC = 0xF8                          # Capability Discovery and Control
CEC_OPCODE_NONE = 0xFD                         # No operation

# General
CEC_OPCODE_FEATURE_ABORT = 0x00                # Feature not supported
CEC_OPCODE_ABORT = 0xFF                        # Abort message

Library Feature Constants

Feature availability constants for checking library capabilities:

HAVE_CEC_ADAPTER_DESCRIPTOR = 0 or 1    # Whether library supports adapter descriptors

Advanced Usage Examples

Event Handling

import cec

def log_callback(event, level, time, message):
    """Handle log messages."""
    print(f"CEC Log [{level}]: {message}")

def command_callback(event, source, destination, opcode, parameters):
    """Handle CEC commands."""
    print(f"CEC Command: {source} -> {destination}, opcode: {opcode}")

def key_callback(event, key_code, duration):
    """Handle key press events."""
    print(f"Key pressed: {key_code}, duration: {duration}")

# Register callbacks
cec.add_callback(log_callback, cec.EVENT_LOG)
cec.add_callback(command_callback, cec.EVENT_COMMAND)
cec.add_callback(key_callback, cec.EVENT_KEYPRESS)

# Initialize and use CEC
cec.init()

# Your application logic here...

# Remove callbacks when done
cec.remove_callback(log_callback, cec.EVENT_LOG)
cec.remove_callback(command_callback, cec.EVENT_COMMAND)
cec.remove_callback(key_callback, cec.EVENT_KEYPRESS)

Device Enumeration and Control

import cec

# Initialize CEC
adapters = cec.list_adapters()
if adapters:
    print(f"Available adapters: {adapters}")
    cec.init(adapters[0])  # Use first adapter
else:
    cec.init()  # Use default adapter

# Discover devices
devices = cec.list_devices()
print(f"Discovered devices: {devices}")

# Create device objects and query information
for device_addr in devices:
    device = cec.Device(device_addr)
    print(f"Device {device_addr}:")
    print(f"  Physical Address: {device.physical_address}")
    print(f"  OSD Name: {device.osd_string}")
    print(f"  Vendor: {device.vendor}")
    print(f"  CEC Version: {device.cec_version}")
    print(f"  Language: {device.language}")
    print(f"  Is On: {device.is_on()}")
    print(f"  Is Active: {device.is_active()}")

Raw Command Transmission

import cec

cec.init()

# Set arbitrary device as active source (physical address 2.0.0.0)
destination = cec.CECDEVICE_BROADCAST
opcode = cec.CEC_OPCODE_ACTIVE_SOURCE
parameters = b'\x20\x00'  # Physical address 2.0.0.0

success = cec.transmit(destination, opcode, parameters)
print(f"Command transmission {'successful' if success else 'failed'}")

# Send standby command to TV
tv_addr = cec.CECDEVICE_TV
standby_opcode = cec.CEC_OPCODE_STANDBY
success = cec.transmit(tv_addr, standby_opcode, b'')
print(f"Standby command {'sent' if success else 'failed'}")

Configuration Management

import cec

cec.init()

# Check if adapter supports configuration persistence
if cec.can_persist_config():
    print("Adapter supports configuration persistence")
    
    # Set physical address
    success = cec.set_physical_addr(0x2000)  # Physical address 2.0.0.0
    if success:
        print("Physical address set")
        
        # Persist configuration
        if cec.persist_config():
            print("Configuration persisted to adapter")
        else:
            print("Failed to persist configuration")
    else:
        print("Failed to set physical address")
else:
    print("Adapter does not support configuration persistence")

Error Handling

The CEC library may raise exceptions or return False/None for failed operations:

  • RuntimeError: Raised during initialization failures or critical errors
  • False return values: Most functions return False when commands fail to send
  • None return values: Some functions return None when unable to retrieve information
  • Empty lists: Device/adapter listing functions return empty lists when none found

Always check return values and handle potential exceptions:

import cec

try:
    # Initialize with error handling
    adapters = cec.list_adapters()
    if not adapters:
        print("No CEC adapters found")
        exit(1)
    
    cec.init(adapters[0])
    print("CEC initialized successfully")
    
    # Device operations with return value checking
    tv = cec.Device(cec.CECDEVICE_TV)
    
    if tv.power_on():
        print("TV power on command sent")
    else:
        print("Failed to send TV power on command")
        
    # Volume control with return value checking
    if cec.volume_up():
        print("Volume up command sent")
    else:
        print("Failed to send volume up command")
        
except RuntimeError as e:
    print(f"CEC initialization failed: {e}")
except Exception as e:
    print(f"Unexpected error: {e}")

Platform Considerations

  • Linux: Requires libcec development libraries and appropriate permissions for CEC device access
  • Windows: Requires building libcec from source as binary distributions don't include required development files
  • macOS: Install libcec via Homebrew (brew install libcec)
  • Raspberry Pi: Built-in CEC support, no additional hardware required
  • Hardware: Most computer graphics cards don't support CEC; requires dedicated CEC adapter (Pulse-Eight USB-CEC) or compatible hardware

Install with Tessl CLI

npx tessl i tessl/pypi-cec
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
pypipkg:pypi/cec@0.2.x
Publish Source
CLI
Badge
tessl/pypi-cec badge