Google Home miniをもらったから遊んでみた



# はじめに

Google Playで課金をしていたらGoogle Home miniを頂きました。

せっかくなので、何か作ってみることにします。


# つくるもの

年末でちょうど大掃除をしていたので、ゴミの分別を教えてくれるものを作ることにしました。

「OK, Google. 電池の捨て方！」

みたいな。

APIは慣れているという理由でAWS Lambdaを使い、Pythonで実装しました。

# 前提知識

## 用語

* Actions on Google
    * Google Home のアプリを Action と呼び、そのプラットフォームのこと。
* Dialogflow
    * 会話型サービスを構築するサービス。
    * Google Assistantに依存するものではない。
* Invocation
    * 呼びかけ。Actionを呼び出す設定。
* Intents
    * 意図。会話の1キャッチボール単位。
* Fulfillment
    * 履行。処理の実装。

# 実装

## Actions on Googleでプロジェクトを作る

[Actions on Google](https://console.actions.google.com/) にアクセスして「Add/import project」

以下のような画面が開くので、適当に入力します。

![image](https://qiita-image-store.s3.amazonaws.com/0/197945/4f4e57aa-5717-2ef7-0a4e-74751dd1894b.png)

プロジェクトの情報を入力します。

![image](https://qiita-image-store.s3.amazonaws.com/0/197945/571461b7-421f-7760-d0dc-77e9bd8aabef.png)

適当にカテゴリを選択します。

### Invocation SETUP

左のメニューから「Invocation」を選択します。「Display name」にGoogle Homeに呼びかける名前を入力して「SAVE」しておきます。「Google Assistant voice」はお好みで。

![image](https://qiita-image-store.s3.amazonaws.com/0/197945/0bf01e98-8266-49fc-1438-90d9fe38d702.png)


### Actions SETUP

左のメニューから「Actions」‐「ADD YOUR FIRST ACTION」を選択。

よくわからないので「Custom intent」で「BUILD」。Dialogflowが開きます。

![image](https://qiita-image-store.s3.amazonaws.com/0/197945/eb73668f-26b8-4275-4ca7-b84ab3015eaf.png)

## Dialogflowでプロジェクトを作る

※ Dialogflowを始めて使う場合、アカウントの権限許可を求められるので、許可します。

メニューから「Create Agent」で「CREATE」します。プロジェクトが作成されます。

![image](https://qiita-image-store.s3.amazonaws.com/0/197945/9846ead2-6b2b-5c17-bc0a-a9654fd56cd8.png)

先にGoogle Assistantと統合しておきます。「Integrations」でGoogle Assistantの「INTEGRATION SETTINGS」を選択します。

![image](https://qiita-image-store.s3.amazonaws.com/0/197945/5b249711-ff8b-dcdc-b9a2-a0252af619a1.png)

とりあえず、「MANAGE ASSISTANT APP」を選択します。

![image](https://qiita-image-store.s3.amazonaws.com/0/197945/774b7db1-282e-77ec-ed37-57903cd92856.png)

Actions on Googleに戻り、Dialogflowと連携されたことがわかります。

![image](https://qiita-image-store.s3.amazonaws.com/0/197945/04ab05de-8170-8070-0f36-8fcd2759b614.png)


### 新しいIntentを作る

まず、アプリの起動のIntentとなる「Default Welcom intent」を選択し、「output context」に「gomi_context」を入力します。

![image](https://qiita-image-store.s3.amazonaws.com/0/197945/c29f318a-9597-dba8-9e2f-db525edcc9d4.png)

Dialogflowの「Intents」から「CREATE INTENT」します。
「input context」を先ほど指定した「gomi_context」とします。

「Training phrases」にユーザからの呼びかけのパターンを入力し、ごみを表す部分を変数「word」で受け取るようにします。ENTITYはsys.anyとしています。

![image](https://qiita-image-store.s3.amazonaws.com/0/197945/ece054d2-ef00-f309-a914-d8c458785af5.png)

「Fulfillment」ではwebhookを有効にしておきます。

![image](https://qiita-image-store.s3.amazonaws.com/0/197945/7d4e2de9-0abc-80fa-1f75-ba38c8184b2d.png)

### Fulfillmentの設定

Webhookを「ENABLED」としてAPIのURLを指定します。
指定するURLは「[AWSでゴミ分別APIを作った - Qiita](https://qiita.com/estaro/items/3105a10b314316c997e9)」で作成したものです。

![image](https://qiita-image-store.s3.amazonaws.com/0/197945/61df0f98-a5b7-f3e8-73cf-8cb30aa173af.png)

## APIの対応

「[AWSでゴミ分別APIを作った - Qiita](https://qiita.com/estaro/items/3105a10b314316c997e9)」で作成したAPIをDialogflowのWebhookの仕様に合わせて修正します。

Webhookの仕様は「[How fulfillment works  |  Dialogflow](https://dialogflow.com/docs/fulfillment/how-it-works)」を参考にします。

WebhookからのアクセスはPOSTなので、template.yamlを修正します。

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: An AWS Serverless Specification template describing your function.
Resources:
  garbage:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: garbage/lambda_function.lambda_handler
      Runtime: python3.6
      Description: ''
      MemorySize: 128
      Timeout: 15
      CodeUri: .debug/
      Events:
        LambdaMicroservice:
          Type: Api
          Properties:
            Path: /garbage
            Method: POST
  garbagePermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      Action: 'lambda:InvokeFunction'
      FunctionName:
        'Fn::GetAtt':
          - garbage
          - Arn
      Principal: apigateway.amazonaws.com
      SourceArn:
        'Fn::Sub': 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*/*/*'
```

Intentsの設定でパラメータを「word」としたため、リクエストから取得するために以下のように変更しました。

```py
    body = json.loads(event.get('body', {}))
    word = body.get('queryResult', {}).get('parameters', {}).get('word', None)
```

レスポンスは以下のように、「fulfillmentText」で返却するように変更します。
日本語で返却するため、Content-Typeにcharsetを設定しておきます。

```json
    {
        'statusCode': '200',
        'body': {
            'fulfillmentText': result,
        },
        'headers': {
            'Content-Type': 'application/json; charset=UTF-8',
        },
    }
```

# テスト

Actions on Googleの「Simulator」でも、Google Homeの実機でも確認ができます。

![image](https://qiita-image-store.s3.amazonaws.com/0/197945/c1ae5f17-8837-365c-d187-120b78f454ce.png)


# おわりに

やっていることは難しいわけではないのですが、実際に試してみないと難しいですね。

# 参考

[Actions on Googleチュートリアル - Qiita](https://qiita.com/knanco/items/f3044dd447178b94cbf9)

[Dialogflowで会話中に値を保持する方法 - Qiita](https://qiita.com/running1017/items/15718bb7ee763613cda3)

[Actions on Google と AWS Lambda で Google Home から Slack にポストする - ユニファ開発者ブログ](https://tech.unifa-e.com/entry/2017/10/16/103458)