Amazon Echoの買い物リストとGoogleカレンダーを連携させて日用品の買い物を効率化に挑戦してみた。


きっかけ

うちは共働きで休みはあまり一致しません。ある日、私は休みで妻は仕事、妻が夜遅くにドラッグストアに寄って買い物して帰ってきました。妻に「俺休みだから言ってくれたら買い物行ったのに」と言ったら「違うもの買ってこられたらイヤだし正確に伝えるのも面倒だし」と言われました。そこで、

  • 簡単に使えて。
  • なおかつ正確な情報を共有することができる。

仕組みを作ろうと思い立ちました。

現状の問題点

  1. 私に買い物をさせると違うものを買ってくることがある(同じブランドの違う香りの柔軟剤とか)。
  2. かといって正確に伝えるのはなかなかめんどくさい。

考えた解決策

  1. Amazon Echoの買い物リストに登録(「アレクサ、Xを買い物リストに登録して。」)。
  2. IFTTTを使って、Beebotteからメッセージ(先ほど買い物リストに登録したX)を送信。
  3. node-redでBeebotteからのメッセージを受け取る。
  4. node-redでXに対応した正式名称、および製品のURL(今回はAmazonの商品ページを利用)を呼び出す。
  5. 先ほど呼び出した正式名称、URL、日程をGoogleカレンダーに登録。

準備

  1. Beebotteへの登録
  2. IFTTTへの登録
  3. Echo、IFTTT、Beebotteの連携。こちらを参考にさせていただき、トリガーに「Amazon Alexa」の「Item added to your Shopping List」を選択し、 アクションに「Webhooks」の「Make a web request」を選択します。
  4. node-redに「node-red-contrib-iconv」「node-red-node-google」を追加します。
  5. Google APIの有効化。こちらや先ほどの「node-red-node-google」の説明を参考にGoogle APIを有効化します。なお、今回はGoogleカレンダーの機能しか使いませんが、「Google Calendar API」「Directions API」「Google+ API」の3つをすべて有効化しないとうまく動作しませんでした。

実装

node-redで以下のように実装してみました。

