サイネージ HDMI CEC 連携
カレンダーを表示するおうちサイネージ というのを作っていたがモニタの置き場がなくなってしまったので、テレビ画面に映すことにしてみた。
Google TV と Switch でしか使わないので、普段は基本的に Google TV のリモコンで操作している。しかし Raspberry Pi の HDMI を繋いで外部入力をそちらにすると、Google TV のリモコンからの操作 (HDMI CEC信号) をテレビ(SONY BRAVIA)が無視するという困った挙動になった。
試行錯誤してみると Raspberry Pi を HDMI CEC デバイスとして単に参加させる (以下のように)と、他の機器の CEC 信号も無視しなくなるようだった。
# 再生デバイスとして CEC デバイスのネットワークに参加
cec-ctl --playback
他のデバイスのCEC信号を受けて自動表示
ここまではいいとして、カレンダーアプリはある程度自動的に起動してくれないと見ないため、Google TV のリモコンから電源をオフにすると、自動的に Raspberry Pi のカレンダーを表示する、という挙動にしたくなった。cec-ctl --monitor で HDMI CEC のイベントをストリーム取得できるようなので、これを利用してフックし、他の機器からのSTANDBYがきたら、Raspberry Pi 側に画面を切り替えるという処理を入れた。
import subprocess
import time
import re
import sys
CEC_ADAPTER_INIT_CMD = ["/usr/bin/cec-ctl", "--playback"]
CEC_MONITOR_CMD = ["/usr/bin/cec-ctl", "--monitor"]
TV_ON_CMDS = [
["/usr/bin/cec-ctl", "--to", "0", "--image-view-on"],
["/usr/bin/cec-ctl", "--to", "0", "--active-source", "phys-addr=4.0.0.0"]
]
DEBUG = True
def log(msg):
"""ログ出力(デバッグ時のみ)"""
if DEBUG:
print(f"[{time.strftime('%H:%M:%S')}] {msg}", flush=True)
def run_cmd(cmd):
"""単発コマンド実行"""
try:
subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
log(f"Executed: {' '.join(cmd)}")
except subprocess.CalledProcessError as e:
log(f"Command failed: {cmd} ({e})")
def main():
log("Initializing CEC adapter...")
run_cmd(CEC_ADAPTER_INIT_CMD)
log("Starting CEC monitor...")
proc = subprocess.Popen(
CEC_MONITOR_CMD,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True
)
last_device_standby = 0
try:
for line in proc.stdout:
line = line.strip()
if not line:
continue
if re.search(r"Received from Playback Device", line) and "STANDBY" in line:
log("Detected STANDBY from another playback device")
time.sleep(3)
for cmd in TV_ON_CMDS:
run_cmd(cmd)
except KeyboardInterrupt:
log("Interrupted by user")
finally:
proc.terminate()
log("Monitor stopped")
if __name__ == "__main__":
if sys.version_info < (3, 6):
print("Python 3.6+ is required", file=sys.stderr)
sys.exit(1)
main()
これを systemd にいれておく
sudo vi /etc/systemd/system/cec-init.service
[Unit]
Description=Configure HDMI CEC playback mode
After=graphical.target
Wants=graphical.target
[Service]
Type=oneshot
ExecStart=/home/pi/cec.py
RemainAfterExit=true
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable cec-init.service
sudo systemctl start cec-init.service
sudo systemctl status cec-init.service
● cec-init.service - Configure HDMI CEC playback mode
Loaded: loaded (/etc/systemd/system/cec-init.service; enabled; preset: enabled)
Active: activating (start) since Sat 2025-10-18 14:59:08 JST; 11min ago
Main PID: 2820 (python3)
Tasks: 2 (limit: 754)
CPU: 371ms
CGroup: /system.slice/cec-init.service
├─2820 python3 /home/pi/cec.py
└─2823 /usr/bin/cec-ctl --monitor
Oct 18 14:59:08 sinage systemd[1]: Starting cec-init.service - Configure HDMI CEC playback mode...
Oct 18 14:59:08 sinage cec.py[2820]: [14:59:08] Initializing CEC adapter...
Oct 18 14:59:09 sinage cec.py[2820]: [14:59:09] Executed: /usr/bin/cec-ctl --playback
Oct 18 14:59:09 sinage cec.py[2820]: [14:59:09] Starting CEC monitor...
Oct 18 15:00:42 sinage cec.py[2820]: [15:00:42] Detected STANDBY from another playback device
Oct 18 15:00:45 sinage cec.py[2820]: [15:00:45] Executed: /usr/bin/cec-ctl --to 0 --image-view-on
Oct 18 15:00:45 sinage cec.py[2820]: [15:00:45] Executed: /usr/bin/cec-ctl --to 0 --active-source phys-addr=4.0.0.0
Oct 18 15:08:43 sinage cec.py[2820]: [15:08:43] Detected STANDBY from another playback device
Oct 18 15:08:46 sinage cec.py[2820]: [15:08:46] Executed: /usr/bin/cec-ctl --to 0 --image-view-on
Oct 18 15:08:46 sinage cec.py[2820]: [15:08:46] Executed: /usr/bin/cec-ctl --to 0 --active-source phys-addr=4.0.0.0 - トップ
- tech
- Raspberry Pi から HDMI-CEC コントロール