📺

BRAVIAをコントロールしよう

2023/06/29に公開

リモコンが遠い

リモコンって必要なときには見つからない。あるあるですよね。
うちのTVはソニーのBRAVIAなのでVideo & TV SideView: Remoteなるアプリケーションが使えます。iOSとAndroidどちらにも対応してるアプリケーションです。
公式アプリなのでもちろん使えるんですがiPhoneはリモコンとして使うにはちょっと操作しにくい部分があって使いにくいんですよね。
例えば

  • iPhoneの画面で操作するので触覚的にフィードバックがないから押したかわからない
  • 他のアプリを使ってるときにいちいち切り替えないといけない
  • バックグラウンドで放置すると再接続しないといけない

など、いくつか使いにくいポイントが有りました。
そもそもリモコン機能以外に番組表を見たり、録画予約、家の外からTVを見たりするというアプリケーションなのでリモコンとしてだけ使うというアプリじゃないのかもしれません。
でも自分は別にリモコン以外いらないんですよね。なんだったらYouTubeの広告を飛ばす決定ボタン以外はそんなに押しません。

BRAVIAにはAPIがある

色々調べていくうちに法人向けBRAVIAにはREST APIなどわかりやすいAPIが生えていることがわかりました。
うちのは法人向けではないのですが、家庭用にも同じAPIが生えているようなのでこれを叩けば色々面白いことができると考えました。
どうやらREST API、IRCC-IP、簡易IPコントロールという3つに対応しているようです。後者2つはソニー独自かもしれません。

コントロールしよう

前準備

TVにアクセスするわけなので、IPアドレスを固定しました。
また、認証キーを設定するためにTV側の設定が必要です。
設定->ネットワークとインターネット->ホームネットワーク->IPコントロール の認証で設定値をNomal and Pre-Shared Keyに変更しましょう。
また、Pre-Shared Keyから認証キーの設定もしましょう。
ここで設定した認証キーは後で必要になります。
ホームネットワークにあるレンダラー設定のレンダラー機能も有効にしてください。

通信テスト(REST API)

まずは通信できるかどうかチェックしましょう。
電源のオンオフが見た目で一番わかりやすくリクエストが通ったのか確認できると思ったので、最初は電源のオンオフで試してみます。

ヘッダーに

Content-Type: application/json
X-Auth-PSK: {your_pre_shared_key}

を設定し、ボディにはJSONで

{
  "method": "setPowerStatus",
  "id": 55,
  "params": [{"status": false}],
  "version": "1.0"
}

http://{your_bravia_address}/systemにPOSTしてみましょう。TVの電源が切れるはずです。statusをtrueにすると電源が入ります。

これでとりあえず通信ができることは確認できました。ただ本当にやりたいことはリモコンの決定ボタンを押すのと同じ動きをすることです。
REST APIのAPIリストを見ても決定ボタンを押すのと同じ意味のAPIが見つかりません。どうやらIRCC-IPというAPIを使ってリモコンのエミュレートができるようです。

通信テスト(IRCC-IP)

IRCC-IPなるものを知らなかったのですが、検索してもBRAVIAに関係するページかカナダの移民局についてのページしか出てきません。おそらくソニー独自のプロトコルでしょう。

「IRCC-IP」は、「InfraRed Compatible Control over Internet Protocol」を意味します。
IP 経由で赤外線リモコンのコマンドコードを送信できます。

引用元:BRAVIA Professional Displays Knowledge Center

リモコンの決定ボタンを押したいので、やりたいことにぴったりです。これを使いましょう。

こちらはSOAPを使うようです。なのでヘッダーに

Content-Type: text/xml
SOAPACTION: "urn:schemas-sony-com:service:IRCC:1#X_SendIRCC"
X-Auth-PSK: {your_pre_shared_key}

ボディにxmlで

<s:Envelope
    xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
    s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <s:Body>
        <u:X_SendIRCC xmlns:u="urn:schemas-sony-com:service:IRCC:1">
            <IRCCCode>AAAAAQAAAAEAAAAVAw==</IRCCCode>
        </u:X_SendIRCC>
    </s:Body>
</s:Envelope>

http://{your_bravia_address}/sony/irccにPOSTしてみましょう。電源が入ったり切れたりするはずです。あくまでボタンを押しているのと同じなのでトグル動作になるはずです。

