メールで
仕事では殆どメールを見る機会はないのですが、通知系のメールに気が付かなかったという経験をしたことがある人は多いはず。
今回はGmailのAPIを使ってメールを定期的に受信して、条件に合うメールの場合は音声で通知できる仕組みを Raspberry Pi 3
とGamil API
を利用して作ってみたいと思います。
Gamil API
利用を開始するまでにはいくつかステップがあって、いろいろなBlogで書かれていますが上記の公式サイトにあるQuickstart
で、利用する言語毎に書かれているドキュメントの通りに進めるのをおすすめします。公式ドキュメントが充実しているのと最新の情報が記載されているため、手戻りすることなく進めることができます。
今回は当然大好きなgolang
を利用するので以下に沿って進めていきます
Go Quickstart | GmailAPI | Google Developers
https://developers.google.com/gmail/api/quickstart/go
準備
Google Developers Console
まずはGoogle Developers Console
にアクセスして
Google API Console
に移動します。
表示されたAPIの一覧からGmail API
を選択します。
プロジェクトが存在しないとAPIを有効化できないので、先にプロジェクトを作成します。
既存のプロジェクトでAPIを有効かする場合には、当然ですが新規にプロジェクトを作成する必要はありません。
APIを有効化しただけでは利用ができません。認証情報を作成する必要があります。
OAuth クライアントIDを取得するのですが、先に同意画面の設定が必要になります。
クライアントIDのアプリケーションの種類はその他を選択します。
設定が完了すると認証に必要な情報をJSONファイルとしてダウンロードすることができるので、ダウンロードして保存します。
詳細は以下の公式ドキュメントに書かれています
Authorizing Your App with Gmail | GmailAPI | Google Developers
https://developers.google.com/gmail/api/auth/about-auth
Implementing Server-Side Authorization | GmailAPI | Google Developers
https://developers.google.com/gmail/api/auth/web-server
Choose Auth Scopes | GmailAPI | Google Developers
https://developers.google.com/gmail/api/auth/scopes
golang packages
次にgolangで必要になるパッケージを以下のコマンドを実行して取得します
$ go get -u google.golang.org/api/gmail/v1 $ go get -u golang.org/x/oauth2/...
実際に取得されるのは以下のパッケージになります。
パッケージの利用方法は、それぞれのパッケージに用意されているドキュメントを確認してください。 サンプルについてもパッケージのリポジトリにあるので参考になるかと思います。
動作確認
続けて動作と認可の確認を行います。
Go Quickstart | GmailAPI | Google Developers
https://developers.google.com/gmail/api/quickstart/go#step_3_set_up_the_sample
上記のサンプルコードをそのまま利用するのですが、最初の工程でダウンロードしたGmailAPIのクライアントIDのJSONファイルが必要になります。
サンプルコードをビルドして実行すると、コンソールに
Go to the following link in your browser then type the authorization code:
のメッセージと一緒にURLが表示されます。このURLが認可ページのURLになるのでブラウザにコピペしてページを表示します。
ブラウザに表示したら認可を与えるアカウントを選択します
認可の内容を確認します。今回は読み取りのみを指定しています。
問題がなければ許可をして、その後の画面に表示されるコードをターミナルに貼り付けます
これで次回の動作からはクライアント側に保存されたtokenを利用してアクセスすることができるようになります。
作ったもの
実際に作ってみたものは以下の写真のような感じに
使ったのは
- Raspberry Pi 3 MODEL B
- USB電源スピーカー(MS-UP201BK)
- USB電源LEDライト(100円ショップ)
ぐらいで、ラズパイはAmazon Echoもどきで遊んだ時のやつを再設定して利用しましたし、LEDライトは100円ショップで適当に買ったやつですし、唯一スピーカーが手元になかったので700円ぐらいのものを買った感じですね。
ラズパイだと情報も多いですし、選択肢もいろいろとあるんですが手間もかけずに適当に作れるのはいいですねー
Raspberry Pi 3 MODEL B
特別なことは何もしておらず、普通にRaspbianのLITEをインストールしてsshで接続できるように設定しました。
hub-ctrl
USBの電源をON/OFFするために、以下を参考して
Raspberry Pi B+ turn usb power off - Raspberry Pi Forums
https://www.raspberrypi.org/forums/viewtopic.php?f=29&t=93463
hub-ctrl をインストールしてあります。
ラズパイ3だとUSBの個別ポートで電源のON/OFFができると記載されている 記事も目にしましたが、手元のラズパイだと
pi@raspberrypi:~ $ hub-ctrl Hub #0 at 001:000 INFO: ganged switching. WARN: Port indicators are NOT supported. Hub #1 at 001:000 INFO: ganged switching. WARN: Port indicators are NOT supported.
pi@raspberrypi:~ $ lsusb -v Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter Couldn't open device, some information will be missingDevice Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 255 Vendor Specific Class bDeviceSubClass 0 bDeviceProtocol 1 bMaxPacketSize0 64 idVendor 0x0424 Standard Microsystems Corp. idProduct 0xec00 SMSC9512/9514 Fast Ethernet Adapter bcdDevice 2.00 iManufacturer 0 iProduct 0 iSerial 0 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 39 bNumInterfaces 1 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xe0 Self Powered Remote Wakeup MaxPower 2mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 3 bInterfaceClass 255 Vendor Specific Class bInterfaceSubClass 0 bInterfaceProtocol 255 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x83 EP 3 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0010 1x 16 bytes bInterval 4Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.Couldn't open device, some information will be missing Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 9 Hub bDeviceSubClass 0 Unused bDeviceProtocol 2 TT per port bMaxPacketSize0 64 idVendor 0x0424 Standard Microsystems Corp. idProduct 0x9514 bcdDevice 2.00 iManufacturer 0 iProduct 0 iSerial 0 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 41 bNumInterfaces 1 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xe0 Self Powered Remote Wakeup MaxPower 2mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 9 Hub bInterfaceSubClass 0 Unused bInterfaceProtocol 1 Single TT iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0001 1x 1 bytes bInterval 12 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 1 bNumEndpoints 1 bInterfaceClass 9 Hub bInterfaceSubClass 0 Unused bInterfaceProtocol 2 TT per port iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0001 1x 1 bytes bInterval 12 Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Couldn't open device, some information will be missingDevice Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 9 Hub bDeviceSubClass 0 Unused bDeviceProtocol 1 Single TT bMaxPacketSize0 64 idVendor 0x1d6b Linux Foundation idProduct 0x0002 2.0 root hub bcdDevice 4.09 iManufacturer 3 iProduct 2 iSerial 1 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 25 bNumInterfaces 1 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xe0 Self Powered Remote Wakeup MaxPower 0mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 9 Hub bInterfaceSubClass 0 Unused bInterfaceProtocol 0 Full speed (or root) hub iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0004 1x 4 bytes bInterval 12
な感じなんですが、個別のON/OFFは残念ながらできませんでした。
USBの規格としてPer-port power switching
というのに対応していると個別のON/OFFが可能みたいです。調べた限りだと
スゴイハブ USB2-HUB4Xシリーズ 製品情報
http://www.system-talks.co.jp/product/sgc-4x.htm
が動作実績がありそうな感じでした。
AquesTalk Pi
日本語を音声合成するためのTTSとして、以下のAquesTalk Piをインストールしてあります
AquesTalk Pi - Raspberry Pi用の音声合成
https://www.a-quest.com/products/aquestalkpi.html
今回はなるべく手軽に実現したかったのでAquesTalk Piを選択しましたが、他にも
Open JTalk
http://open-jtalk.sourceforge.net/
なんかを利用している人が多いようです。
また、クラウドサービスを使って音声合成をすることももちろん可能なのですが、今後家のネットワーク以外の例えばSORACOM Air
なんかを利用したい場合を考えると、ラズパイ側で処理できるなら処理した方が良いかなーと。
クラウド側で処理する場合は
あたりが定番でしょうか?
意外だったのはGoogleにはTTSのAPIがなさそということでしょうか・・・音声認識はあるので、内部的には当然あるんでしょうけど。
あとは、crontabで作ったプログラムの定期実行と起動時のUSBのコントロールを行っているぐらいです
pi@raspberrypi:~ $ crontab -l# Edit this file to introduce tasks to be run by cron.## Each task to run has to be defined through a single line# indicating with different fields when the task will be run# and what command to run for the task## To define the time you can provide concrete values for# minute (m), hour (h), day of month (dom), month (mon),# and day of week (dow) or use '*' in these fields (for 'any').## Notice that tasks will be started based on the cron's system# daemon's notion of time and timezones.## Output of the crontab jobs (including errors) is sent through# email to the user the crontab file belongs to (unless redirected).## For example, you can run a backup of all your user accounts# at 5 a.m every week with:# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/## For more information see the manual pages of crontab(5) and cron(8)## m h dom mon dow command# ggm */3 * * * * /home/pi/ggm/ggm #set init @reboot amixer set PCM 100% @reboot sudo hub-ctrl -h0-P2-p0
golang program
やっていることはGmailをAPI直接ではなく、golangのSDKを利用して受信しているだけです。
特に変わったこともしていないので、何の参考にもならないかもしれませんが・・・
動作の設定で記述が必要なのは.ggm/user_config.json
ファイルだけです。
{"LastDate": 1504243346130, "LastTotal": 749, "UserEmail": "test@example.com", "SpeakCommands": [["/home/pi/aquestalkpi/AquesTalkPi", "-s", "120", "%s"],["aplay"]], "UsbCommands": [["sudo", "hub-ctrl", "-h", "0", "-P", "2", "-p", "%d"]], "Filters": [{"From": "user1@example.com", "Subjects": ["test","user1"]}, {"From": "user2@example.com", "Subjects": null }] }
SpeakCommands
には音声合成するためのコマンドを記載しますが、配列で1つのコマンドを表して、配列を配列に入れることでパイプでつないで実行します。なので、上記の例だと
$ /home/pi/aquestalkpi/AquesTalkPi -s120 %s | aplay
のコマンドを実行していることになります。コマンド中の%s
にはGmailの受信した内容が入ります。
UsbCommands
にはUSB電源管理するためのコマンドを記載します。形式は前述の通りです。コマンド中の%d
はON/OFFの数値が入ります。
Filters
には音声合成対象のメール条件を記載します。From
には送信元のメールアドレスを、Subjects
には配列で件名を記載します。特に指定がされていない場合にはすべてのメールが対象となります。
cross compile
Raspberry Pi 3 用にgolangをコンパイルする際には
Installing Go from source - The Go Programming Language
https://golang.org/doc/install/source#environment
を参考にして環境変数を指定するわけですが、$GOARM
に指定する値はラズパイ上で以下のコマンドを実行すると確認できます。
pi@raspberrypi:~ $ uname -m
armv7l
どうやらv7らしいので
$ GOOS=linux GOARCH=arm GOARM=7 go build -v
とすれば、今回の利用するライズパイ上で動作するバイナリがビルドできることになります。
動作デモ
で、実際に動作するとどうなるのか?というと・・・
twitter.com音声合成できたー楽しい pic.twitter.com/ZRDM7dXPoG
— Manabu Uchida (@uchimanajet7) 2017年9月6日
な、感じになりました。思ったよりちゃんと音声合成できていて個人的には十分かなぁーと思いました。
まとめ
- Raspberry Pi 3 は結構サクサク動く
- これだけ動けばgolangでちょっとしたことは余裕そう
- 今回は簡単にやってみたかったので複雑なのは却下した
- Gmailだとpush通知を利用した方が良いかもしれない
Push Notifications | GmailAPI | Google Developers
https://developers.google.com/gmail/api/guides/push
- SORACOM Airを利用したい場合、USBドングルを使うことになるのでUSB電源をOFFにする動作が微妙
- 前述した
Per-port power switching
に対応したUSBハブを導入して回避する - USBをON/OFFしている理由は、雑音が聞こえることがあるのとUSBライトを受信時にだけに光らせたいから
- 予算や手間を気にしないならCrystal Signal Pi みたいなのを導入するのもありかもしれない
- 雑に扱っても大丈夫というのが1つの目的だったので自作はなるべくなし
- golangはいろいろできて楽しい
- 急に喋り出すので若干びっくりする
- 特に夜中とか怖いw
- 要望は叶えられたので便利にはなった
- また機会があればぜひなんかやってみたい
以上になります。