[おうちハック]Slack経由でGoogle Calendarの予定をRaspberry Piに音声通知させる
こんにちは、たるこすです。
前回の「「OK Google」音声コマンドを取得してTVの電源をON,OFFするAndroidアプリを作る」に引き続き、おうちハックネタです。
Raspberry Pi で Google Calendar の予定を音声でアナウンスさせたいと思います。 Raspberry Pi から直接 Google Calendar API を使って予定を取得してもいいのですが、実装の簡単さや今後の拡張性を考えて Slack を経由して行います。
Slack と Google Calendar を連携させ、予定の時間になったら Slack のチャンネルに通知が来るようにします。 そして、Raspberry Pi で Hubot を動作させ、メッセージに反応して音声で通知するようにします。
Slack と Google Calendar の連携
まず、Slack で音声通知用のチャンネルを作ります。今回は #notify という名前でチャンネルを作成しました。
次に、Slack の Apps & integrations の設定で、Google Calendar の連携を追加します。
その後、連携ルールの設定をします。 連携させたいカレンダーと通知先チャネルを選び、予定開始時の通知と一日の予定の通知を有効にします。
音声通知する Hubot の作成
次に Raspberry Pi で動作させる Hubot を作成します。
ひな形作成ツールのインストール
まずは、yo というひな形作成ツールと、hubot用のジェネレータをnpmを使ってインストールします。
$ npm install -g yo generator-hubot
nodejs と npm が入っていない場合には、先に以下の記事などを参考にインストールしてください。 Ubuntuに最新のNode.jsを難なくインストールする - Qiita
ひな形の作成
以下のコマンドを実行します。
$ mkdir my-hubot $ cd my-hubot $ yo hubot
作成者やボットの名前などを聞かれるので答えます。
Bot の名前は Deborah としました。
Bot adapter は slack としてください。
Hubot の動作確認
以下のコマンドで起動します。
$ bin/hubot
15行ほどのメッセージが表示された後、プロンプトが表示されます。 ボット名 ping を入力し、PONG が返ってくることを確認します。
Deborah> Deborah ping Deborah> PONG
Slack と Hubot の連携
先ほど作成した Hubot を Slack と連携するには、Slack に Hubot 連携を追加して API Token を取得する必要があります。
API Token の取得は以下のように行います。
Token を取得したら、hubot 起動スクリプトを作成します。
ここでは start_hubot.sh という名前とします。
HUBOT_SLACK_TOKEN には、先ほど取得した API Token を設定します。
#!/bin/sh export HUBOT_SLACK_TOKEN=xxxx-xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxx export PORT=8084 ./bin/hubot --adapter slack
実行権限をつけて実行します。
$ chmod +x start_hubot.sh $ ./start_hubot.sh
Hubot を slack のチャネルに追加するには、追加したいチャネルで以下のようにポストします。
/invite @[hubotの名前]
うまくいけば、以下のように "ボット名 ping" に対して、ボットが pong と返してくれるようなります。
hubot スクリプト作成
ここからは、メッセージを受け取って音声で通知するような Hubot のスクリプトを書いていきます。
Hubot のスクリプトは、coffeescript で書いたスクリプトを scripts フォルダ内に入れて起動すれば動作します。
ユーザからのメッセージを受け取る場合には、以下のようにrobot.hear
を使えば良いです。
module.exports = (robot) -> robot.hear /some message/i, (res) -> res.send "receiver your message"
しかし、今回は別のBotからのメッセージを受け取りたいので、この方法は使えません。
以下のように、adapter を使うことで bot からのメッセージも取得できるようになります。
module.exports = (robot) -> robot.adapter.client?.on? 'raw_message', (msg) -> msg = JSON.parse(msg) # write your code
Google Calendar Bot から送られてくるのは以下のようなメッセージです。
- 1日の予定の通知
- 予定がある場合、"There are 3 events today." のようなメッセージが送られてきて、予定の詳細は msg.attachments に格納されています。
- 予定がない場合、"There is no events today." というメッセージが送られてきます。
- 予定開始の通知
- msg.attachments[0].pretext に "Event starting now:" というメッセージが入っていて、msg.attachments[0].more に予定詳細が入っています。
以下のコードを scripts/speak.coffee という名前で作成しました。
speak = (message, notify = true) -> console.log "speak #{message}" @exec = require('child_process').execSync if notify option = "-n" else option = "" command = "sh scripts/speak.sh #{option} '#{message}'" console.log "#{command}" @exec command, (error, stdout, stderr) -> console.log error if error? module.exports = (robot) -> robot.adapter.client?.on? 'raw_message', (msg) -> msg = JSON.parse(msg) return if msg.type isnt 'message' if msg.subtype is 'bot_message' console.log "bot message" console.log msg if msg.text? match = msg.text.match(/There .* events? today/i) if match? speech_text = "きょうの予定は、" + msg.attachments.length + "件です" speak speech_text for attachment,index in msg.attachments speak attachment.title, false match = msg.text.match(/There is no events today/i) if match? speech_text = "きょうの予定は、ありません" speek speech_text else return unless msg.attachments attachment = msg.attachments[0] # google calendar event if attachment.pretext is "Event starting now:" speak attachment.more return # other message type speak attachment.title else # user message return if msg.text is undefined speak msg.text return
音声通知用に speak メソッドを作成し、その中で speak.sh を呼ぶようにしています。
詳細は、下の「日本語文章の読み上げ」の項目を読んでください。
日本語文章の読み上げ
spaek.sh には、日本語文章の読み上げのためのスクリプトを書きます。
文字から音声に変換してくれるサービス(text2speech)はいろいろありますが、以前使ったことがあった VoiceText を使ってみます。
VoiceText Web API
API キーを取得すれば、シェルスクリプトから簡単にたたくことができるのでとても便利です。
また、いきなり音声通知が始まると初めのほうを聞き逃してしまうので、 飛行機でのアナウンス前に鳴る「ポーン」という効果音を鳴らしてから音声通知をするようにしています。
音源は音人さんのものを使わせていただきました。
効果音 旅客機内アナウンス(ポーン)<固定翼機<飛行<『 乗り物系音 』 by On-Jin ~音人~
plughw:3,0
の 3,0 の部分は使用するサウンドデバイスに合わせて変更してください。
#!/bin/sh API_KEY="YOUR API KEY" script_dir=$(cd $(dirname $0); pwd) while getopts n OPT do case $OPT in n) NOTIFY_SOUND=true esac done shift $((OPTIND - 1)) if [ $# -ne 1 ]; then echo "invalid argument" exit 1 fi curl "https://api.voicetext.jp/v1/tts" \ -u "$API_KEY:" \ -d "text=$1" \ -d "emotion=happiness" \ -d "emotion_level=2" \ -d "speaker=hikari" > tmp.wav if [ $NOTIFY_SOUND ]; then mpg321 -a plughw:3,0 $script_dir/nori_ge_airplane_pon01.mp3 sleep 0.5 fi aplay -D plughw:3,0 tmp.wav rm tmp.wav
実行させた様子
実行させてみると、こんな感じです。
[Raspberry Pi] Google Calendar の予定をアナウンス
夜、給湯器の電源を切るのを忘れてしまうことがあるので、毎日繰り返す予定として Google Calendarに登録しています。
Slack は Google Calendar 以外にも多くのサービスと連携させることができるので、いろいろと応用ができそうです。