Python
twilio
lambda
Alexa
AlexaSkillsKit

スマホを探す Alexa スキル を作りました

💡 Echo を使いこなしたい

 Echo 買ったんですよ Echo、全然役に立たないですね! これちゃんと作り込んで、自分でスキル作ったり赤外線リモコンと連携したりしないとだめなやつですね、ただのお天気読み上げマシンになっちゃいそうなので、手始めになにか作ってみようと思いました。

 よく家の中で電話をなくすので、電話を探してくれるようなスキルを探したのですが、スマホ側にアプリを入れないと動作しなかったり トラッカールで電話を鳴らして と謎キーワードで呼出さなければいけなのにたまにしか呼ばないので トラッカール が覚えられない というニワトリ並の記憶力であったため、スマホどこ? 程度で Echo から電話をかけてくれるようにしました。
 なお、当初 Lambda を使わずに IFTTT で作成しようとやってみたのですが、ポーリング方式なので指示の15分後に電話が鳴るという謎仕様になったので、Lambda で作っています。

連携イメージ:
gainenzu

:octocat: (追記) 成果物

Serverless Framework 設定と共に公開しました。構築手順はこちらのほうが簡単です。
saitota/AlexaCallMyPhone: It is a Alexa Skill to call your smartphone from Alexa / Echo. You can use it when you lose the telephone in the house.

✋手順

事前に必要なもの

  • Twilio アカウント
    • 電話番号を取得しておく必要があります
  • Amazon Developer アカウント
    • Alexa Skills Kit の開発者登録が必要です
    • https://developer.amazon.com/ja/alexa
    • AWS アカウントとは別モノ、Echo で利用しているアカウント(Amazon.co.jp)で登録する必要がありました、わかり辛い
  • AWS アカウント
    • Lambda を作ります。

Twilio/必要なパラメータ取得

  • Twilio からAPI呼出用のクレデンシャル情報を取得
    • 設定>一般>API クレデンシャル>ライブクレデンシャル
      • ACCOUNT_SID: ACx0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0
      • AUTH_TOKEN : y0y0y0y0y0y0y0y0y0y0y0y0y0y0y0y0
  • Twilio から電話番号を取得(無料)
    • 電話番号>アクティブな電話番号
      • FROM_NUMBER:+815011111111
  • 発信先の電話番号(自分の番号の国際表記)
    • TO_NUMBER:+819011111111

Twilio/Call API のテスト実行

API Documents を参考に、Curl でテストしました。%2B+ のエスケープです。

console
curl -XPOST https://api.twilio.com/2010-04-01/Accounts/ACx0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0/Calls \
    -d "Url=http://demo.twilio.com/docs/voice.xml" \
    -d "To=%2B819011111111" \
    -d "From=%2B815011111111" \
    -u 'ACx0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0:y0y0y0y0y0y0y0y0y0y0y0y0y0y0y0y0'

下記のレスポンスが返ってきて、電話がかかってきました。音声は Twilioをご利用いただきありがとうございます 的なものでした。本来は xml に喋らせる内容を指定するようなのですが、詳しく調べていません。

console
<?xml version='1.0' encoding='UTF-8'?>
<TwilioResponse>
    <Call>
        <AccountSid>ACx0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0</AccountSid>
        <To>+819011111111</To>
        <ToFormatted>+819011111111</ToFormatted>
        <From>+815011111111</From>
        <FromFormatted>+815011111111</FromFormatted>
        <Status>queued</Status>
        <PriceUnit>JPY</PriceUnit>
        <Direction>outbound-api</Direction>
        <ApiVersion>2010-04-01</ApiVersion>
        <Uri>/2010-04-01/Accounts/ACx0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0/Calls/CAx0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0</Uri>
        <SubresourceUris>
            <Notifications>/2010-04-01/Accounts/ACx0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0/Calls/CAx0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0/Notifications</Notifications>
            <Recordings>/2010-04-01/Accounts/ACx0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0/Calls/CAx0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0/Recordings</Recordings>
        </SubresourceUris>
    </Call>
