Amazon Echoでテレビの電源を操作する

Google HomeだとChromecastを使ってテレビの電源を操作できると聞いて、なんで出来るんだろうと思っていたら、HDMIにそういう仕様があることを教えてもらった。

## HDMI CEC

http://hometheater-navi.com/cable/about-hdmi-cec.html

信号の規格を決めることで機器間で操作できるようなしくみらしい。
我が家のテレビはブラビアなので、ブラビアリンクとかがそれにあたるらしい。

Amazon EchoからChromecastはいじれない（たぶん）けど、ラズパイにHDMI付いてるし、これでテレビの電源を操作できるんじゃないたと思った。

## 完成品が動いてる所

[![Alexa Smart Home Skill Sample](http://img.youtube.com/vi/SWgoA-lQKj8/0.jpg)](http://www.youtube.com/watch?v=SWgoA-lQKj8)

サンプルソースはこちら
https://github.com/sparkgene/alexa-tv-controller

## 全体の流れ

<img width="969" alt="スクリーンショット 2018-01-27 18.26.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/bf389571-3310-5018-bfaf-781b527a69e3.png">

1. Echoに「テレビを付けて」と話しかける
2. スマートホームスキルが実行され、Lambdaに`Alexa.PowerController`の`TurnOn`イベントが送られる
3. LambdaからAWS IoTのdevice shadowを更新する
4. Raspberry Piがdevice shadowの更新を受け取る
5. `cec-client` を使ってテレビに電源ONの信号を送る

## ラズパイ側

ラズパイで使えるライブラリがあるってことで、それをインストールするだけで使える。

```
sudo apt-get install cec-utils
```

電源ON

```
$ echo 'on 0' | cec-client -s

opening a connection to the CEC adapter...
DEBUG:   [             125]	Broadcast (F): osd name set to 'Broadcast'
DEBUG:   [             126]	Open - vc_cec initialised
DEBUG:   [             126]	logical address changed to Free use (e)
NOTICE:  [             126]	connection opened
DEBUG:   [             127]	processor thread started
DEBUG:   [             127]	<< Broadcast (F) -> TV (0): POLL
DEBUG:   [             127]	initiator 'Broadcast' is not supported by the CEC adapter. using 'Free use' instead
TRAFFIC: [             127]	<< e0
DEBUG:   [             187]	>> POLL sent
DEBUG:   [             187]	TV (0): device status changed into 'present'
DEBUG:   [             187]	<< requesting vendor ID of 'TV' (0)
TRAFFIC: [             187]	<< e0:8c
TRAFFIC: [             382]	>> 0f:87:08:00:46
DEBUG:   [             382]	TV (0): vendor = Sony (080046)
DEBUG:   [             383]	>> TV (0) -> Broadcast (F): device vendor id (87)
DEBUG:   [             383]	expected response received (87: device vendor id)
NOTICE:  [             383]	registering new CEC client - v4.0.2
DEBUG:   [             383]	detecting logical address for type 'recording device'
DEBUG:   [             383]	trying logical address 'Recorder 1'
DEBUG:   [             383]	<< Recorder 1 (1) -> Recorder 1 (1): POLL
T
いっぱい出るので、この辺で
```

電源OFF

```
$ echo 'standby 0' | cec-client -s

opening a connection to the CEC adapter...
DEBUG:   [             145]	Broadcast (F): osd name set to 'Broadcast'
DEBUG:   [             146]	Open - vc_cec initialised
DEBUG:   [             146]	logical address changed to Free use (e)
NOTICE:  [             146]	connection opened
DEBUG:   [             147]	processor thread started
DEBUG:   [             147]	<< Broadcast (F) -> TV (0): POLL
同じくいっぱい出るので、この辺で
```

と言うことで、この2つのコマンドで電源のON/OFFができる。
ちなみに、テレビ側の設定でCECを利用するになっていないと動かないので、最初にテレビの設定画面で変更しておく。

### AWS IoTの設定

AWS IoTでは、Thingの作成、証明書の作成、Policyの作成など行います。
大体の流れはここにあるとおりです。
https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/iot-gs.html

Thing名はサンプルをそのまま使うなら、`tv-controller`で作成します。
Policyを作る所は、ハマると面倒くさいので一旦全部OKにします。

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:*",
      "Resource": "*"
    }
  ]
}
```

証明書はOneClickで作成されたものをRaspberryPiで使うので、3種類ともダウンロードしておく。
Thingが作れたら、shadowの所を以下のJSONで置き換える

```json
{
  "desired": {
    "command": "tv_off",
    "counter": 0
  }
}
```
あとで作るLambda Functionでは、このcommandの所を書き換えることで、ラズパイの方でもその情報を受け取ることが出来、コマンドを実行します。

## ラズパイの設定

リポジトリのソースをcloneします

```
cd /home/pi
git clone https://github.com/sparkgene/alexa-tv-controller.git
cd alexa-tv-controller/raspberrypi
```

AWS IoTの画面でダウンロードした3つの証明書を`certs`格納します。
この時、ユニークな数字がファイル名についているのですが、それを以下のように消します。

- certificate.pem.crt
- private.pem.key
- public.pem.key

AWS IoTのエンドポイントをAWSマネージメントコンソールで確認し、自分のエンドポイントに書き換えて下さい。 `host: "your-endpoint.iot.ap-northeast-1.amazonaws.com"` の所。

```shadow_client.js
var shadowName = "tv-controller"
var thingShadows = awsIot.thingShadow({
   keyPath: "/home/pi/alexa-tv-controller/raspberrypi/certs/private.pem.key",
  certPath: "/home/pi/alexa-tv-controller/raspberrypi/certs/certificate.pem.crt",
    caPath: "/home/pi/alexa-tv-controller/raspberrypi/certs/ca.pem",
  clientId: "tv-controller",
    region: "ap-northeast-1",
    host: "your-endpoint.iot.ap-northeast-1.amazonaws.com"
});
```

AWS IoTに必要なnodeのライブラリなどをインストールするので、以下のコマンドを実行します。

```
sh setup.sh
```

CAの証明書がダウンロードされ、必要なライブラリがインストールされます。

以下のコマンドでクライアントを実行します。
正しく繋がると、現在のshadowの値を拾ったログが出力されます。

```
$ /usr/bin/node shadow_client.js