[
    {
        "id": "f5b1471d.ab6498",
        "type": "tab",
        "label": "Alexa To Googleカレンダー",
        "disabled": false,
        "info": ""
    },
    {
        "id": "60b27da2.1831bc",
        "type": "mqtt in",
        "z": "f5b1471d.ab6498",
        "name": "",
        "topic": "To_GoogleCalandar/Voice",
        "qos": "2",
        "broker": "7f824df3.706b1c",
        "x": 115,
        "y": 25.5,
        "wires": [
            [
                "d8645898.23e9a8"
            ]
        ]
    },
    {
        "id": "9d00040c.9916c",
        "type": "converter",
        "z": "f5b1471d.ab6498",
        "name": "ISO to UTF-8",
        "from": "UTF-8",
        "x": 376,
        "y": 26.5,
        "wires": [
            [
                "32fec42b.9a34a4"
            ]
        ]
    },
    {
        "id": "34a60097.c3ae4",
        "type": "google calendar out",
        "z": "f5b1471d.ab6498",
        "google": "",
        "name": "買い物リスト",
        "calendar": "買い物リスト",
        "x": 564,
        "y": 229,
        "wires": [],
        "inputLabels": [
            "msg.payload"
        ]
    },
    {
        "id": "479ec6e9.0666f8",
        "type": "function",
        "z": "f5b1471d.ab6498",
        "name": "Googleカレンダー登録下処理",
        "func": "//カレンダーへ流し込むためのデータの準備\nvar csv_data = flow.get('csv_data');\nvar itmB;\nitmB = msg.payload.data.slice(1);\n//console.log(itmB);\nvar summary = csv_data[itmB][0];\nvar hiduke = new Date();\n\n//getDayは曜日、日はgetDateを使う。\nvar sy = hiduke.getFullYear();\nvar sm = hiduke.getMonth()+1;\nvar sd = hiduke.getDate();\n\n//期間は1週間(7日後)に設定する。\nhiduke.setDate(hiduke.getDate()+7);\nvar ey = hiduke.getFullYear();\nvar em = hiduke.getMonth()+1;\nvar ed = hiduke.getDate();\n\n//カレンダー登録用のJSON作成\nvar event = {\n    'summary' : summary + 'を買う。',\n    'start' : {\n        'date': sy +'-'+ sm +'-'+ sd,\n    },\n    'end' : {\n        'date' : ey + '-' + em + '-' + ed,\n    },\n    'description' : csv_data[itmB][0] + '\\n' + csv_data[itmB][1],\n}\nmsg.payload = event ;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 427,
        "y": 162.5,
        "wires": [
            [
                "34a60097.c3ae4"
            ]
        ]
    },
    {
        "id": "ec6fd5ba.a52ce",
        "type": "file in",
        "z": "f5b1471d.ab6498",
        "name": "買い物リスト",
        "filename": "/home/mkim/List.csv",
        "format": "lines",
        "chunk": false,
        "sendError": true,
        "x": 145,
        "y": 229.5,
        "wires": [
            [
                "7d22551c.1d8bd4"
            ]
        ],
        "inputLabels": [
            "List.csv"
        ]
    },
    {
        "id": "d8645898.23e9a8",
        "type": "function",
        "z": "f5b1471d.ab6498",
        "name": "Data_Convert",
        "func": "//ConvertノードはBuffer型のみ受け付けるため、変換を行う。\nvar buf1 = new Buffer(msg.payload,'ascii');\nmsg.payload = buf1;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 236,
        "y": 87.5,
        "wires": [
            [
                "9d00040c.9916c"
            ]
        ]
    },
    {
        "id": "a18760fd.d061a",
        "type": "inject",
        "z": "f5b1471d.ab6498",
        "name": "",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": "1",
        "x": 125,
        "y": 154.5,
        "wires": [
            [
                "ec6fd5ba.a52ce"
            ]
        ]
    },
    {
        "id": "7d22551c.1d8bd4",
        "type": "function",
        "z": "f5b1471d.ab6498",
        "name": "Data_Set",
        "func": "var csv_data = flow.get('csv_data')||{};\nvar strBUY = [];\nvar strSrc = msg.payload;\nstrBUY = strSrc.split(',');\ncsv_data[strBUY[0]] = [strBUY[1],strBUY[2]];\nflow.set('csv_data',csv_data);\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 330,
        "y": 237.5,
        "wires": [
            []
        ]
    },
    {
        "id": "32fec42b.9a34a4",
        "type": "json",
        "z": "f5b1471d.ab6498",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 454,
        "y": 89.5,
        "wires": [
            [
                "479ec6e9.0666f8"
            ]
        ]
    },
    {
        "id": "d34e8227.82dfa",
        "type": "comment",
        "z": "f5b1471d.ab6498",
        "name": "デプロイしたらInjectionをクリックし、データ更新",
        "info": "",
        "x": 225,
        "y": 295.5,
        "wires": []
    },
    {
        "id": "7f824df3.706b1c",
        "type": "mqtt-broker",
        "z": "",
        "name": "Beebotte For Google Calendar",
        "broker": "mqtt.beebotte.com",
        "port": "8883",
        "tls": "",
        "clientid": "",
        "usetls": true,
        "compatmode": false,
        "keepalive": "60",
        "cleansession": true,
        "willTopic": "",
        "willQos": "0",
        "willPayload": "",
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": ""
    }
]

これで何とか動いてくれました(多分)。

苦労したところ

  • IFTTTを使ったらすぐにできると思っていましたが、細かい設定ができないうえに日本語が化けてしまうので大変でした。対策としてnode-red-contrib-iconvを追加して文字コードの変換を行い、無事に表示できるようになりました。
  • node-red、node.jsどころかjavascriptもほとんど触ったことがなかったので一から勉強となって大変でした。特に型の概念について、javascriptは型の概念がないという間違った認識に捉われていたので文字コードの変換がうまくいかず苦労しました。

効果

まだ作ったばかりで妻には披露していません。多分便利になる、はずです。なるといいな。喜んでくれるといいな。

最後に

ほとんど一からの勉強で大変でしたが、とても楽しかったです。昔に比べると便利なものや情報がたくさんあってとても良い時代になったと思います。
自分もいろいろなところで情報をいただき大変お世話になったので今回の経験が他の人の参考に少しでもなればと思い、拙いながらもこの記事を書いてみました。
もし変なところがありましたらご指摘いただけますと幸いです。