</TwilioResponse>
※一部省略しています

AWS/Lambad 関数作成

zip, python3 が必要です。

console
$ mkdir callmyphone # 作業用ディレクトリ
$ cd callmyphone
$ pip install twilio -t . # カレントディレクトリにライブラリインストール
$ vi lambda_function.py # 関数のソース
$ zip -r upload.zip * # 関数つくったらまとめてZIP
lambda_function.py
from twilio.rest import Client
import os

def lambda_handler(event, context):

    print("event.session.application.applicationId=" + event['session']['application']['applicationId']  +
          ", event.session.sessionId=" + event['session']['sessionId'] + 
          ", event.request.requestId=" + event['request']['requestId'])
    account_sid = os.environ['ACCOUNT_SID']
    auth_token = os.environ['AUTH_TOKEN']

    client = Client(account_sid, auth_token)
    # Make the call
    call = client.api.account.calls\
          .create(to=os.environ['TO_NUMBER'],  # Any phone number
                  from_=os.environ['FROM_NUMBER'], # Must be a valid Twilio number
                  url="http://twimlets.com/holdmusic?Bucket=com.twilio.music.ambient")
    print("call.sid=" + call.sid)

    return {
        'version': '1.0',
        'sessionAttributes': {},
        'response': {
            'outputSpeech': {
                'type': 'PlainText',
                'text': "電話を鳴らします"
            },
            'card': {
                'type': 'Simple',
                'title': "CallMyPhone",
                'content': "電話を鳴らしました"
            },
            'reprompt': {
                'outputSpeech': {
                    'type': 'PlainText',
                    'text': "プロンプト出力メッセージ"
                }
            },
            'shouldEndSession': True
        }
    }

 Lambda関数はマネジメントコンソールからぽちぽち設定しました。電話番号とかは環境変数にしました。

  • 関数名:CallMyPhone
  • トリガ―:Alexa Skills Kit
  • ロール:lambda_basic_execution
  • arn:arn:aws:lambda:us-east-1:000000000000:function:CallMyPhone
  • 関数コード
    • コードエントリタイプ:ZIPファイルをアップロード(upload.zip)
    • ランタイム:python 3.6
    • ハンドラ:lambda_function.lambda_handler
  • 環境変数
    • ACCOUNT_SID:ACx0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0
    • AUTH_TOKEN:y0y0y0y0y0y0y0y0y0y0y0y0y0y0y0y0
    • TO_NUMBER:+815011111111
    • FROM_NUMBER:+819011111111

AmazonDeveloper/Alexaスキル作成

あとは Alexa Skills Kit 側の設定をぽちぽち。対話モデルとかインテントスキーマとか新しい用語に戸惑いながらやってみました。
微妙にはまったのは「電話をかけて」などのフレーズで呼び出し名登録すると、テストはできるけど Echo から実行できませんでした。~で、~を、などのつなぎの語はつかうことができない、ということが 呼び出し名の要件 に明記されていました。

  • スキル情報
    • 言語:Jaspanese
    • スキル名:CallMyPhone
    • 呼び出し名:スマホどこ
setting_インテントスキーマ
{
  "intents": [
    {
      "intent": "GetIntent"
    }
  ]
}
setting_サンプル発話
GetIntent 電話鳴らして
GetIntent 電話を鳴らして
GetIntent スマホを鳴らして
GetIntent 携帯を鳴らして
  • 公開情報
    • 小アイコン だけ設定
  • 設定
    • AWS Lambda の ARN (Amazonリソースネーム)
      • arn:aws:lambda:us-east-1:000000000000:function:CallMyPhone

📱できました

Screenshot1
Screenshot2

Alexaアプリ「DEVスキル」に自動的にインストールされていました。いいかんじに動きました!
今回だと「スマホどこ を起動」「スマホどこ で電話を鳴らして」などで起動できました。
対話モデルの理解が薄いままなので動的に呼出し先の番号を変えたり、会話したりヘルプ作ったりなどできていませんが、今後活用していけそうです。

📒参考にしました