connected
registered
received accepted on tv-controller: {"state":{"desired":{"command":"tv_off","counter":1517043288},"reported":{"counter":1517043288,"command":"tv_off"}},"metadata":{"desired":{"command":{"timestamp":1517043289},"counter":{"timestamp":1517043289}},"reported":{"counter":{"timestamp":1517043293},"command":{"timestamp":1517043293}}},"version":26,"timestamp":1517046963}
counter:1517043288
no change do nothing
```

この状態で、AWS IoTの画面で、shadowの値を `"command": "tv_on"`にすると、テレビの電源が入り、 `"command": "tv_off"` にすると電源が切れます。

## Smart Home Skillの作成

スマートホームスキルは日本語のペイロードのバージョンv3で作成します。
アカウントリンキングが必須なので、Login with Amazonを使ってOAuthプロバイダの設定をします。
途中でLambdaのARNを聞かれますが、そこにはこのあと作成するLambdaのARNを設定します。

前に書いた記事を参考にしてもらえれば、スキルの設定はできるかと思います。
https://qiita.com/sparkgene/items/055d7864c92a80b0c040#login-with-amazonlwa-security-profile%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B

## Lambdaファンクションの作成

サンプルソースをそのまま利用します。
https://github.com/sparkgene/alexa-tv-controller/blob/master/lambda_function/lambda_function.py

LambdaのRoleはAWSIoTを利用できるように以下のようなRoleを作成します。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "iot:GetThingShadow",
                "iot:UpdateThingShadow"
            ],
            "Resource": "*"
        }
    ]
}
```

Lambdaが作成できたら、以下のJSONをテストイベントに設定して、実行するとテレビが付きます。

```json
{
  "directive": {
    "header": {
      "namespace": "Alexa.PowerController",
      "name": "TurnOn",
      "payloadVersion": "3",
      "messageId": "sss",
      "correlationToken": "ccc"
    },
    "endpoint": {
      "scope": {
        "type": "BearerToken",
        "token": "aaa"
      },
      "endpointId": "my-living-tv",
      "cookie": {}
    },
    "payload": {}
  }
}
```

`TurnOn` を `TurnOff` に変えて試せば、テレビの電源が落ちます。

## Echoから試す

ここまで動作確認が取れていれば、後はEchoから実際に試すだけです。

Alexa Appで追加したスマートホームスキルが存在するか探します。

<img width="342" alt="スクリーンショット 2018-01-27 17.37.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/1ea31422-b33b-6f63-7850-180057e678ec.png">

スキルの詳細に入ると、「有効にする」ボタンがあるので、クリックします。すると、Amazonのログインを促されるので、Alexa Appと同じアカウントでログインします。

<img width="363" alt="スクリーンショット 2018-01-27 17.37.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/6b6c93ae-4300-bc41-a06f-ecb109c0138e.png">

無事リンクされました。

<img width="822" alt="スクリーンショット 2018-01-27 17.38.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/4c66b009-6dd1-2d37-da3d-125190d347f8.png">

スマートホームデバイスを探しに行くので、「端末の検出」をクリックします。

<img width="636" alt="スクリーンショット 2018-01-27 17.38.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/61784dca-1c6a-9713-1956-5626df785703.png">

しばらくすると、「リビングのテレビ」が見つかります。

<img width="529" alt="スクリーンショット 2018-01-27 17.40.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/0116ae8e-295a-7f8d-d600-54e1ae56e1f4.png">

これで、Echoから使えるようになりました。

- アレクサ、リビングのテレビを付けて
- アレクサ、リビングのテレビを消して

と話しかけると、操作できます。

## あとがき

いやー、最高ですね。
テレビが付くのに時間がかかるのは、テレビ側が起動するのが遅いだけですが、中々便利です。
後は、チャンネルを変えられると良いのだけど、それ用の機能がまだ無いので、 `Alexa.BrightnessController` あたりを使って、 `アレクサ、リビングのテレビの明るさを8%にして`みたいな感じでチャンネルを指定できるように出来そう。（発話がいまいちだけど）


## 参考記事

- https://qiita.com/mascii/items/132c064c30e321dea911
- http://min117.hatenablog.com/entry/2017/09/18/161636