Google HomeからGoogle Keepのメモに追記する

### 2020年07月16日 追記
firebase使用量について
現在3つのメモで使用しており、コンソールで確認したところDBのデータ容量については60Byteでした。
そのため無料の[Sparkプラン](https://firebase.google.com/pricing?hl=ja)で収まる範囲となっています。

### 2023/01/23 追記
2022/10月頃よりIFTTTのGoogle Assistant V2の変更に伴い任意のパラメータをリクエストに含めることが出来なくなりました。
現在は「〇〇リストに△△を追加して」というトリガーでGoogle Assistantから直接Google Keepに追加が可能となっています。
本記事の内容は記録として残しております。

____
## はじめに
本記事では Google Home(Google Assistant)を用いたGoogle Keepへのメモの追記 について忘備録も兼ねて残します。

## 背景
Google Home Miniが我が家に来てから、最初に試したことはIFTT経由で買い物のメモを私と家族と「Line Notify」というBotのいるLineのトークルームに投稿するといったものでした。
（[コチラ](https://qiita.com/jyoshida/items/f74afe3e421795cdd63c)の記事でされているものが近いです）

導入が手軽な上に家族には好評で利用開始から2年ほど使用していましたが、Lineの仕組み上自分の投稿は消すことができてもBotが発言した内容は自分の端末上しか非表示とすることができないため、各々が買ったものを共有しないといけないと言う手間が発生してしまい使用期間が長くなるにつれ投稿の非表示が面倒になってきました…。
一方で「話しかけてメモをとる」というスタイルは子供にも好評で、Google Homeを変わらずトリガーとして実現できる別の方法を検討することとなりました。

最終的なメモを保存先としていくつかのサービスを試す中で
「Google Keepと連携できたら便利なのになー、共有もできるし。」
「でも見た感じ外部連携なさそう…」
と思い候補から外れていましたが、改めて調べたところ非公式なAPIの[gkeepapi](https://github.com/kiwiz/gkeepapi)というものはあるようで今回はこちらを使って連携を試してみます。

## 本題
### やりたいこと
Google Homeに話しかけてGoogle Keepの任意のメモに追記を行う。
・IFTTT経由でFiebase Realtime Database上にデータを登録する。
・Raspberry Pi上でFirebaseの監視を行っておきDBに値が入ればGoogle Keepに追記を行う。
構成は以下のイメージです。
![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482413/39fa951a-dbce-6b59-51ac-8a2652e1169a.png)

### 環境
 - Raspberry Pi
 - Raspbian Buster
 - Kernel version:4.19
 - Python 3.7.2

### 利用サービス
- Google Assistant
- IFTTT
- Firebase Realtime Database
- gkeepapi 0.11.13

### 前準備
#### Google Keepにメモを作成
我が家では「買い物リスト」と「やることリスト」を作成し、家族のGoogleアカウントに共有をかけました。

#### Firebaseの設定
設定に際し以下の記事を参考にさせていただきました。
[新着メールを知らせてくれるGoogle Home](https://qiita.com/tetotetote/items/4d6ac866b716695fa4c9)
私の場合はDatabaseの構成は以下のような形です。

```
xxxxx(プロジェクト名)
 └ googlehome
 　 └ shop_memo: ""
 　 └ task_memo: ""
```
これで、以下のURLにリクエストを行うとパラメータの値でDatabaseが更新されます。とても便利…。
https://xxxxx(プロジェクト名).firebaseio.com/googlehome.json

#### IFTTTのApplet作成 
Google Assistantの特定のワードをトリガーにweb requestを行うAppletを作成します。
<img width="200" alt="1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482413/88e1e9c9-1b4f-881b-8a45-9fb16ece31d8.png"> <img width="200" alt="2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482413/665b14b5-95f2-bd4b-3699-87184b0b6795.png"> <img width="200" alt="3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482413/3972eea5-7c1b-c0ef-f230-c8dbc7887e3f.png">
URLは先程作成したFirebaseへのリクエストURLです。
部分更新でよいのでMethodは「PATCH」をContent Typeは「applecation/json」を選択し、BODYのパラメータで対象のキーの値を更新しています。

### 実装
#### Raspberry PIの環境構築
[gkeepapi](https://github.com/kiwiz/gkeepapi)のモジュールを追加します。

```
$ pip install gkeepai
```

Raspberry PI上の任意の場所で、ディレクトリ（firebaseなど）を作成し、[firebase](https://firebase.google.com/docs/web/setup?hl=ja)のモジュールを追加します。

```
$ npm init
$ npm install --save firebase
```

###### メモのIDを取得する
スクリプトでアカウントのログインを行うため、まずは[アプリパスワード](https://support.google.com/accounts/answer/185833?hl=ja)を作成します。
次に、取得したパスワードを元にRaspberry PI上で以下のスクリプトを作成してGoogle KeepのメモIDを取得します。

```python:check.py
# coding: utf-8
## GoogleKeepの一覧を表示
import gkeepapi
import sys

#ログイン
keep = gkeepapi.Keep()
success = keep.login('アカウント','アプリパスワード')

gnotes = keep.all()
for item in gnotes:
    print(item.title)
    print(item.id)
```
これを実行すると以下のようにメモごとのIDが確認できます。

```
やることリスト
XXXXXXXXXXXXX.XXXXXXXXX
買い物リスト
XXXXXXXXXXXXX.XXXXXX.XXXXXXXXXX
…
```
余談ですが、最初にMac Bookでgkeepapiを試した際に `gkeepapi.exception.LoginException: (u'CaptchaRequired', None)` となりうまく取得できませんでしたが、pythonのバージョンが古くリスク検知されていたようです。([開発者の方が回答していました](https://www.bountysource.com/issues/83332726-loginexception-captcharequired-since-a-few-days))
pyenvでpythonのバージョンを`3.7.2`に変更したところ無事取得できました。

### スクリプトの実装
firebaseディレクトリにスクリプトを作成します。
node.jsでfirebaseの監視を行い、DBの更新があればgkeepaiのpythonスクリプトを呼び出します。

まずはGoogle Keepへのメモ内容をパラメータとして受け取るpythonスクリプトを作成します。

```python:shop_memo.py
# coding: utf-8
import gkeepapi
import sys

# メモの内容
value = sys.stdin.readline().strip()

keep = gkeepapi.Keep()
success =keep.login('アカウント','アプリパスワード')
glist = keep.get('メモのID')
# 末尾にメモを追加
glist.add(value, False, gkeepapi.node.NewListItemPlacementValue.Bottom)
# 同期
keep.sync()
```
同様にtask_memo.pyも作成します。

次にfirebaseを監視するindex.jsを作成します。

```js:index.js
var firebase = require("firebase");
var {PythonShell} = require('python-shell');

//firebase config　※自分の環境に合わせて設定してください。
var config = {
  apiKey: "xxxxxxxxxxxxx",
  authDomain: "xxxxxx.firebaseapp.com",
  databaseURL: "https://xxxxx.firebaseio.com",
  projectId: "xxxxx",
  storageBucket: "xxxxx.appspot.com",
  messagingSenderId: "xxxxxxxxxxxx"
};
firebase.initializeApp(config);

const path = "/googlehome";
const shop_key = "shop_memo";
const task_key = "task_memo";
const db = firebase.database();
var googleHomeRef = db.ref(path);

// 買い物リスト
googleHomeRef.on("value", function(changedSnapshot) {
  const shop_value = changedSnapshot.child(shop_key).val();
  if (shop_value) {
    console.log(shop_value);

    var options = {
      mode: 'text',
      pythonPath: '/usr/bin/python3',
      pythonOptions: ['-u'],
    };

    var pyshell = new PythonShell('shop_memo.py',options);
    var json = {
    "key": shop_value
     }
    pyshell.send(shop_value) ;

    //firebase clear
    db.ref(path).update({[shop_key]: ""});
  }
});

// やることリスト
googleHomeRef.on("value", function(changedSnapshot) {
  const task_value = changedSnapshot.child(task_key).val();
  if (task_value) {
    console.log(task_value);

    var options = {
      mode: 'text',
      pythonPath: '/usr/bin/python3',
      pythonOptions: ['-u'],
    };

    var pyshell = new PythonShell('task_memo.py',options);
    var json = {
    "key": task_value
     }
    pyshell.send(task_value) ;

    //firebase clear
    db.ref(path).update({[task_key]: ""});
  }
});
```
こちらのjsから先程のpythonを呼んでいます。

### node.jsの永続化
ターミナルを抜けても index.js は常時起動しておきたいため、foreverを追加します。

```
$ npm install -g forever
$ forever start index.js
```

これで準備が整いました。Google Homeに話しかけてみましょう。

### 動作確認
私「買い物リストに テスト3。」
グ「買い物リストに テスト3 って送ったよ。」

![firebase_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482413/b0eaf6ac-cd4a-3728-cdb1-a08135f6bd9a.png)
firebaseのDBに一瞬値が登録されて…
<img width="200" alt="shopmemo1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482413/fda5b893-15a9-6c66-2030-de6c69973514.png">　　<img width="50" alt="arrow2.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482413/bc41caeb-7021-7ba9-c92f-4ab962289eb3.jpeg">　　<img width="200" alt="Image from iOS.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482413/1b763cc3-0afe-cf11-cc10-035ac1abf14f.png">
Google Keepが更新されました！ めでたしめでたし。

### さいごに
Firebase Databaseは想像以上に導入が容易でした。
DBを使えばGoogle Homeからメモの一覧を取得して読み上げてもらうことも簡単にできそうなので気が向いたらやってみようと思います。

### 使ってみた感想
実際に導入してから数ヶ月使用していますが Google Keep に移行したことで一番良かったことはチェックボックスリストのメモの場合、買い物途中にカゴに入れてGoogle Keepでチェックをすると選択中の項目が下に移るところです！（Google Home関係ない）
<img width="200" alt="shopmemo2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482413/30945511-a258-0435-5715-da09e34124d7.png">
これで買い物の途中にメモの中でどこまで買ったっけ…みたいな事がなくなりました。（にっこり