Raspberry Pi 3+Gmail APIでメールを受信して音声合成してみた #raspberrypi #gmail #golang

メールで

仕事では殆どメールを見る機会はないのですが、通知系のメールに気が付かなかったという経験をしたことがある人は多いはず。

今回はGmailAPI を使ってメールを定期的に受信して、条件に合うメールの場合は音声で通知できる仕組みを Raspberry Pi 3Gamil APIを利用して作ってみたいと思います。

Gamil API

developers.google.com

利用を開始するまでにはいくつかステップがあって、いろいろなBlogで書かれていますが上記の公式サイトにあるQuickstart で、利用する言語毎に書かれているドキュメントの通りに進めるのをおすすめします。公式ドキュメントが充実しているのと最新の情報が記載されているため、手戻りすることなく進めることができます。

今回は当然大好きなgolangを利用するので以下に沿って進めていきます

Go Quickstart  |  Gmail API  |  Google Developers
https://developers.google.com/gmail/api/quickstart/go

準備

Google Developers Console

developers.google.com

まずはGoogle Developers Consoleにアクセスして

f:id:uchimanajet7:20170904151423p:plain

Google API Consoleに移動します。

console.developers.google.com

表示されたAPIの一覧からGmail APIを選択します。

f:id:uchimanajet7:20170904152527p:plain

プロジェクトが存在しないとAPIを有効化できないので、先にプロジェクトを作成します。

f:id:uchimanajet7:20170904153616p:plain

既存のプロジェクトでAPIを有効かする場合には、当然ですが新規にプロジェクトを作成する必要はありません。

f:id:uchimanajet7:20170904163117p:plain

APIを有効化しただけでは利用ができません。認証情報を作成する必要があります。

f:id:uchimanajet7:20170904163950p:plain

OAuth クライアントIDを取得するのですが、先に同意画面の設定が必要になります。

f:id:uchimanajet7:20170904170220p:plain

クライアントIDのアプリケーションの種類はその他を選択します。

f:id:uchimanajet7:20170904174533p:plain

設定が完了すると認証に必要な情報をJSONファイルとしてダウンロードすることができるので、ダウンロードして保存します。

f:id:uchimanajet7:20170904175531p:plain

詳細は以下の公式ドキュメントに書かれています

Authorizing Your App with Gmail  |  Gmail API  |  Google Developers
https://developers.google.com/gmail/api/auth/about-auth

Implementing Server-Side Authorization  |  Gmail API  |  Google Developers
https://developers.google.com/gmail/api/auth/web-server

Choose Auth Scopes  |  Gmail API  |  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/...

実際に取得されるのは以下のパッケージになります。

github.com

github.com

パッケージの利用方法は、それぞれのパッケージに用意されているドキュメントを確認してください。 サンプルについてもパッケージのリポジトリにあるので参考になるかと思います。

動作確認

続けて動作と認可の確認を行います。

Go Quickstart  |  Gmail API  |  Google Developers
https://developers.google.com/gmail/api/quickstart/go#step_3_set_up_the_sample

上記のサンプルコードをそのまま利用するのですが、最初の工程でダウンロードしたGmail APIのクライアントIDのJSONファイルが必要になります。

サンプルコードをビルドして実行すると、コンソールに

Go to the following link in your browser then type the authorization code:

のメッセージと一緒にURLが表示されます。このURLが認可ページのURLになるのでブラウザにコピペしてページを表示します。

godoc.org

ブラウザに表示したら認可を与えるアカウントを選択します

f:id:uchimanajet7:20170904195633p:plain

認可の内容を確認します。今回は読み取りのみを指定しています。

f:id:uchimanajet7:20170904201054p:plain

godoc.org

問題がなければ許可をして、その後の画面に表示されるコードをターミナルに貼り付けます

f:id:uchimanajet7:20170904201716p:plain

これで次回の動作からはクライアント側に保存されたtokenを利用してアクセスすることができるようになります。

作ったもの

実際に作ってみたものは以下の写真のような感じに

f:id:uchimanajet7:20170902103822j:plain

使ったのは

  • Raspberry Pi 3 MODEL B
  • USB電源スピーカー(MS-UP201BK)
  • USB電源LEDライト(100円ショップ)

ぐらいで、ラズパイはAmazon Echoもどきで遊んだ時のやつを再設定して利用しましたし、LEDライトは100円ショップで適当に買ったやつですし、唯一スピーカーが手元になかったので700円ぐらいのものを買った感じですね。

ラズパイだと情報も多いですし、選択肢もいろいろとあるんですが手間もかけずに適当に作れるのはいいですねー

Raspberry Pi 3 MODEL B

www.raspberrypi.org

特別なことは何もしておらず、普通に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 をインストールしてあります。

github.com

ラズパイ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 missing
Device 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               4

Bus 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 missing
Device 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

blog-yama.a-quest.com

今回はなるべく手軽に実現したかったのでAquesTalk Piを選択しましたが、他にも

Open JTalk
http://open-jtalk.sourceforge.net/

なんかを利用している人が多いようです。

また、クラウドサービスを使って音声合成をすることももちろん可能なのですが、今後家のネットワーク以外の例えばSORACOM Airなんかを利用したい場合を考えると、ラズパイ側で処理できるなら処理した方が良いかなーと。

クラウド側で処理する場合は

aws.amazon.com

azure.microsoft.com

www.ibm.com

あたりが定番でしょうか?

意外だったのはGoogleにはTTSのAPIがなさそということでしょうか・・・音声認識はあるので、内部的には当然あるんでしょうけど。

cloud.google.com

あとは、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 -h 0 -P 2 -p 0

golang program

やっていることはGmailAPI直接ではなく、golangSDKを利用して受信しているだけです。

github.com

特に変わったこともしていないので、何の参考にもならないかもしれませんが・・・

動作の設定で記述が必要なのは.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 -s 120 %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

な、感じになりました。思ったよりちゃんと音声合成できていて個人的には十分かなぁーと思いました。

まとめ

  • Raspberry Pi 3 は結構サクサク動く
  • これだけ動けばgolangでちょっとしたことは余裕そう
  • 今回は簡単にやってみたかったので複雑なのは却下した
  • Gmailだとpush通知を利用した方が良いかもしれない

Push Notifications  |  Gmail API  |  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 みたいなのを導入するのもありかもしれない

crystal-signal.com

  • 雑に扱っても大丈夫というのが1つの目的だったので自作はなるべくなし
  • golangはいろいろできて楽しい
  • 急に喋り出すので若干びっくりする
  • 特に夜中とか怖いw
  • 要望は叶えられたので便利にはなった
  • また機会があればぜひなんかやってみたい

以上になります。