Googleアシスタントを使ったスマートホーム連携アプリケーションをAoGで作るハンズオン


## 0. Overview

2018/02/26 GDG Tokyoで行われたAoGのハンズオンの末尾で紹介していたAppendixのQiita版です。
こちらは多くの人に観てもらうために置いたものなので、ドキュメント自体の更新は以下のGoogle Documentsのみ行います。

https://docs.google.com/document/d/1WR8zTadFaiYHkE-yyuLodQU8k8QgzaRgMRM9tk6yncU/edit#

なお画像などもなるべくこちらに移植していますが、Documents版のほうが細かく書いてありますので、ご注意ください。


```
このコードラボでは、予め以下が済んでいることを前提としています。
* Chromeウェブブラウザがインストールされていること。
* Googleアカウントを持っていること。

Googleアシスタントで動作を試すには、以下の条件を満たしたスマートフォンが必要です。
Android 5.0以上のスマートフォン、あるいはGoogleアシスタントアプリがインストールされたiPhoneを持っていること。
```



## 1. Introduction

Actions on Googleを利用すれば、ユーザがGoogleアシスタントを利用して、IoTデバイスを制御できるスマートホームアプリケーションを作成できます。

スマートホームアプリを作成すると、既存のクラウドインフラを通じて、デバイスを接続、紹介、制御することができるようになります。


### ここでは何をしますか？

このデモではGoogleから公開されているサンプルコードを利用して、Actions on Google(以下AoG)上でのスマートホームアプリケーションの作成を作成します。


<img src='https://qiita-image-store.s3.amazonaws.com/0/2965/df87f354-07f1-220c-68b6-a216d3d921bb.png' width='50%'>

サンプルコード内では、物理デバイスを利用せず、Webブラウザ上に表現した、カラーランプ、通常のON/OFFのライト、ロボット掃除機やカメラなどの仮想のデバイスを使って、音声操作可能なスマートホームを実現します。


### 学ぶこと

✓AoGを使ったスマートホームアプリケーションの作り方

✓スマートホームアプリケーションサーバの構築方法


### 必要なスキル



