Amazon Echo で TVリモコン操作への道 (RPizero & AWSIoT & Lambda & AlexaSkill) 後編

前回の記事
[Amazon Echo で TVリモコン操作への道 (RPizero & AWSIoT & Lambda & AlexaSkill) 前編](https://qiita.com/toguma/items/57d6faed119c41bd4332)
からの続き。

- RaspberryPiでのリモコン送信機の作成
- AWS IoTとの接続
- AWS Lambdaからの操作

ここまでが完了している前提。
ここからはいよいよ本丸のAlexaSkillの作成と連携。
しかも先日ついに日本語バージョンがリリースされたので、日本語での操作を前提とします。

#1 構成

##1.1 全体構成

![alexa-qiita2.jpg](https://qiita-image-store.s3.amazonaws.com/0/89940/f652f610-787f-861b-a26e-a0e9066ea4ec.jpeg)


ピタゴラスイッチ並みの連携。
Amazon Echo -> Alexa Skill への接続が目標。

AlexaSkillでテレビリモコンを実現するには、
テレビや証明などの公式の製品、デバイス向けに標準化された
- [スマートホームスキル](https://developer.amazon.com/ja/docs/smarthome/understand-the-smart-home-skill-api.html)
- [カスタムスキル](https://developer.amazon.com/ja/docs/custom-skills/understanding-custom-skills.html)
での実現が可能。

今回はより柔軟で、汎用性の高い、カスタムスキルにて構築を前提とする。
実はスマートホームスキルで当初進めていたのだが、使い勝手の問題や日本語化未実装の部分があったりと、やりたいことの実現が今の自分の技術では難しそうだったので、カスタムスキルに変更した経緯もあり。。
この辺りは機会があれば後々紹介しようかと思います。

##1.2 操作対象

今回実現したいアレクサ向けの命令セット

- 電源のON/OFF
    - 電源をつけて/オンにして 、 電源を消して/オフにして
- 音量UP/Down
    - 音量をあげて/大きくして 、 音量をさげて/小さくして
- チャンネル操作(局名）
    - フジテレビにして/フジテレビに変えて 、 （日テレ/TBS/テレ朝/テレ東)
- チャンネル操作(チャンネル番号)
    - 8にして/8に変えて 、 (4/6/5/7)


#2 Alexa開発ツール

##2.1 Amazon 開発コンソールへログイン

AlexaのSKill開発には[Amazon Developer](https://developer.amazon.com/ja/)にアカウント登録が必要。
アカウント作成は無料なのでサクッと登録。

<img width="1379" alt="スクリーンショット 2017-11-26 02.34.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/02454b57-6a48-332c-efaa-33adccbb81f5.png">

ログイン後、Alexaのタブから「Alexa Skills Kit」を選択

<img width="681" alt="スクリーンショット 2017-11-26 02.35.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/1306a161-fed3-2bd3-779f-60e77fe3697d.png">

##2.2 新しいスキルを追加

「新しいスキルを追加する」から新規作成
<img width="1234" alt="スクリーンショット 2017-11-26 02.38.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/f7c6aef8-f4fc-f98e-7673-81e237736f49.png">
　
　


##2.3 スキル情報の入力

スキルの種類 : カスタム対話モデル
言語 : Japanese
スキル名 : myHomeTV-Ctrl (任意の名前)
呼び出し名 : テレビリモコン ※

※この呼び出し名が、AlexaへのSkill発動文言となる。
　

<img width="1046" alt="スクリーンショット 2017-11-26 02.54.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/3e71b6ea-b91c-d03c-eb58-281ad7d3f0d4.png">


##2.4 対話モデル

対話モデルに進むと、Builderを使うかどうかの選択を迫られる。
（まだBATA扱いなので選択可能だが、今後はこちらが標準となるかもしれない。）
今回はBuilderを使って作成する。

<img width="1031" alt="スクリーンショット 2017-11-26 02.56.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/750ec192-254b-4e98-0384-4d1f614f5c19.png">


Builderを開くと、いきなりUIが変わり初めは面食らうが、、そのうち慣れるので気にしない。


<img width="681" alt="スクリーンショット 2017-11-26 02.57.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/9c4bc264-dc9e-4205-4a85-0600917b49c7.png">
　

**slotの追加**

slotとはAlexaから、Lambda側に引き渡す際の変数定義、のようなもの。
電源のオフ、オンであれば、
電源を（オフ / オン） の　（）の部分を変数として定義して、Lambda側での処理に利用する。

ここでは
slot : power
として定義。


<img width="794" alt="スクリーンショット 2017-11-26 03.01.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/53d34abb-33c9-00fd-1942-cfa565bb96c6.png">

　

また、power に想定されるvalue=値（この場合文言）を定義しておく。

ちなみに、SYNONYMSとは類義語？のようなもので、同じ意味合いの別単語を登録しておくと良いらしい。（、、ただ、私自身まだよくわかってない、、）

<img width="978" alt="スクリーンショット 2017-11-26 03.02.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/b7e824fb-b4a9-10eb-51a9-fe9079549fba.png">

　
　

**Intentsの追加**
次にIntentsの追加。ドキュメントによれば
*インテントとは、ユーザーの音声によるリクエストを満たすアクション*
とのこと。

Alexaの入力と、LambdaへのSlot引き渡しの架け橋、のような位置付けのものと考えていいかと。

動作毎に1インテントを作成するとまとまりが良い。
電源 = tv_switch
音量 = tv_vol
局名指定 = tvch_name
など。

<img width="813" alt="スクリーンショット 2017-11-26 03.00.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/5470460a-7e59-1206-19fe-a5e7f925d1c8.png">
　

Sample Utterances　= サンプル発話集の定義。
想定される発話フレーズセットを登録する。
不特定多数の人向けのSkillなら想像つく限りのフレーズを入れるんだろうけど、今回の用途だとあくまで個人利用前提なので、自身が話しかける予定のフレーズセット登録のみとする。

電源の場合のサンプル発話は

- 電源を {power} = (つけて・オンにして）

のみ。
ちなみに、フレーズとSlotの間に**スペースを入れない**と、**Build Modelで失敗**するので、注意。
上記の場合は「電源を」 と「 {power} 」の間。
（私はこれで悩んだ。。） 

<img width="979" alt="スクリーンショット 2017-11-26 03.02.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/c4086c5c-eebd-b315-9c18-51a9ced82df9.png">

　


作成したインテントに関連付けたSlotの「Slot Type」を、指定したものに合わせる。
ここも指定し忘れると、Save自体で失敗するので注意。

<img width="725" alt="スクリーンショット 2017-11-26 03.03.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/c9b4ffcd-acdb-6225-6cc6-d6cfc55a1d08.png">




以上の要領で、同様に音量up/downの動作セットのIntentも登録。

Volome インテント
<img width="978" alt="スクリーンショット 2017-11-26 15.40.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/bf42bdfc-e8da-2e19-e2d5-dc7f5efe1cfb.png">

Volume スロット
<img width="985" alt="スクリーンショット 2017-11-26 15.41.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/0a462ac1-c6ee-81fc-26de-60ad77f7cdbc.png">

　

channelは、
局名(フジテレビ・日テレ・テレ朝、など）で呼ぶ場合と、チャンネル数(8,4,5,6など）で呼ぶ場合があるので、Slotをそれぞれ分ける。

局名指定のインテント・スロット
<img width="983" alt="スクリーンショット 2017-11-26 15.39.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/597a1b31-9220-53da-fe45-2ce0257792d4.png">

スロット名では各局の呼び名を一通り入力。
ちなみに、TBS/NHKなど大文字で入れているが、Lambdaと連携される際には、英小文字になっているのでLambada側での条件分岐の際には注意。

<img width="1000" alt="スクリーンショット 2017-11-26 15.39.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/5382dd25-6625-7059-9eb4-c9386157514e.png">

　

局数指定のインテント・スロット
<img width="983" alt="スクリーンショット 2017-11-26 15.39.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/e4ffb5d0-4bef-aff3-668b-543419db4f0b.png">

単純な数などの場合は、標準提供されているSlot集のうち、AMAZON.NUMBERを利用すると、多少楽。
<img width="969" alt="スクリーンショット 2017-11-26 15.44.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/0e2b2afc-fc0a-8324-5901-9d1bbaafe79d.png">




インテント・スロットの設定が一通り完了したら、「Save Model」して、問題なければ、「Build Model」を行う。
この後設定を替えるたびにSave/Buildを実施する必要がある。

<img width="721" alt="スクリーンショット 2017-11-26 15.52.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/f9b7722e-0e1e-e686-6546-02a27937a550.png">

<img width="649" alt="スクリーンショット 2017-11-26 15.53.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/7b0c745b-5c4d-fb0e-dec1-372551297e3a.png">



これでビルダーでの作業は終了。


#3 AlexaSkill用 Lambda関数の作成

##3.1 Lambdaの作成

2017/11現在、日本語(Japanise)対応のLambdaを設置するリージョンとしては、**オレゴン(us-west-2)**が指定されているので、Lambdaはオレゴンにて作成する。

関数名　: myHomePi-Tv-ctrl
言語　: Python2.7
IAMPolycy : LambdaデフォルトのIAM権限＋AWSIoTDataAccess
mem : 128NB
Timeout : 30s


基本的にはAWS Lambdaのブループリント(テンプレート) **alexa-skills-kit-color-expert-python**　をベースにして作成。
ただ、今回は単純なデバイス操作のため、セッション維持は利用せず、単純に１回の応答処理のみを想定しているので、テンプレートの様なAlexaとの会話を想定している訳ではないので、比較的シンプルになっているかも。


```py:lambda_function.py
# coding: UTF-8

from __future__ import print_function
import json
import boto3

ThingName = "myHomePi"

### Alexaへのresponse用jsonの組み立て関数
def build_speechlet_response(title, output, reprompt_text, should_end_session):
    return {
        'outputSpeech': {
            'type': 'PlainText',
            'text': output
        },
        'card': {
            'type': 'Simple',
            'title': "SessionSpeechlet - " + title,
            'content': "SessionSpeechlet - " + output
        },
        'reprompt': {
            'outputSpeech': {
                'type': 'PlainText',
                'text': reprompt_text
            }
        },
        'shouldEndSession': should_end_session
    }

### Alexaへのresponse用関数
def build_response(session_attributes, speechlet_response):
    return {
        'version': '1.0',
        'sessionAttributes': session_attributes,
        'response': speechlet_response
    }

### Alexa LaunchRequest時のレスポンス用関数
def get_welcome_response():
    """ If we wanted to initialize the session to have some attributes we could
    add those here
    """

    session_attributes = {}
    card_title = "TVリモコン"
    speech_output = "ご命令を"
    # If the user either does not reply to the welcome message or says something
    # that is not understood, they will be prompted again with this text.
    reprompt_text = "やってほしいリモコン操作を教えてください " \
                    "電源をつけて"
    should_end_session = False
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))

### Alexa LaunchRequest時のレスポンス用関数        
def on_launch(launch_request):
    """ Called when the user launches the skill without specifying what they
    want
    """

    print("on_launch requestId=" + launch_request['requestId'])
    # Dispatch to your skill's launch
    return get_welcome_response()
    

### 聞き取れなかった場合の返答用スピーチセット
def retry_voice():
    response = {
        'version': '1.0',
        'response': {
            'outputSpeech': {
                'type': 'PlainText',
                'text':  "よく分かりません、もう一回言って。",
            }
        }
    }
    return response;
    
### コマンド完了後の返答用スピーチセット
def return_voice(state):
    response = {
        'version': '1.0',
        'response': {
            'outputSpeech': {
                'type': 'PlainText',
                'text':  state + "にしました",
            }
        }
    }
    return response;


### AWS IoTデバイスのシャドウ変更用関数   
def send_command(command):
    
    iot_client = boto3.client('iot-data', region_name='ap-northeast-1')
    
    result = iot_client.get_thing_shadow(
        thingName = ThingName
        )
    result_dict = json.loads(result['payload'].read())
 
    if ( result_dict["state"]["desired"][command] == 0 ):
        shadow = {
            'state': {
                'desired': {
                    command: 1
                }
            }
        }
        payload = json.dumps(shadow)
    else:
        shadow = {
            'state': {
                'desired': {
                    command: 0
                }
            }
        }
        payload = json.dumps(shadow)   
    
    response = iot_client.update_thing_shadow(
        thingName = ThingName,
        payload = payload
    )
    return True

### TV電源操作用関数    
def power_command(power):
    
    print(power)
    
    if(power == "つけて" or power =="オンにして" ):
       send_command("tv_on") 
       print("TV power on!" )
       return return_voice("電源をオン")
       
    elif(power == "消して" or power =="オフにして" ):
       send_command("tv_off") 
       print("TV power off!" )
       return return_voice("電源をオフ")
       
    else:
        print("Not Command")
        return retry_voice()

### TVボリューム操作用関数   
def vol_command(volume):
    
    print(volume)
    
    if(volume == "大きくして" or volume =="上げて" or volume == "あげて" ):
       send_command("volup") 
       print("TV volume up!" )
       return return_voice("ボリュームアップ")
       
    elif(volume == "小さくして" or volume =="下げて" or volume == "さげて" ):
       send_command("voldown") 
       print("TV volume down!" )
       return return_voice("ボリュームダウン") 
    
    else:
        print("Not Command")
        return retry_voice()

### TVチャンネル操作用関数    
def channel_command(tvch):
    
    if(tvch == "フジテレビ" or tvch == "フジ" or tvch =="8"):
       send_command("ch8") 
       print("change channel to " + tvch )
       return return_voice("フジテレビ")
       
    elif(tvch == "日テレ" or tvch =="4"):
       send_command("ch4") 
       print("change channel to " + tvch )
       return return_voice("日テレ")

    elif(tvch == "tbs" or tvch == "ティービーエス" or tvch =="6"):
       send_command("ch6") 
       print("change channel to " + tvch )
       return return_voice("TBS")
       
    elif(tvch == "テレビ朝日" or tvch == "テレ朝" or tvch =="5"):
       send_command("ch5") 
       print("change channel to " + tvch )
       return return_voice("テレ朝")
       
    elif(tvch == "テレビ東京" or tvch == "テレ東" or tvch =="7"):
       send_command("ch5") 
       print("change channel to " + tvch )
       return return_voice("テレ東")
       
    elif(tvch == "nhk" or tvch == "エヌエチケー" or tvch =="1"):
       send_command("ch1") 
       print("change channel to " + tvch )
       return return_voice("NHK")
       
    else:
        print("Not Command")
        return retry_voice()

### インテント処理関数
def on_intent(intent_request):
    print(intent_request)
    
    intent = intent_request['intent']
    intent_name = intent_request['intent']['name']
    
    if intent_name == "tv_switch" :
        
        if  str(intent_request['intent']['slots'].get('power')) != "None" :
            print("Chanege TV power.")
            
            if str(intent_request['intent']['slots']['power'].get('value')) != "None":
                power = str(intent_request['intent']['slots']['power']['value'])
                response = power_command(power)
                return response
            else:
                print("Dont't Tv ctrl.")
                response = retry_voice()
                return response
    
    elif intent_name == "tv_vol" :
        
        if  str(intent_request['intent']['slots'].get('volume')) != "None" :
            print("Chanege TV volume.")
            
            if str(intent_request['intent']['slots']['volume'].get('value')) != "None":
                volume = str(intent_request['intent']['slots']['volume']['value'])
                response = vol_command(volume)
                return response
            else:
                print("Dont't Tv ctrl.")
                response = retry_voice()
                return response
    
    elif intent_name == "tvchannel" or intent_name =="tvchannel_num" :
    
        if  str(intent_request['intent']['slots'].get('AMAZON.NUMBER')) != "None" :
            print("Chanege TV channel.")
            
            if str(intent_request['intent']['slots']['AMAZON.NUMBER'].get('value')) != "None":
                tvch = str(intent_request['intent']['slots']['AMAZON.NUMBER']['value'])
                response = channel_command(tvch)
                return response
            else:
                print("Dont't Tv ctrl.")
                response = retry_voice()
                return response
                
        elif str(intent_request['intent']['slots'].get('tvch_name')) != "None" :
            print("Change TV channel.")
            
            if str(intent_request['intent']['slots']['tvch_name'].get('value')) != "None":
                tvch = str(intent_request['intent']['slots']['tvch_name']['value'])
                response = channel_command(tvch)
                return response
            else:
                print("Dont't Tv ctrl.")
                response = retry_voice()
                return response

### メイン処理
def lambda_handler(event, context):
    if event['request']['type'] == "LaunchRequest":
        return on_launch(event['request'])
    elif event['request']['type'] == "IntentRequest":
        return on_intent(event['request'])
        
```


ザックリとした処理内容

- AlexaからSkill呼び出し司令＝「テレビリモコンを開いて」が発生したら、Lambdaに対して、LaunchRequestが発生。
- LambdaはlaunchRequestの場合は、セッション開始応答と、初回スピーチ内容のjsonを返答する。
    - テレビリモコンのスキルが開始したら「ご命令を」と言って、待機状態になる
- ユーザからの発話の種類に応じて、Alexaスキル側でインテントを選択。
    - 電源のオフオン or 音量up/down or チャンネルの変更　のインテントを判定
- Lambda側は各インテント毎に処理を分けて、それぞれで正常処理、エラーハンドリングを行い、結果セットを返す。
- 正常にIoT処理を行なった場合は、〇〇しました、というスピーチセットを返す。指示文言が不明で処理が実行できなかった場合は「もう一度最初から指示してください」というスピーチセットを返し、ユーザにやり直しさせる。


##3.2 Lambda ARNを AlexaSkillに登録
登録したLambdaのARNをメモし、Alexaスキルの「設定」 - グローバルフィールドのデフォルトエンドポイントに入力する。

<img width="1019" alt="スクリーンショット 2017-11-26 15.57.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/9f6ced89-386d-f2a9-0225-83bdfa5ae511.png">


##3.3 Alexa - Lambda間の動作テスト

「テスト」にて、実際にAlexaを経由せずに、ブラウザ上でシミュレーションをすることが可能。

以下の例は、
「電源をつけて」
と発話した場合の応答結果が、どの様にLambdaから返ってくるかを確認している例。
この際にLambda側でエラーが発生し正常処理されないと、レスポンスは返ってこない。

その場合は、通常のLambda関数開発同様にLambdaのエラーログをCloudWatchLogで適宜確認しながらデバックしていく。


<img width="843" alt="スクリーンショット 2017-11-26 16.22.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/05971383-bec8-d78d-5adf-a04a0d83ab25.png">


ここで想定する文言を全て確認しておく。


##3.4 テスト公開準備

以上で、一通りのSkill設定は完了だが、実際にAmazon EchoなどAlexa端末で利用する為には、
公開情報を入力し、「Skill Bata Testing」をアクティブにする必要がある。

まずは公開情報のフィールドに全て入力する。

<img width="818" alt="スクリーンショット 2017-11-26 16.28.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/5d9601a5-29da-1270-f5c2-e2da80099d79.png">
<img width="826" alt="スクリーンショット 2017-11-26 16.29.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/a49403a4-0669-feb9-0103-5cb4a887d8dc.png">
<img width="831" alt="スクリーンショット 2017-11-26 16.29.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/5f5ed515-6cdb-131c-6c68-caf2b4966c84.png">


画像アイコンはとりあえず、指定されたpixel数の画像を用意しておくこと。


プライバシー・コンプライアンス設定を適宜行う。

<img width="830" alt="スクリーンショット 2017-11-26 16.30.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/de09877a-d331-094e-a46a-299bc7e12361.png">

13際未満の子供を対象としている、に「はい」とすると、、テストできないので注意。。


画像登録すると、スキル一覧にも画像が表示される。
<img width="559" alt="スクリーンショット 2017-11-25 23.33.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/0769f811-4b43-01f9-07eb-3b049d814acc.png">

取り急ぎ、会社ロゴ入れたけど、なんの機能だか一目じゃわからないことこの上ないので、
ちゃんとTVリモコンアイコンを入れればよかった。

 

#4 Skillテスト公開

これで以下の通り、今までグレーアウトされていた、スキルの「ベータテスト」が選択可能になっているはず。

<img width="214" alt="スクリーンショット 2017-11-25 23.32.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/00e55997-8447-17b1-99cf-bc4a429e71ef.png">

公開をクリック。
　


##4.1 Skillテスト招待

動かしたいAlexaが登録されているアカウントへ招待メールを送付する。

<img width="1127" alt="スクリーンショット 2017-11-25 23.33.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/fde50686-a0d7-79c8-ea65-fd011f864b25.png">

招待メールが、
「You're invited to beta test a new Alexa skill」というサブジェクトでメールが届くので、その中のリンク、
「JP Customers: To get started.」 のリンクをクリックする。

<img width="375" alt="IMG_3358.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/89940/e541a71a-432e-f380-347a-1e76ca23ca00.jpeg">

<img width="375" alt="IMG_3292.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/89940/4e49cfcd-a928-e669-e59c-817b84c80526.png">


以上で、テスト公開完了。
招待したユーザが管理しているAlexa端末から該当のSkillが使える。
　

ちなみにテスト中はステータスがアクティブとなる。

<img width="207" alt="スクリーンショット 2017-11-28 06.42.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/89940/0d66830a-a217-991f-2b70-c00b07756f73.png">


#5 AlexaでSkill実行

これでAlexa側Skill実装も完了しているので、一通りの準備内容を確認

- RaspberryPiの赤外線送信準備が整っていること(lircdが稼働していること）

```shell-session
pi@raspberrypi:~ $ sudo /etc/init.d/lircd start
[ ok ] Starting lircd (via systemctl): lircd.service.
```

- AWSIoTにデバイス登録が完了している。
- RaspberryPiでAWSIoTとの連携プログラム(Tv.py)がバックグラウンドで稼働していること

```shell-session
pi@raspberrypi:~/python_aws_iot $ python Tv.py 
start readConfig func
start connct shadow
shadow connect
satrt subscribe shadow
```

実際にAlexaから操作した際の実行例

---

<font color="Blue">ユーザー：アレクサ、テレビリモコンを開いて。</font>

**カスタムスキル発動**

<font color="Red">アレクサ： 「ご命令を。」</font>

<font color="Blue">ユーザー： 電源をつけて。</font>

**TVが付く**

<font color="Red">アレクサ：「はい、電源をつけました。」</font>

---

<font color="Blue">ユーザー： アレクサ、テレビリモコンを開いて。</font>

<font color="Red">アレクサ： 「ご命令を」</font>

<font color="Blue">ユーザー： フジテレビに変えて。</font>

**フジテレビに変わる**

<font color="Red">アレクサ： はい。</font>

<font color="Red">アレクサ：　「フジテレビに変えました。」</font>

---

サンプル動画置いてみました。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Qiita Alexaテレビリモコンへの道 サンプル動画<a href="https://t.co/anZofswru3">https://t.co/anZofswru3</a></p>&mdash; toguma (@ogmtkc) <a href="https://twitter.com/ogmtkc/status/937207545139101696?ref_src=twsrc%5Etfw">2017年12月3日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

　
　
#雑感

実際、動くとかなり感動。
結果、自分が、というか子供が大喜びで使ってる。
リモコンがすぐそこにもあるのに、わざわざAlexaに声をかける始末w

デバイスからの準備はなかなか大変だけど、やってみる価値はある。
しかもAlexa端末と、RaspberryPiさえあれば、他、部品代は数百円で、理論的には家中のリモコン操作可能な製品は、Alexa操作が可能になる。

利用方法によっては、夢の広がる技術であることがわかる。
今回はその一端が感じられることが出来たので、とても有意義だった。

今後も改善して行くので、改善ごは追記して行こうかなと。