サイネージ 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 側に画面を切り替えるという処理を入れた。

 #!/usr/bin/env python3
 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
 
             # 他のデバイスが STANDBY 送信
             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
  1. トップ
  2. tech
  3. Raspberry Pi から HDMI-CEC コントロール
▲ この日のエントリ