てんちょーの技術日誌

自分がつまづいたこととかメモ

GoogleHome で遊んでみたことメモ

はじめに

スマートスピーカー Advent Calendar 2017 の 3日目です。

qiita.com

昨年末はPCのキーボードを封印して音声認識でどこまでいけるか試したりしていました。

コルタナさんがあまりにもEdge頼りなので、気合でPowerShellスクリプトを書くなどしてTwitterをしてました。

shop-0761.hatenablog.com

今回は勢いで購入したGoogleHome miniが思ってた以上に認識精度が高いので、 あれこれ勢いで作って便利になったメモを残しておこうかと。

nodejsは手探りで書いたのであまりよくない書き方かも…。

ついでにAmazon echo dotも安かったので、最後に感想とかも。

やったこと

基本的に RaspberriPi Google Apps Script(GAS) IFTTT の3つでやりくりしてます。

Raspberri Pi zero W を買う

事前にちょろっと調べるとGoogle Home notifier を使えば発話させられるらしく、 これはラズパイ買っとこ と思って秋葉原で買いました。 キットみたいなやつを買ってすぐ使えるやつだったのに、何を思ったかSDカードをフォーマットしてしまい 結局入れ直しました…地味にひっかかったりした…

Hue連携

Hueがあれば、すぐ終わります。なんか覚えてないくらいに一瞬でした。 "部屋"って名前の部屋をGoogleHomeのアプリで作っておくと、「OK Google, 部屋の電気をつけて」みたいに使えて便利です。

wemo連携

家に転がってたwemo mini smart plugをつかうことにしました。 こいつはコンセントのオン/オフができます。

www.belkin.com

IFTTTで連携できるので、とりあえずそのまま使っています。 運用でカバーってやつですね()

IFTTTでコマンドの上書き

どうやらGoogle Homeにある既存のコマンド「おはよう」などはIFTTT連携で上書きできるようです。 はたして需要があるのかコレ。

特に難しい設定もせずにこうすると、上書き?になるっぽい

f:id:shop_0761:20171116094813p:plain

Google Home notifier

セットアップ

qiita.com

記事をそのままやりました。注意すべきはnodejsは7系でないと動かないかもです。 これで喋らせる時に「とぅるん」ってこれから喋るよみたいな音がなるやつ消せないのかな…?

ngrokのURLをslackにPOST

Google Home Notifier では ngrok というのを使ってローカル環境を外部に公開しています。

qiita.com

example.js を実行するたびにアドレスが生成され、これを使えばGAS等からアクセスできます。 サブドメインを割り当てることができるらしいのですが、2.0だと有料、1.0だと無料だそうです。

google home notifierがどっちなのか調べるのも面倒だったので、 urlが生成されるたびにslackへPOSTして、それをOutgoing webhooksで拾い、GASでSpread Sheetに記録してました。 まあ、メリットとしてラズパイが落ちてしまった時にこれらのスクリプトの準備できたらslackに通知がくるって感じですかね。

こんな感じで

f:id:shop_0761:20171116010213p:plain

outgoing webhooks は legacy になってしまったので代わりになるものを探しましたが、いまいちしっくりくるものが無かったので そのまま使っています。ただtokenの生成が前と変わって、権限を付与する形のものになりました。 今はこの3つを許可したtokenを使っています。

f:id:shop_0761:20171116011350p:plain

api.slack.com

qiita.com

example.jsに追記する感じで書いています。

app.listen(serverPort, function () {
  ngrok.connect(serverPort, function (err, url) {
    console.log('POST "text=Hello Google Home" to:');
    console.log('    http://localhost:' + serverPort + '/google-home-notifier');
    console.log('    ' +url + '/google-home-notifier');
    console.log('example:');
    console.log('curl -X POST -d "text=Hello Google Home" ' + url + '/google-home-notifier');

   //ここから
        var request = require('request');
        const HOST = 'https:\/\/slack.com/api/chat.postMessage';
        var options = {
                url: "https://slack.com/api/chat.postMessage",
                form: {
                        "token": "とーくんをいれる",
                        "channel": "#googlehome",
                        "text": "ngrokのアドレスが変わったよ " + url + "/google-home-notifier",
                        "username": "GoogleHome_Assistant"
                }
        };

        request.post(options, function(error, response, body){
                if(!error){
                        console.log("BODY: " + body);
                }

                else{
                        console.log("error: " + error.message);
                }
        });
  //ここまで
  });
})