実際にやりたいことをIRCCCodeタグに記載します。IRCCコードを参考に決定ボタンなのでConfirmを設定します。

コントローラーを作る

今までの手順でhttpリクエストを送ることによってリモコンの動作をリモコンなしで操作する事はできました。
ただこれではPCの前から動けません。意味がないので操作するためのデバイスを作りましょう。

そんなにお金をかけたくないので、既存のあまり高価ではないデバイスを使いたいと思います。
最近の100円ショップはBluetooth接続のリモートシャッターが売っています。これを流用しましょう。多分200-300円で買えます。

あとは皆様の家にあるBluetoothのつながるデバイスと連動させましょう。
自分は部屋にRaspberry Pi 3 Model B+が落ちていたのでこれを使いたいと思います。

自分的にはラズパイを使うときはpythonでコードを書くということを何故かしているので今回もpythonで書きます。
使いたいのはevdevなのでLinuxで動かす事ができる言語ならなんでもいいんじゃないかと思います。

接続前にls -la /dev/inputで今繋がっているデバイスを確認しましょう。
確認できたらペアリングしましょう。方法はGUIとCUIで違うのでここでは記載しません。環境にあった方法でペアリングしてください。
接続できたら再度ls -la /dev/inputで今繋がっているデバイスを確認しましょう。増えたeventXが接続したリモートシャッターです。
これ以外にも確認方法はいくつかありますが、event番号を取得することができれば問題ありません。

pythonのコード内でevdev,requestsを使用するので、インストールします。

$ sudo pip3 install evdev requests

では徐ろにコードを書き始めます。まだこのリモートシャッターのボタンが押されたときにどんなキーコードが出力されるのかわかっていません。調査のために以下のコードを動かしてみます。

test.py
#!/usr/bin/python3

import evdev
device = evdev.InputDevice('/dev/input/event0')
print(device)
for event in device.read_loop():
    if event.type == evdev.ecodes.EV_KEY:
        print(event)

コードを実行すると以下のような結果が出力されます。

 $ python3 test.py

device /dev/input/event0, name "BT Shutter Consumer Control", phys "B8:27:EB:C5:5C:C2"
event at 1688038047.608829, code 115, type 01, val 01
event at 1688038047.833777, code 115, type 01, val 00
event at 1688038048.283790, code 115, type 01, val 01
event at 1688038048.508735, code 115, type 01, val 00
event at 1688038048.846344, code 115, type 01, val 01
event at 1688038049.033789, code 115, type 01, val 00
event at 1688038049.258766, code 115, type 01, val 01
event at 1688038049.446316, code 115, type 01, val 00

このボタンを押すとcode 115が出力されていることがわかります。valはkeydownが01、keyupが00になっています。
ボタンが押されたときにAPIを叩くようにするので以下のようなコードにしました。

button.py
#!/usr/bin/python3

import evdev
import requests

def requestBravia():
    url = "http://{your_bravia_address}/sony/ircc"

    payload = "<s:Envelope\n    xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\n    s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n    <s:Body>\n        <u:X_SendIRCC xmlns:u=\"urn:schemas-sony-com:service:IRCC:1\">\n            <IRCCCode>AAAAAQAAAAEAAABlAw==</IRCCCode>\n        </u:X_SendIRCC>\n    </s:Body>\n</s:Envelope>"
    headers = {
      'X-Auth-PSK': {your_pre_shared_key},
      'SOAPACTION': '"urn:schemas-sony-com:service:IRCC:1#X_SendIRCC"',
      'Content-Type': 'application/xml'
    }

    response = requests.request("POST", url, headers=headers, data=payload)
    if response.status_code != requests.codes.ok:
        print('BAD STATUS!!')

while True:
    try:
        device = evdev.InputDevice('/dev/input/event0')
        print(device)
        for event in device.read_loop():
            if event.type == evdev.ecodes.EV_KEY:
                if event.code == 115 and event.value == 1:
                    requestBravia()
    except:
        time.sleep(1)

以上のようなコードになりました。
requests部分はPostmanのコード変換を使用しました。payloadがものすごいことになっていますが許してください。

参考文献

BRAVIA Professional Displays Knowledge Center
100均Bluetoothボタンをラズパイ活用

Discussion