*   AoGに関する知識
    *   [こちら](https://docs.google.com/document/d/1v8fTgutyJ3X5i3gW9FrRlJo9t8X9hUtTwabZx4CYhrE/edit#)を一度やっておいてください。
*   Web、Web API、OAuth2に関する基本知識
*   コーディング経験
    *   本ハンズオンではコーディングは行いませんが、コードを理解するために必要です。


### 必要なもの



*   以下がインストール済みのPC
    *   NodeJS 6.2.2以上
    *   bower (npm install -g bower でインストール)
    *   git
*   [Chrome](https://www.google.com/chrome/)の最近のバージョン \
これは、他のブラウザでも同様に実施できます
*   スマートフォン向けのGoogle Homeアプリ
    *   [Android](https://play.google.com/store/apps/details?id=com.google.android.apps.chromecast.app&hl=ja)
    *   [iOS](https://itunes.apple.com/jp/app/google-home/id680819774?mt=8)
*   Googleアカウント（例 Gmailアドレス）


### 言葉の定義

便宜上幾つかこのドキュメント内でのみ利用する言葉を定義します。

※Googleのドキュメント上も以下のようには書いてないので、このドキュメント内でのみ利用してください。



*   Googleアシスタントクライアント
    *   AndroidやiOSのアプリ、Google Homeなどのユーザとの接点を持つGoogleアシスタントサービスのクライアント
*   Googleアシスタントサービス
    *   Googleのインフラ上に存在するGoogleアシスタントが動いているクラウドサービス
*   スマートホームサービス
    *   フルフィルメントやOAuth2 Providerとして登録されるスマートホームアプリケーションを動かすためのクラウドサービス
    *   Googleアシスタントサーバからの各種インテントを受け取って処理する
*   スマートホームアプリケーション(Smart Home App)
    *   AoGで登録されたスマートホームのアプリケーション


## 2. Getting set up


### どのように動作しますか？

このデモではローカル上にスマートホームサービスのサーバを起動し、それを[ngrok](https://ngrok.com/)を利用して外部からアクセスできるようにすることで、Googleアシスタントからスマートデバイスを操作できるようにします。

ローカル上のスマートホームサービスではブラウザから仮想のスマートデバイスを登録することができ、Googleアシスタントで操作可能な様々なスマートデバイスを扱うことが出来ます。

![Kobito.CWVvMw.png](https://qiita-image-store.s3.amazonaws.com/0/2965/e3de5a3a-889d-460a-ada4-01d04eb8c673.png "Kobito.CWVvMw.png")
図0 - 今回はngrokを利用して、PC上で起動したローカルサーバに外部からアクセスできるようにする。ローカルサーバ内では可能のスマートデバイスを登録し、Googleアシスタントを使って、それらを操作できるようにする。


```
以下座学です。
すぐに試したい方はこちらへ
```



#### スマートホームアプリとHome Graph

スマートホームアプリはHome Graphに依存しています。
Home Graphとは、家(デバイスが置かれた場所 Home)とそこに置かれたデバイスの情報を持ったデータベースです。
例えば、Home Graphにはリビングルームに置かれている様々なメーカーから作成されたデバイス(ライト、テレビ、スピーカー等)の情報を保存されています。
その情報は適切な状況に基づいてユーザのリクエストを実行するために、Googleアシスタントに渡されます。


#### デバイスの同期

Googleアシスタントを通じて、スマートホームアプリを動作させる際、
まずユーザはGoogleが保持するHome Graphにデバイスを登録する必要があります。
ユーザはGoogleアシスタントクライアントを通じて、スマートホームサービスで認証し、デバイスを登録します。またその際に、Googleアシスタントサービスはスマートホームサービスを呼び出すためのOAuthトークンを取得します。

このタイミングで、Googleアシスタントサービスは登録されたフルフィルメント(Webhook)にaction.devices.SYNCインテントを送信してデバイスとその機能の初期リストを取得します。

![Kobito.ANFRjD.png](https://qiita-image-store.s3.amazonaws.com/0/2965/65419c7a-5b03-7846-3876-8dbe7c96a0fc.png "Kobito.ANFRjD.png")
図1 - 青はGoogleのインフラ、黄色はスマートホームアプリケーションプロバイダ(スマートホームサービス)のインフラ CC BY 3.0 from <a href="https://developers.google.com/actions/smarthome/">Google document</a>


またスマートホームサービス側から、Googleアシスタントサービスに対して、action.intent.REQUEST_SYNCインテントを使用して、デバイス上の再同期を依頼することが出来ます。

![Kobito.npvlgf.png](https://qiita-image-store.s3.amazonaws.com/0/2965/ee783111-f90c-d665-28da-a8c842e394ea.png "Kobito.npvlgf.png")
図2 CC BY 3.0 from <a href="https://developers.google.com/actions/smarthome/">Google document</a>

#### コマンドの実行

ユーザがGoogleアシスタントクライアントに対して、デバイスにコマンドを送るように依頼すると、Googleアシスタントサービスは、フルフィルメントにアクションと、そのデバイスを説明するペイロードを含むaction.devices.EXECUTEインテントを送信します。

![Kobito.n1drbi.png](https://qiita-image-store.s3.amazonaws.com/0/2965/efc8173c-4ae6-241b-2eec-ba833615681b.png "Kobito.n1drbi.png")

図3 CC BY 3.0 from <a href="https://developers.google.com/actions/smarthome/">Google document</a>



#### デバイスの状態を取得

ユーザが「キッチンのライトは点灯していますか?」などの質問を行ったり、「明るさをあげて」の様な相対的な輝度を扱うコマンドを送ると、Googleアシスタントサービスがフルフィルメントに対して、action.devices.QUERYインテントを送信します。

![Kobito.4GTqAG.png](https://qiita-image-store.s3.amazonaws.com/0/2965/f2addaac-414f-3a4d-971e-ea97c334eed7.png "Kobito.4GTqAG.png")

図4 CC BY 3.0 from <a href="https://developers.google.com/actions/smarthome/">Google document</a>


またスマートホームサービスはHome Graphに、現在のデバイスの状況を登録することが出来ます。 

![Kobito.wVcR99.png](https://qiita-image-store.s3.amazonaws.com/0/2965/fe5d64a8-a3b3-53d7-0e9f-62c09dddd746.png "Kobito.wVcR99.png")

図5 CC BY 3.0 from <a href="https://developers.google.com/actions/smarthome/">Google document</a>

```
座学以上です。
```



## 3. Create your first Smart Home Application

では実際にスマートホームアプリケーションを作成していきます。

### サンプルコードのクローン

以下をgit cloneしてください。

[https://github.com/actions-on-google/actionssdk-smart-home-nodejs](https://github.com/actions-on-google/actionssdk-smart-home-nodejs)

### プロジェクトの作成とセットアップ

Actions Consoleで新しいスマートホームアプリケーション(アシスタントアプリ)の登録を行います。

1.  [Actions Console](https://console.actions.google.com)にログインし、「Add/Import Project」をクリックします。
1.  新規にプロジェクトを作成するか、Google Cloud projectをインポートします。
1.  Google Cloud Consoleの[API Manager](https://console.developers.google.com/apis)を表示し、2.のプロジェクトを選択します。
1.  「APIとサービスの有効化」ボタンをクリックして、API一覧を表示し、「Google HomeGraph API」を有効にします。 
1.  左メニューから「認証情報」を選択し、「認証情報を作成」ボタンをクリック、「APIキー」を選択して、APIキーを作成します。  
このAPIキーは後ほど利用するのでどこかに保存しておいてください。

### ローカルサーバのセットアップ

ローカルで動くスマートホームサービスをセットアップします。

1.  git cloneしたリポジトリ内にある smart-home-provier/cloud/config-provider.js にある<API_KEY>と書かれた部分に上記で取得した、APIキーを設定します。 
1.  仮想スマートデバイスを動かすウェブポータルをセットアップします。  

```bash
cd smart-home-provider/frontend
npm install -g bower
bower install
cd ..
```

1.  ローカルサーバを起動します。  

```bash
npm install
npm start
```

1.  ローカルサーバを起動するとngrokにより、外部からアクセスできるURLが表示されますのでメモしておいてください。  
またOAuth2のクライアントID，クライアントシークレットも表示されるのでメモしておきます。
1.  一度表示されたURLにブラウザでアクセスし、以下のuser/passwordでログインします。  

```
user: rick
password: oldman
```

<img src='https://qiita-image-store.s3.amazonaws.com/0/2965/3e6863e9-8987-66d6-fa44-1a12b62b54c2.png' width="50%">

1.  ログインをしたら、ヘッダーの＋ボタンをクリックして、好きな仮想スマートデバイスを登録しておきます。名前も適当に日本語に変えておいたほうが後が楽です。  
<img src='https://qiita-image-store.s3.amazonaws.com/0/2965/564ecb7e-3a82-04c8-a18e-826110287234.png' width="50%">


**注意**

ngrokは起動ごとにURLが変わります。この後の設定後にローカルサーバを落としてしまうと、URLが変わり再度セットアップをする必要が出てしまいますので、このハンズオン中は一度ローカルサーバを起動しセットアップをしたら停止しないようにしてください。


### プロジェクトの設定

ローカルサーバがセットアップできたので、その情報をプロジェクトに設定していきます。



1.  Actions Consoleに戻り、「Smart Home」のBUILDをクリックします。  
<img src='https://qiita-image-store.s3.amazonaws.com/0/2965/d4729066-dacc-630f-fd2d-1c57c2d28f51.png' width="50%">
1.  FulfillmentのURLを登録する画面が表示されるので、{先程表示されたURL}/smarthome を設定し、DONEをクリックします。  
<img src='https://qiita-image-store.s3.amazonaws.com/0/2965/c68bcd3d-2e07-1e8b-4ae1-759773c17254.png' width="50%">

1.  「App information」下のEDITボタンをクリックし、左上にある「English」→「Add languege」をクリック、「Japanese」を追加します。  
<img src='https://qiita-image-store.s3.amazonaws.com/0/2965/fbc4db47-9248-c317-0fdb-a47d473cdfa3.png' width="50%">
1.  「App information」の各種情報を入力していきます。基本的にテストで使う場合は、Name以外のフィールドは設定してないなくても大丈夫です。(Nameも無くても行けるかも)  
ある程度情報を入力したら、画面右下にある「Save」ボタンをクリックします。
1.  画面左上のヘッダー横にある「←」ボタンをクリックし、次は「Account Linking」の「Add」ボタンをクリックします。  
<img src='https://qiita-image-store.s3.amazonaws.com/0/2965/73be64f6-1773-bd1b-9dfa-8ed7f5e62241.png' width="50%">
1.  Grant typeに「Authorization Code」を選択し「NEXT」ボタンをクリック
1.  Client informationに以下を設定します。
    1.  Client ID
        1.  先程ローカルサーバ起動時に表示されたクライアントID
    1.  Client Secret
        1.  先程ローカルサーバ起動時に表示されたクライアントシークレット
    1.  Authorization URL
        1.  {先程表示されたURL}/oauth
    1.  Token URL
        1.  {先程表示されたURL}/token
1.  4の「Testing instructions」をクリックし、表示されるテキストボックスに適当な文字を入力し、最後に「SAVE」ボタンをクリックします。
    1.  Testing instructionsはGoogleがアプリの審査をするために、アプリケーションの動作確認をする際に必要な情報を入力しておきます。デモユーザのIDやパスワードが必要です。
1.  設定の一覧に戻ったら「TEST DRAFT」をクリックします。


### スマートデバイスの登録

通常のAoGのアプリではこの時点でテストが行えるようになりますが、スマートホームアプリケーションの場合は、スマートデバイスの登録が必要です。

スマートデバイスは、AndroidやiOSで利用できる「Google Home」アプリを通じて登録できます。



1.  今まで操作してきた、Googleアカウントと同じアカウントでログイン済みなGoogle Homeアプリを起動します。
1.  メニューから「スマートホーム」を選択します。
1.  +ボタンを選択します。すると、自分で作成したアプリケーションが表示されます。
1.  表示されたアプリケーションを選択
1.  ブラウザが起動し、ローカルサーバが呼び出されるので、先程のユーザでログイン
1.  部屋の割当を行い、完了ボタンを選択

## 4. Test our action

デバイスの登録を行ったらGoogleアシスタントを起動し、
「{デバイス名}を付けて」「{デバイス名}を消して」「{デバイス名}を赤くして」などと言ってみてください。

Webブラウザ上の仮想スマートデバイスが操作できれば完了です！


## 5. Create Smart Home App

ここまでは初めてのスマートホームアプリケーションを動かすためにサンプルコードを動かしてきましたが、 \
この章では実際にスマートホームアプリケーションを作成するために、どのような機能が必要か書いていきます。

スマートフォームアプリケーションを作成するには以下が必要です。



1.  OAuth 2.0 Server
1.  Smart Home Intentsを受け取るためのFulfillments


### OAuth 2.0 Server

fulfillmentsにインテントが送信される際に、事前にGoogleアシスタントサービスによって取得されたOAuthアクセストークンが渡されます。 \
actions.devices.SYNCを通じてデバイス情報が取得されるため、OAuth2.0によるアカウントの承認が行われる前に、fulfillmentでEXECUTEインテントが実行されることはありません。

なお注意点として、このOAuth2.0 Serverは1つのアカウントに対して、複数のGoogleアカウントを紐付けられるようにする必要があります。 \
これは、ユーザが家庭内のスマートデバイスを動かす場合、1つのデバイスに対して、複数人のユーザが動かす可能性があるためです。

なおこのOAuth 2.0サーバは公開されたサーバである必要があり、OAuth 2.0 Authorization Code flowをサポートしている必要があります。


### Smart Home Intentsを受け取るためのFulfillments

Fulfillments(webhookのコールバック)ではGoogleアシスタントサーバから送信されるSmart Home Intentsに応じて処理を行い、決まったフォーマットのレスポンスを返却する必要があります。 \
リクエストとレスポンスのフォーマットが正しければ、サーバサイドのプログラミング言語に制限はありません。

なおFulfillmentsでは出来得る限り最小の応答時間にする必要があります。

Googleアシスタントサーバから送信しされるSmart Home Intentsは以下です。



*   action.devices.SYNC
    *   ユーザが持っているデバイスの一覧を要求します。
    *   このインテントはユーザーセットアップ時やデバイスを一括して再同期する際(デバイスに新しい機能が追加された場合など)やアプリの再接続時に送信されます。
    *   Fulfillmentsはユーザが所持している全てのデバイス情報及び、その属性(Device Traits、Device Types)を返却する必要があります。
*   action.devices.QUERY
    *   現在のデバイスの状況を要求します。
    *   これはドアロックの状態など、リアルタイムのデバイス状況を返却する必要があります。
    *   永続的な、デバイスの特性やプロパティなどを要求する場合はaction.devices.SYNCが利用されます。
*   action.devices.EXECUTE
    *   デバイスに対するコマンドを実行を要求します。 \
可能であれば新しいデバイスの状態をレスポンスで返却します。 \
一つのEXECUTEインテントで複数のコマンド、複数のデバイスを操作可能です。(複数のライトの色をそれぞれ異なる色に設定するなど)

なお各Intentのリクエスト内容と応答内容のフォーマットは以下のページを参照してください。 \
[https://developers.google.com/actions/smarthome/create-app](https://developers.google.com/actions/smarthome/create-app)


#### Device TraitsとDevice Types

現在Googleアシスタントでサポート可能な、スマートデバイスにはいくつか制限があります。 \
現状はGoogleアシスタントによって定義されたデバイスの特性(Device Traits)と、デバイスの種別(Device Types)のみサポートしており、それぞれに応じた、音声コマンドが定義されています。

Device TraitsとDevice Typesに関しては以下を参照してください。



*   [Device Traits](https://developers.google.com/actions/smarthome/traits/)
*   [Device Types](https://developers.google.com/actions/smarthome/guides/)


## 6. Keep in touch

より詳しくは"[Actions on Google Smart Home](https://developers.google.com/actions/smarthome/)"を読んで下さい。