trello にメモを残す

Google Home 標準で買い物メモを残せますが、消すのはアプリ経由らしくて面倒いのでtrelloで管理出来るようにしました。 カードの追加/削除 がボイスコマンドで出来ます。全てのカードの読み上げもできるようにしようかな。

GAS側のコードはこの辺にまとめておきました。

gist.github.com

IFTTTはこんな感じで。

f:id:shop_0761:20171116013755p:plain

こうして「メモに牛乳を追加して」というと、「牛乳を追加」とSpread Sheetに書き込まれるので、 追加の文字列が入ってたらtrelloにカード追加、無かったら削除 みたいにしてます。

実行済みのチェック

Spread Sheet に書き込まれたタイミングでGASを実行するため、適当に編集してると 何度も関数が呼ばれてアワアワすることになるので、こんな感じでチェックするようにするといいでしょう。

f:id:shop_0761:20171116014152p:plain

function checkCmdDone(){
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  //sheet が複数あるので名前で指定
  var sheet = spreadsheet.getSheetByName('CommandLog'); 
  var value = sheet.getRange(sheet.getLastRow(), 1, 1, 2).getValues();

  if(value[1] != "Done"){
    sheet.getRange(sheet.getLastRow(), 2).setValue("Done");
    return true;
  }
  
  else{
   return false; 
  }
}

勝手にしゃべる

細かく時間を指定して実行できるらしいです。(知らなかった)

qiita.com

これらを使って勝手にしゃべるようにしました。

ゴミ捨ての通知

ゴミ捨て表をSpread Sheetで作って、そこを参照しつつ通知してくれます。 前日の夜とかに知りたいので少しindexを調整してます。

f:id:shop_0761:20171116015341p:plain

  for(var i in cels){
   // todayは12/3(日)10:00表記になるものを作っておいて、indexOfでさがしてる
    var index = readableDate.indexOf(cels[(parseInt(i)+1) % (cels.length)][0]);
    var state = cels[(parseInt(i)+2) % (cels.length)][1];

    if(index > -1 && state != ""){
      //読み上げ
      textToSpeech("てんちょーさん、今日は" + state + "だよ");
    }
  }

ストレッチの通知

ストレッチし忘れるので、さっきの時間指定できるTriggerで設定しています。

降水確率の読み上げ

天気はスクレイピングしてきて使っています。 現在の時刻に合わせて、それ以降の降水確率を読み上げてくれます。 スクレイピングしてきたときに、過去の降水確率は"-"みたいになってたりするので、 その時は読まない、みたいにして書いてます。大したことないので省略。

ミクさんに喋ってもらう

という気持ちで作りました。 多分 OpenJTalk + MMD Agentの初音ミクの音響モデル が1番ラクな気がしますが、 あまり好みではなかったのでゴリ押しで作りました。

流れとしては

  1. Google Home で音声認識 + IFTTT でSpreadSheetに書き込み
  2. GASで検知して、Raspberri Pi にテキストを送信
  3. MeCabでカナ読み取得
  4. 一文字ずつに分割
  5. 対応する.wavファイルを探す
  6. sox コマンドで一つの.wavファイルに
  7. google home notifier で音声ファイル読み上げ

って感じですね。面倒いので全部は書きませんが、ポイントだけ。

  • MeCab で品詞分解に失敗して読みが取れない時がある
  • 数字は漢字に変換してMeCab
  • 促音・撥音・拗音・長音あたりを考慮して分割

これ作ってる途中で気づきましたが、GASってzip作れるんですね…やばみ…

gist.github.com

Amazon echo dotとの比較

Amazon echo dot は Google カレンダーに予定を追加できます。Google Home mini では出来ません。 ????どういうこと????

あとAmazonで買い物できますが、やり取りが長いので喋るくらいならポチポチしたほうが早いという印象です。 Hey mikuも使えます。ただ1回遊んだら終わりですかね…

それと体感的な話ですが、echo dotは自分で再生しはじめた音楽のせいでマイクの感度が落ちる…?自爆…? その点Google home miniのが優秀な気がします。布団からごにょごにょ喋っても反応したり、 モニターを正面に座り、真後ろに置いてあるGoogle home mini に話しかけても反応したりします。すごい。

まとめ

ここまでやるともはやGoogleHomeである意味とは…?という気持ちになります。 ただ、GoogleHomeの認識精度は魅力的ですね。