これは、KIT Developer Advent Calendar 2017 13日目の記事です。
これに参加するのもこれが最後です。少し寂しいですね。
みなさん。こんにちは。306_sanです。最近流行りのスマートスピーカーで開発してみました。
自分はAmazon Echo dotを購入しました。余談ですがスマートスピーカーの強みって高性能なマイクでの音声認識だと思うのですが、Amazon Echoシリーズが全シリーズ7つのマイクアレイを搭載されており、廉価版であるdotでも高性能なマイクを使った音声認識が可能です(Google Homeと同等だと思ってます)。Google Home miniはマイクが一つしかないので検討してる方は一つの指標にしてもいいかもしれません。
さて、Google HomeではIFTTT→Slack→Rubyで受け取りでいろいろいじれます。
ex. https://youtu.be/8yT5PeZuWJ4
(※投稿者≠私)
しかし、これをAlexaでやろうとした時点では、どうもAlexaのスキルは各国で使えるかどうかの制限がかけられており、IFTTTやNode-Red経由(最近対応しました)では英語しか対応していないため、日本語でやるには自力でJSONを受け取って処理しないといけません。
幸いAlexaには公式開発チュートリアルがありますが、このチュートリアルはAWSがないとできないものとなっています。AWSはちょっと大学では使えないので、自力で頑張ってみたのが今回の記事です。チュートリアルを見ずに開発したので詳しいことはよくわかってないままです。コメントどしどしお待ちしております。
参考にした記事:https://www.slideshare.net/tanakataro4/rubyalexa
とりあえず開発に必要な物
- HTTPS環境とドメイン
- オレオレ証明書は開発用途ならいいみたいですが、本番はダメみたいです。
- 自分はサーバーを持っていないのでここで詰みました。
- 今回はngrokを使って無理やり通信してます。
- 導入は省略します。
- Ruby
- 今回はRuby2.3.4を使いました
- 開発用語の知識
- インテント、カスタムスロットなどスキル開発用語が出てきますがここでは詳しく説明しません。
それでは開発していきます。
目標として↑と同じ機能を持つスキルを開発します。
Alexa周り
まず、Amazonの開発者登録をします。
![スクリーンショット 2017-12-11 13.33.13.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F79241%2Faa73aebd-8df2-354f-aa7d-2c449087bac8.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=452395cd497110e8dfd9ca590de52193)
![スクリーンショット 2017-12-11 13.36.31.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F79241%2F07eee1ee-f1f8-e767-22af-3311b459504c.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=05fdeee5609249077ef50951c3a4fe30)
![スクリーンショット 2017-12-11 13.39.42.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F79241%2F92d8647e-c0e1-9009-0134-fcf90ae60f01.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=1c7dc944ccbae1b34df57fc03d4515fa)
![スクリーンショット 2017-12-11 13.44.01.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F79241%2Fd80c39cf-a8b4-555d-4197-2c08be22f65b.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=ef353f08c4c16ac4b83c0eb7214aa983)
JSONでインテントを記述します。インテントとはメソッドのようなものです。slotsというのはあとで説明するカスタムスロットです。
![スクリーンショット 2017-12-11 13.46.59.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F79241%2Fca214ea5-c3d8-6c4f-540a-914135a5e533.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=a2ff56364a9b2ca1807719ded27eebac)
![スクリーンショット 2017-12-11 13.54.11.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F79241%2Ff850af6f-800b-8811-8af8-099d1f44423f.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=309691c0be1c42b55fa2f9e93992b6fd)
![スクリーンショット 2017-12-11 13.56.56.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F79241%2F06a65283-a4c6-b5f1-f0b0-705ad49492c0.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=851628c0fd59f73a683b388b0811e9d4)
![スクリーンショット 2017-12-11 13.59.11.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F79241%2F9d931e59-0af5-8fe4-0c89-845b0b76a15d.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=c68107ff430b4fe2e2c5171e57bc4eda)
![スクリーンショット 2017-12-11 14.00.30.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F79241%2Fd9bcb8c2-d652-c172-8f75-91aaf760ffe5.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=048629a78b15484ce653b9721009451a)
Ruby周り
さて、次はバックエンドを書きます。がんばルビィ!(※筆者はラブライブ!(サンシャイン!!も)が大好きです)
gemとして素敵なSinatraベースの素敵なライブラリ「ralyxa」があるので、これを使います。
source "https://rubygems.org"
gem 'ralyxa'
gem 'sinatra'
んで、bundle installでよしなにします。
フォルダー階層はこんな感じにします
├── Gemfile
├── Gemfile.lock
├── intents
│ └── test.rb
├── server.rb
└── vendor
└── path
注意してほしいがintentsフォルダーで、この中に対応するインテントを書きます。
ファイル名は別に一致してなくても良いみたいですが、本番時のことを考えると同名にした方がいいかなと思います。
ここではtest.rbでまとめて受け取ります。
require 'bundler'
Bundler.require
post '/' do
Ralyxa::Skill.handle(request)
end
# coding: utf-8
intent "LaunchRequest" do
ask("こんばんは。コーヒーはいかがですか?")
end
intent "drip_coffee" do
p request.slot_value("size")
if request.slot_value("size").nil?
tell("わかりました。コーヒを淹れますね。")
else
tell("わかりました。コーヒーを#{request.slot_value("size")}に淹れますね。")
end
end
intent "SessionEndedRequest" do
respond("Hello World!")
end
こんな感じでかきかきします。askとtellはユーザーの応答を待つかどうかの違いです。書いてないインテントもありますが、これは動かすために必要なインテント(おまじない)になります。ドキュメントを参照してください。
bundle exec ruby server.rb -p 8080 -o 0.0.0.0
とりあえず、127.0.0.1:8080にアクセスしてサーバーがた立ち上がっている(おそらくエラー画面だけど)のをを確認します。
次に別のターミナルを開いてngrokを立ち上げます。ngorkはポート開放などができなくても中継サーバーがいい感じに外部に公開してくれるサービスです。その為セキュリティのリスクがありますので開発用途に使いましょう。
$ngrok http 127.0.0.1:8080
すると、
ngrok by @inconshreveable (Ctrl+C to quit)
Session Status online
Account 306san (Plan: Free)
Version 2.2.8
Region United States (us)
Web Interface http://127.0.0.1:4040
Forwarding http://****.ngrok.io -> 127.0.0.1:8080
Forwarding https://****.ngrok.io -> 127.0.0.1:8080
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
となるのでこれでもう外部に公開されている状態になりました。httpsのURLを先程の開発者ポータルでも書き換えます。
![スクリーンショット 2017-12-11 14.54.07.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F79241%2Fb216d404-1df8-08ca-5b32-f9ac3304876e.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=0896859fb6946b244fda5d32fade1683)
試しにテストページに飛んで試してみましょう。
![スクリーンショット 2017-12-11 15.00.11.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F79241%2F87901804-723f-b60b-6819-c19e9a4f8b7e.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=35e86570128420a1eb11a06ecde506c7)
まとめ
ralyxaを使えば、Rubyでスキル開発ができる。
ngrokを使えば、ポート開放しなくとも無理やり外部に公開できる。
サーバーなくとも無料でスキル開発ができる!!
最高!
rm mini3をポチったので次はスマートホーム化を目指します