An ESPHome component that supports receiving and transmitting HDMI-CEC messages to connected HDMI devices. The ultimate goal of this project is to eventually be merged into the core ESPHome project once it's up to quality. It's currently out for review as esphome/esphome#3017 (see also: esphome/esphome-docs#1789).
The core CEC driver is forked from s-moch/CEC.
My use case: I already have an IR blaster built with ESPHome, but my new TCL TV has a Bluetooth remote. I want to control my older sound gear (connected over optical) with the TV remote but this TV only supports controlling sound gear via HDMI (typically for HDMI-ARC devices). This component allows me to intercept HDMI-CEC volume commands and transmit the IR codes to control the soundbar. It also allows my Apple TV to control the soundbar (so I can control volume from the Remote app on my phone 📱). Theoretically it should allow you to make any older non-HDMI equipment work seamlessly with newer gear. You can also do things like monitor which source is selected, which HDMI devices are powered on, etc.
Drop me a line on Twitter or the ESPHome Discord if you end up trying it and/or have ideas for cool use cases!
Since HDMI-CEC is a 3.3V protocol, no external components are needed. A HDMI breakout connector might be handy though. Just connect:
For now on the ESP8266, until the HDMI driver is rewritten to fully use interrupts, it's a good idea to bump the CPU speed to 160MHz:
esphome:
...
platformio_options:
board_build.f_cpu: 160000000L
Add this repo to your external components
external_components:
- source: github://johnboiles/esphome-hdmi-cec
Configure your hdmi_cec
component. The on_message
triggers will only fire if any specified source
, destination
, opcode
, and data
match. Something like this (the below is abbreviated -- see full example here).
hdmi_cec:
# The initial logical address -- corresponds to device type. This may be
# reassigned if there are other devices of the same type on the CEC bus.
address: 0x05 # Audio system
# Promiscuous mode can be enabled to allow receiving messages not intended for us
promiscuous_mode: false
# Typically the physical address is discovered based on the point-to-point
# topology of the HDMI connections using the DDC line. We don't have access
# to that so we just hardcode a physical address.
physical_address: 0x4000
pin: 4 # GPIO4
on_message:
- opcode: 0xC3 # Request ARC start
then:
- hdmi_cec.send: # Report ARC started
destination: 0x0
data: [ 0xC1 ]
- opcode: 0x71 # Give audio status
source: 0x0 # From the TV
then:
- hdmi_cec.send:
destination: 0x0
data: [ 0x7A, 0x7F ]
- opcode: 0x46 # Give OSD name
then:
- hdmi_cec.send:
destination: 0x0
data: [0x47, 0x65, 0x73, 0x70, 0x68, 0x6F, 0x6D, 0x65]
- data: [0x44, 0x41] # User control pressed: volume up
then:
- logger.log: "Volume up"
You can use the hmdi_cec.send
action from anywhere you can use actions.
button:
- platform: template
name: TV Turn On
on_press:
- hdmi_cec.send:
source: 0x05 # Optional since we'll default to `hdmi_cec.address`
destination: 0x0
data: [ 0x04 ]
loop
is called and thus this is sensitive to other things the microcontroller are doing that may delay the loop
method getting called. I've implemented pin change interrupts which makes receive more reliable but more work needs to be done to make this entirely interrupt driven. The ESP32 will probably work better as-is, though I haven't tried it yet.