Alexaでタイマースキルを作るときにつまったこと

みなさんスマートスピーカーはお持ちでしょうか？
自分は2台のアレクサを使い倒しています。エンジニアたるもの実際に使っていると自分でもスキルを開発したくなりますよね。:sunglasses:
この記事では自分が初めてスキル開発したときに詰まったことを書き留めていきます。
<img src="https://qiita-image-store.s3.amazonaws.com/0/149427/49fbee94-c034-3461-cb40-ae540b582f17.png" width="500px">

## 作ったスキル

今回自分は、[リピートタイマー](https://www.amazon.co.jp/tamorita-%E3%83%AA%E3%83%94%E3%83%BC%E3%83%88%E3%82%BF%E3%82%A4%E3%83%9E%E3%83%BC/dp/B07MG446K5/ref=sr_1_1?s=digital-skills&ie=UTF8&qid=1550569455&sr=1-1&keywords=%E3%83%AA%E3%83%94%E3%83%BC%E3%83%88%E3%82%BF%E3%82%A4%E3%83%9E%E3%83%BC)というスキルを開発しました。どんなスキルかというと、タイマーにセット数とインターバル時間を設定できるような感じにしたものです。例えば、「アレクサ、リピートタイマーで3分タイマー、3セット、インターバル10秒」のような感じで話しかけると動作します。

<img width="732" alt="スクリーンショット 2019-02-19 18.56.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/149427/e72ced36-7dce-4cc6-55cf-98aa5c4733f1.png">

[アレクサスキル開発トレーニング](https://developer.amazon.com/ja/alexa-skills-kit/training/building-a-skill)をやれば一通りのスキル開発の流れが分かるので、スキル開発をしたければまず、こちらをやってみるのをおすすめします。その上で今回の開発で詰まったところなどを上げていきます。

## 詰まったところ
### 1. そもそもタイマースキルってどうやって実装するの？
今回実装したかったのは普通のタイマー機能に、セット回数とセット間のインターバル時間を追加したスキルです。下の図は例です。
![fig1.jpg](https://qiita-image-store.s3.amazonaws.com/0/149427/8238ce09-b345-98e1-97dd-830c57c3f8fc.jpeg)


理想としてはタイマー設定をした後、バックグランウドモードに移行してくれるようなものがいいのですが、調べてもそういうやり方はできなそうだったので、一回の応答で設定した分の発話を全て応答させるようにしました。ただし、1回の応答で数時間分の発話をさせるのは流石にやばそうだったので、上限はトータル1時間にしてます。


### 2. 無音時間の確保
地味に迷ったのが無音時間の確保です。色々調べたところ、アレクサに発話せるspeechOutで```"<break time ='0.5s' />"```のように設定すれば、break timeを設定できることがわかりました。しかし、break timeはMAX10秒しか設定できません。今回は以下のようにループを回して時間を確保しました。

```main.js
// ループによってタイマーの時間を確保(break_timeの上限の10秒)
for (let i=1; i <= t_loop_num; i++) {
    speachOut += "<break time ='" + 10 + "s' />";
}
```

### 3. mp3の使用回数制限
今回作ったタイマースキルは、それぞれのセットごとに開始と終了のタイミングでmp3でカウントダウンの音を鳴らそうとしました。しかし、**1回の応答の中で使用できる音声ファイルは5個まで**という制約（[参考](https://developer.amazon.com/ja/docs/custom-skills/speech-synthesis-markup-language-ssml-reference.html#audio)）があるらしく3セット以上には対応できなかったので、もしmp3利用回数が5回を越える場合は、以下の文を読み上げさせることで無理やりカウントダウンを発話させました。

```main.js
let CountVoice = "3<break time ='0.5s' />2<break time ='0.5s' />1<break time ='0.5s' />";
```


### 4. ユーザの発話から時間に変換したい
タイマースキルを開発するにあたって必要だった処理が、ユーザーの発話を秒に変換する作業です。例えば、「3分30秒」など入力された場合は、210秒に変換してカウントダウンする必要があります。今回はこの機能を標準スロットタイプの`AMAZON.DURATION`を使って変換しました。

`AMAZON.DURATION`では「3分30秒」のような発話を、「PT3M30S」と変換します(時間はH)。したがって、インテントで以下のように設定すれば、ユーザーの発話をいい感じに取得できます。

<img width="1054" alt="スクリーンショット 2019-02-19 20.13.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/149427/56b0667b-eff9-5768-3105-f9a8f743ee61.png">
<img width="1061" alt="スクリーンショット 2019-02-19 20.13.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/149427/da217c42-5dbe-f81c-1cb0-e793b241283d.png">
ただし、これを```let time = this.event.request.intent.slots.Time.value;```のように取得し、アレクサに発話させると「PT3M30Sタイマーですね」のように発話してしまいます。:tired_face:
なので自分は発話形式に変換する関数とINT型の整数に変換する関数を以下のように実装しました。

```main.js
/*
AMAZON.DURATION形式を発話形式時間に変換する関数
引数: AMAZON.DURATION形式(ex. PT10M)
return: 発話形式時間(ex. 10分)
*/
function convertTimeSpeech(time) {
    time = time.replace("PT", "");
    time = time.replace("S", "秒");
    time = time.replace("M", "分");
    time = time.replace("H", "時間");
    return time
}
```

```main.js
/*
発話形式の時間をInt型の秒に変換する関数
引数: 発話形式時間(ex. PT10M)
return: 秒(ex. 600s)
*/
function convertTimeSec(time) {
    
    let hour_sec = 0;
    let min_sec = 0;
    let sec_sec = 0;
    
    if (time.includes("時間")) {
        let cut_str = '時';
        let index = time.indexOf(cut_str);
        let hour = time.substring(0, index);
        hour_sec = parseInt(hour) * 3600;
        time = time.slice(index + 2);
    }
    if (time.includes("分")) {
        let cut_str = '分';
        let index = time.indexOf(cut_str);
        let min = time.substring(0, index);
        min_sec = parseInt(min) * 60;
        time = time.slice(index + 1);
    }
    if (time.includes("秒")) {
        let cut_str = '秒';
        let index = time.indexOf(cut_str);
        let sec = time.substring(0, index);
        sec_sec = parseInt(sec);
    }
    return (hour_sec + min_sec + sec_sec)
}
```

### 5. デバッグのやり方
今回の開発はAWS Lambdaのオンラインエディタで行ないました。lambda上ではテストイベントによって簡単なテストはできるのですが、関数が正しく動いているかなどは```console.log```などで見たいです。まあオンラインエディタ使わなければいいのですが、手軽なもんで・・・。
オンラインエディタでログを確認する場合、[Amazon CloudWatch](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/WhatIsCloudWatch.html)を使えば簡単に確認できます。
alexa developer consoleのテストでスキルを実行して、AWS CloudWatchの「ログ→ロググループ→ログストリーム」でログを確認していました。
<img width="1080" alt="スクリーンショット 2019-03-12 12.26.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/149427/3e1c8858-9d75-1ac8-26bf-93c8dcdfe9e6.png">

### 6. AWSってお金かかるの？
通常のAWS無料利用枠は、毎月100万件のAWS Lambdaリクエストまで利用可能なので、シンプルなスキルならまず大丈夫です。さらにスキル開発者は、このAWS無料利用枠に加えて、100ドル分のAWSプロモーションクレジットを受け取れるらしいです（自分はまだ申請していません）。また、スキルの開発でAWS使用料が発生した場合は、さらに毎月100ドル分のプロモーションクレジットを受け取ることができます。太っ腹ですね！
[Alexa AWSプロモーションクレジットのご紹介](https://developer.amazon.com/ja/alexa-skills-kit/alexa-aws-credits
)

## ちなみに
スキルを申請すると、結構厳密にAmazonから審査されます。ただレスポンスが異常に早いのでだいたい3日くらいで公開までもっていけるかなと思います。
また、スキル公開後はレポートで色々なデータを見ることができるので面白いです！
<img width="1179" alt="スクリーンショット 2019-03-12 12.45.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/149427/0b20ba9d-4e57-1334-be19-872eee4a38c0.png">