LINEに送信したメッセージを、Google Homeで読み上げ、家族に通知

仕事から帰る前に、LINEで「今から帰りまーす」とメッセージを送るが、嫁が気づかないことが多いため、Google Homeにメッセージを読み上げてもらうことにしました。

# 全体像
- LINEのMessaging APIにある、メッセージ受信(など)した時、指定URLに通知するwebhook機能

- Google Homeに好きな言葉をしゃべらせるgoogle-home-notifier

を使い、自宅にhttpサーバを起動しておき、LINE@のアカウントからPOSTされたら、google-home-notifierを使ってGoogle Homeにしゃべってもらう。

家のGlobal IPはちょくちょく変わってしまうので、ngrokを使用してURLを公開。

![qiita投稿_line_googlehome.001__.png](https://qiita-image-store.s3.amazonaws.com/0/132225/18d239f7-59a0-ad1a-cb1e-e53754977cb6.png)


# LINE Channelの作成

- [LINE developers](https://developers.line.me/) で「Messaging API(ボット）をはじめる」を選択し、新規Channelを作成

- 作成したLINE Channelの設定画面で、以下を設定
    - Webhook設定を「利用する」
    - Webhook URLを、自宅サーバのURL(後ででてくるngrokで生成したURL)
<img src="https://qiita-image-store.s3.amazonaws.com/0/132225/b3ceff96-a251-0128-d195-7347924a397f.png" width=90%>

これで、作成したChannelのIDにメッセージを送信すると、webhookイベントが指定したURLに通知されるようになる。

# HTTPサーバ準備

Node.js, npmが動作する環境が前提

### Install ngrok

ngrokは、localhostをXXXXXXXX.ngrok.comで、外部からアクセス可能にするサービス。
localで作成した環境をサクッと外部に公開できる。

- [https://ngrok.com](https://ngrok.com)からdownload

- downloadした`ngrok`をpathの通った場所に格納

```sh:macの場合の例
# copy to Applications directory
$ mv ngrok /Applications/

# cd into your local bin directory
$ cd /usr/local/bin

# create symlink
$ ln -s /Applications/ngrok ngrok
```

### Install google-home-notifier
google-home-notifierは、JavaScriptでGoogleHomeに発話させることができるライブラリ。

```sh
$ npm install google-home-notifier
```

ref : [Github - Send notifications to Google Home](https://github.com/noelportugal/google-home-notifier)


### httpサーバソース

[GitHub - play_with_googlehome](https://github.com/DaaasukeMurata/play_with_googlehome/tree/qiita/speak_message)

webhook eventはJSON形式で通知され、メッセージ受信時は、
- `events[0].type`に、"message"
- `events[0].message.text`に、メッセージ本文
- `events[0].source.userId`に、送信者のUserId
が入る

他のformatは、[Messaging API - Webhookイベントオブジェクト](https://developers.line.me/ja/docs/messaging-api/reference/#anchor-eff5d13cad53e16173242bbc2f157655a9e8dbd8)参照


```js:server_for_line.js
const config = require('./config/default.json');

// for http server
const http = require('http');

// for googlehome
const googlehome = require('google-home-notifier');
const language = 'ja';

function googlehome_init() {
  googlehome.device(config.googlehome_name, language);
  googlehome.ip(config.googlehome_ip, language);
}

function googlehome_speak(text) {
  googlehome.notify(text, function (res) {
    console.log('googlehome_res : ' + res + '   speech_text : ' + text);
  });
}

// main()
googlehome_init();

http.createServer(function (request, response) {
  let post_data = '';

  request.on('data', function (chunk) {
    post_data += chunk;
  });

  request.on('end', function () {
    console.log('post_data : ' + post_data);

    const webhook = JSON.parse(post_data).events[0];
    if (webhook.type != 'message' || webhook.message.type != 'text') {
      return;
    }

    // 特定の人からのメッセージのみ発話
    if (config.speakable_userid == '' || webhook.source.userId == config.speakable_userid) {
      const data_text = webhook.message.text;
      googlehome_speak(config.beginning_sentence + data_text);
    }

    response.writeHead(200, { 'Content-Type': 'text/plain' });
    response.end();
  });
}).listen(config.server_port);
```

config/default.jsonの
- `server_port`は、webhookのeventを受け取るport。ngrok使用時に指定するport番号とする
- `googlehome_ip`に、GoogleHomeのIPアドレス
- `googlehome_name`は、GoogleHomeのDeviceNameだが、設定しなくても大丈夫かも
- `beginning_sentence`に、メッセージの冒頭に発話したい文を記載（「メッセージが届きました。${メッセージ本文}」）
- `speakable_userid`は、特定Userからのメッセージのみ発話させたい場合に記載。空白の場合、誰のメッセージでも発話。UserIdは一度メッセージを受信させ、webhook eventの、`source.userId`を見て確認

```json:default.json
{
    "server_port": "8080",

    "googlehome_ip": "192.168.XX.XX",
    "googlehome_name": "device name of google home",

    "beginning_sentence": "メッセージが届きました。",
    "speakable_userid": ""
}
```


# HTTPサーバ起動

### start ngrok

```sh:ngrok起動
# create symlink. default.jsonで指定したportとする。
$ ngrok http 8080

# 起動すると、下記の様な画面が表示される。
# https://XXXXXXXX.ngrok.ioを、LINE Channel設定の webhook URLに設定。
ngrok by @inconshreveable
 (Ctrl+C to quit)

Session Status                online
Version                       2.2.8
Region                        United States (us)
Web Interface                 http://127.0.0.1:4040
Forwarding                    http://XXXXXXXX.ngrok.io -> localhost:8080
Forwarding                    https://XXXXXXXX.ngrok.io -> localhost:8080
```

### run http_server

```sh:server起動
$ node server_for_line.js

# LINE message受信すると、下記のようなログが出力される。
# "userId"が、送信者のuserId。
post_data : {"events":[{"type":"message","replyToken":"0f3a5cccea3543f89f502345eb3df90","source":{"userId":"Uxxxxxxxxxxxxxxxxxxxxxxxxx","type":"user"},"timestamp":1515074416236,"message":{"type":"text","id":"7256796266214","text":"メッセージ本文"}}]}
```


# 動作

- 作成したLINE Channelのアカウントにメッセージを送り、GoogleHomeがしゃべればOK

- LINEのGroupを作成し、送りたい相手と、作成したLINE Channelのアカウントを登録しておけば、メッセージ文とGoogle Homeの発話で相手に通知できる


# おまけ

逆の、Google Homeにしゃべりかけ、認識した文をLINEメッセージで通知するのは、IFTTTを設定するだけでできます。GitHubのREADMEに、簡単な設定例を記載しました。[GitHub - play_w_googlehome#ifttt連携](https://github.com/DaaasukeMurata/play_with_googlehome/tree/qiita#ifttt連携)

娘がしゃべるようになったら、Google Homeにしゃべりかけてメッセージを送ってもらうの楽しみ。