Raspberry PiでGoogle Calendarの予定を読み上げさせる 

#はじめに
家にRaspberry Piが眠っていたので遊んでみようと思った。ただそれだけ。

####環境
- Raspberry Pi 2 model B
- 8GB SDカード
- 有線キーボード（研究室の）
- 有線マウス（同上）
- wifi子機（1000円くらいのUSBスロットに挿すタイプのやつ）
- [github](https://github.com/ryupy/google_calendar)

####参照サイト
https://karaage.hatenadiary.jp/entry/2017/09/20/073000
https://www.infiniteloop.co.jp/blog/2014/03/yukkuri-hisho/
https://dev.classmethod.jp/cloud/google-calendar-api-get-start/
https://www.rs-online.com/designspark/raspberry-pi-japanese
https://qiita.com/shu-maibentou/items/6d2e55ffd8907deba843
https://lib.hatenablog.com/entry/2018/09/30/raspberry_pi%E3%81%A7Google%E3%82%AB%E3%83%AC%E3%83%B3%E3%83%80%E3%83%BC%E5%8F%96%E5%BE%97%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%AE%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97

#起動からセットアップまで
SDカードにraspbian 2015 GUI版がインストールしてあったので、これを使う（当時インストールした圧縮ファイルを解凍しただけで、「なんで動かねぇんだ...」と思っていたのは内緒）。
研究室のwinマシンでwin32 disk imagerを使って、SDカードにimgファイルを書き込む。
それを本体に突っ込んで起動完了。

####日本語化
起動したはいいけど全部英語なので、設定からlocation、timezone、keyboardを良い感じにしてrebootしたら文字化けした。
調べてみると、日本語フォントをインスコしないとダメっぽい。

[参照](https://www.rs-online.com/designspark/raspberry-pi-japanese)

```terminal:LXTerminal
$ sudo apt-get update #アップデート
$ sudo apt-get upgrade #アップグレード
$ apt-get install ttf-kochi-gothic xfonts-intl-japanese xfonts-intl-japanese-big xfonts-kaname
$ reboot #再起動
```

これで日本語化完了。
次に、日本語入力をできるようにibus-mozcとかいうのをインスコする。

```terminal:LXTerminal
$ apt-get install ibus-mozc
```
rebootしたかは忘れた。


####GUIが立ち上がらない
確かここらへんで、なぜかGUI版が立ち上がらなくなった（ラズベリーが4つでてくる黒いコンソール画面は起動するが、そこから画面が黒いまま）。
症状そのままのqiitaの記事があったので、同じことをしてみると起動するようになった。

[参照](https://qiita.com/shu-maibentou/items/6d2e55ffd8907deba843)
まず、Ctrl + Alt + F1でCUIを立ち上げる。

```terminal:LXTerminal
$ df -h #どっかの項目が100％になっていた。

$ sudo raspi-config
```

expand filesystemを押して終了させるとちゃんと立ち上がった。


####ネット環境
他の人の記事を見てるといろいろやってるが、うちの子は有線LANを挿しても、無線子機を挿しても、再起動すれば勝手にネットに繋いでくれた（IPアドレスの固定化をするならちゃんと設定しないといけないのかも）。

ここまでやればraspberry pi はちゃんと動くと思う。

#Google Calendarの予定を取りに行く
####pythonとpipのインスコ
[ここ](https://karaage.hatenadiary.jp/entry/2017/09/20/073000)を丸パクリした。

```terminal:LXTerminal 
$ sudo apt-get install python-pip #pythonとpipのインスコ
$ pip install google-api-python-client #pythonでgoogle api を叩きに行くためのライブラリ？
```

####OAuth認証でgoogle calendar apiに接続
上記のサイトを見ると、[ここ](https://dev.classmethod.jp/cloud/google-calendar-api-get-start/)のSTEP1を進めて、jsonファイルを持ってくるらしい。持ってきたjsonファイルの名前を変えておく。

####pythonでgoogle calendar を叩く
また[ここ](https://karaage.hatenadiary.jp/entry/2017/09/20/073000)を見ながら進めていく。
このブログの作者が書いてくれているプログラムをそのまま流用して実行。

```terminal:LXTerminal
$ wget https://raw.githubusercontent.com/karaage0703/karaage-echo/master/get_gcal_schedule.py
$ python get_gcal_schedule.py
```

が走らない。この記事の下の方に[同じ症状の人](https://lib.hatenablog.com/entry/2018/09/30/raspberry_pi%E3%81%A7Google%E3%82%AB%E3%83%AC%E3%83%B3%E3%83%80%E3%83%BC%E5%8F%96%E5%BE%97%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%AE%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97)がいたので参考にする

```terminal:LXTerminal
$ sudo pip install --upgrade oauth2client --ignore-installed six
```

次に、[この記事](https://dev.classmethod.jp/cloud/google-calendar-api-get-start/)のSTEP3にあるquickstart.pyを持ってくる。（githubの方に上がっているquickstart.pyは中身が違うっぽいので、記事内にあるコードをそのままコピペするのが無難か？）

で実行

```terminal:LXTerminal
$ python quickstart.py
```

これでOK。

#読み上げてくれ！
[ここ](https://www.infiniteloop.co.jp/blog/2014/03/yukkuri-hisho/)を見ると、aquestalkpiというのが読み上げソフトとして使えるらしい。
ダウンロードして解凍。
使い方は[ここ](http://blog-yama.a-quest.com/?eid=970157)を参照。

例えば、カレントディレクトリにこれまでの全てのファイルを置いていたとすると、

```terminal:LXTerminal
~ $ python get_gcal_schedule.py | aquestalkpi/AquestalkPi -f - | aplay #pythonの出力結果をaquestalkpiにパイプし、それをさらに、aplayにパイプ
```
みたいな感じで書けばOK。

#時間通りにな！
今回は研究室のカレンダーに入っている予定を読み上げさせたかった。（~~私は昼出勤だが~~）当ラボは朝10時から一応始まるので、朝10時に予定を読み上げてくれるようにしたい。こういった、時間をトリガーにしてプログラムを実行させるのにはcronとやらを使うと良いらしい。
ラズパイの場合、

```terminal:LXTerminal
$  crontab -e
```

でcronの編集ができる。
詳しい解説については[ここ](https://www.express.nec.co.jp/linux/distributions/knowledge/system/crond.html)らへんを読めば大丈夫。

ここまで完成したらあとはモニターやらキーボードを全部引っこ抜いて、スピーカーを繋げば完成！
（最初、スピーカーを繋いでも音が出なかったのでかなり焦ったが、GUI版のラズパイの場合は、右上にあるスピーカーのアイコンを右クリックすれば音声の出力先が選べるので注意です）

#ちょっとコードをいじってみた
[karaage0703さん](https://github.com/karaage0703)が公開しているget_gcal_schedule.pyを少しいじった。

```python:get_gcal_schedule
# -*- coding: utf-8 -*-
from __future__ import print_function
import httplib2
import os

from apiclient import discovery
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage

import datetime

# try:
#     import argparse
#     flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
# except ImportError:
#     flags = None

# If modifying these scopes, delete your previously saved credentials
# at ~/.credentials/calendar-python-quickstart.json
SCOPES = 'https://www.googleapis.com/auth/calendar.readonly'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Google Calendar API Python Quickstart'


def get_credentials():
    """Gets valid user credentials from storage.

    If nothing has been stored, or if the stored credentials are invalid,
    the OAuth2 flow is completed to obtain the new credentials.

    Returns:
        Credentials, the obtained credential.
    """
    home_dir = os.path.expanduser('~')
    credential_dir = os.path.join(home_dir, '.credentials')
    if not os.path.exists(credential_dir):
        os.makedirs(credential_dir)
    credential_path = os.path.join(credential_dir,
                                   'calendar-python-quickstart.json')

    store = Storage(credential_path)
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
        flow.user_agent = APPLICATION_NAME
        if flags:
            credentials = tools.run_flow(flow, store, flags)
        else: # Needed only for compatibility with Python 2.6
            credentials = tools.run(flow, store)
        print('Storing credentials to ' + credential_path)
    return credentials

def get_schedule():
    """Shows basic usage of the Google Calendar API.

    Creates a Google Calendar API service object and outputs a list of the next
    10 events on the user's calendar.
    """
    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    service = discovery.build('calendar', 'v3', http=http)

    # now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
    now = datetime.datetime.now()    
    #print(now)
    today_start = str(now.year) + '-' + str(now.month).zfill(2) + '-' + str(now.day).zfill(2) + 'T00:00:00+09:00'
    today_end = str(now.year) + '-' + str(now.month).zfill(2) + '-' + str(now.day).zfill(2) + 'T23:59:59+09:00'
    
    weekday = datetime.date.today().weekday() #曜日の取得
    weekday_list = ["月","火","水","木","金","土","日"]


    eventsResult = service.events().list(
        calendarId='primary', timeMin=today_start, timeMax=today_end, singleEvents=True,
        orderBy='startTime').execute()
    events = eventsResult.get('items', [])

    event_text = '。おはようございます。十時です。本日は、' + str(now.year) + '年、' + str(now.month) + '月、' + str(now.day) + '日、' + str(weekday_list[weekday]) + '曜日、です。今日の、研究室行事は、'
    
    if not events:
        event_text += 'ありません。'
	
	if weekday == 0:
	    event_text += '今週も一週間、がんばりましょう！'
        print(event_text)
        return event_text

    else:
        for event in events:
            start = event['start'].get('dateTime', event['start'].get('date'))
	    event_text += str(start[11:13])+'時'+str(start[14:16])+'分、から、'
            event_text += event['summary'].encode('utf_8')
	    if event == events[-1]:
		event_text += '。です。'
	    else:
		event_text += '。と、'
	
	if weekday == 0:
	    event_text += '今週も一週間、がんばりましょう！'
        print(event_text)
        return event_text

if __name__ == '__main__':
    get_schedule()
```

まず、weekdayを取得して、weekday_listに入れた曜日を引っ張る。
eventがeventsの最後の要素なら”です”をevent_textに追加し、それ以外なら”と”を追加。
また、weekday == 0（月曜日）ならば、”今週も一週間、がんばりましょう！”を追加。

また、[ここ](https://karaage.hatenadiary.jp/entry/2017/09/20/073000)に書いてあるように、calendarIdの値を"primary"から変更してあげれば、カレンダーに設定してあるべつの予定を取得できる。カレンダーIDは ____@group.calendar.google.com みたいな形になっていると思う。

さらに、当ラボは週末にエサをあげる当番を決めているので、金曜日の夜は、週末のエサ当番を教えてくれると嬉しい。なので、

```python:get_gcal_schedule.py
    tommorow = datetime.datetime.now() + datetime.timedelta(days = 1)
    day_after_tommorow = datetime.datetime.now() + datetime.timedelta(days = 2)
    tommorow_start = str(tommorow.year) + '-' + str(tommorow.month).zfill(2) + '-' + str(tommorow.day).zfill(2) + 'T00:00:00+09:00'
    tommorow_end = str(day_after_tommorow.year) + '-' + str(day_after_tommorow.month).zfill(2) + '-' + str(day_after_tommorow.day).zfill(2) + 'T23:59:59+09:00'
```

みたいにしてあげれば、明日と明後日の予定を引っ張ってこれるし、day_after_tommorowを

```python
two_days_after_tommorow = datetime.datetime.now() + datetime.timedelta(days = 3)
```

にしてあげれば三連休にも対応できる。

#終わりに
####感想
ラズパイは楽しい。次はラジコンとか作ってみたい。