Raspberry Pi + スマートプラグでFire TVリモコンをPCモニタに対応させる

新しいPCモニタとFire TV stickを購入しました。ウキウキでセットアップしたものの少し問題が。 PCモニタではFire TVのリモコンで音量調整やモニタの電源のオンオフができません。買い物はちゃんと調べてからしようという教訓ですね。

リモコンでの音量調整はBluetoothスピーカーをペアリングさせることで可能になりましたが、モニタの電源切替はなんとかするしかないということで、スマートプラグを用意してRaspberry Pi経由で操作できるようにしました。

インターネット上には似た記事がたくさんありますが、備忘録兼2024年版として残しておきます。

今回の構成

機材 使ったもの
リモコン Alexa対応音声認識リモコン(2021年発売 第3世代)
スマートプラグ TP-Link Tapo P105
赤外線受信モジュール OSRB38C9AA
ボード Raspberry Pi 3B

下図のような状態を作ります。リモコンの赤外線を受信すると、Raspberry PiがTapo P105の状態を操作します。

ざっくり図解

今回は安かったのでTapo P105を使っています。ライブラリありそうだし安いしと思って適当に買ったら、APIプロトコルが変更されていて見ていた記事が役に立たなくなっており、今使えるライブラリを探すのが大変でした。買い物はちゃんと調べてからしようという教訓ですね。

準備

プログラムを動かす前にいくつか準備をします。

Tapo P105の設定

P105をアプリ経由でwifiに接続して動作を確認し、ルータ上でローカルIPを固定します。 IPがずれるとめんどくさいので、ちゃんと固定します。

ライブラリ等の準備

GPIOを利用するためにpigpio、P105操作用のpythonライブラリとしてtapoをインストールし、赤外線受信モジュールを扱うためにIR Recieverをダウンロードしておきます。

pigpioは自動起動してくれるようにデーモン化しておきます。

sudo apt install pigpio
sudo systemctl enable pigpiod
sudo systemctl start pigpiod

tapoをpipで入れようとしたら仮想環境にしろと怒られたので仮想環境でやります。

python3 -m venv env
source env/bin/activate
pip install tapo

tapoはサンプルがGithubにあるのでそちらも参考にしてください。

IR Recieverはpigpio libraryからダウンロードして、ir_hasher.pyを取り出しておきます。

赤外線受信モジュールの接続

OSRB38C9AAをRaspberry Piに接続します。 データシートを見て、OUTPUTピンを適当なGPIOピンに接続し、電源を通してください。

その後、pigpioで扱えるようにGPIOピンを有効化しておきます。

echo 'm 17 r pud 17 u' > /dev/pigpio
sudo systemctl restart pigpiod

今回はGPIO17に接続しました。ひとまず繋がっていればOKです。

繋がっているのでOK

プログラム

今回は以下のプログラムを用意しました。サンプルの寄せ集めのような見た目をしていますが、サンプルの寄せ集めです。 ホームディレクトリの下に申し訳程度にディレクトリを作って放り込んでいますが、環境に合わせてください。

 #!/usr/bin/env python3

import time
import pigpio
import ir_hasher
import asyncio
from tapo import ApiClient

last_executed = 0

async def toggleP105():
    client = ApiClient("<MailAddress>", "<Password>")
    device = await client.p105("<TapoP105のIP>")
    info = await device.get_device_info()
    if info.device_on:
        await device.off()
    else:
        await device.on()

def callback(hash):
  global last_executed
  execute_time = int(time.time() * 1000)

  MONITOR_POWER = 394093871

  if execute_time - last_executed > 3000:
    last_executed = execute_time
    if hash == MONITOR_POWER:
      loop = asyncio.new_event_loop()
      loop.run_until_complete(toggleP105())
      loop.run_until_complete(asyncio.sleep(0.1))
      loop.close()

if __name__ == "__main__":
  pi = pigpio.pi()
  ir = ir_hasher.hasher(pi, <OSRB38C9AAのGPIO番号>, callback, 5)

  try:
    while True:
      time.sleep(5)

  except KeyboardInterrupt:
    pass

  pi.stop()

MONITOR_POWER に意味不明な値が固定されていますが、我が家のFire TVリモコンの電源ボタンのハッシュ値がこれみたいです。 callback 関数内に print("hash={}".format(hash)); の1行を入れることで、望むリモコンの望むボタンのハッシュ値を取得できるのでそれで置き換えることをおすすめします。

また、連打しておかしくなっても嫌なので、一度切り替えたら3秒以内はもう一度押しても反応しないようにしています。

このプログラムも自動起動してくれるようにデーモン化しておきます。

[Unit]
Description=toggleTapo

[Service]
ExecStart=/home/<username>/TapoP105/env/bin/python3 /home/<username>/TapoP105/toggleTapo.py
Restart=always
Type=simple

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable toggle_tapo
sudo systemctl start toggle_tapo

完成

そういうわけでちゃんと動いた!サクッと作れてかなり楽でした。

スマートプラグがあるならスマホでオンオフの切り替えができますが、スマホを持ってアプリを起動してボタンを押して…とやるよりも、手元のボタン一つで切り替えられるほうが圧倒的に楽です。赤外線受信モジュールの反応も思ったより良く、普通のテレビと同じ感覚で、ある程度離れた位置から適当な方向にリモコンを向けてもちゃんと反応してくれます。1個50円とは思えませんね。

Fire TVは自身がスリープモードになっても接続されているモニターへの出力を止めないのでいちいちモニターをオフにする必要があるというのも聞いていたので、電気代節約にもなって嬉しいです。

今後は使い道がなかったところから元気を取り戻したRaspberry Piと一緒にFalloutのドラマをゆっくり見ようと思います。それでは。