Google AIY Voice kitのデバイスアクションを使ってLED制御してみる

Google Assistant SDKのアップデートに、デバイスアクションの実装というのがあります。
サンプルコードをのぞいてみるとなんとなくそれっぽい記述があったり、デバイス登録の手順で謎のキーワードが出てきたりしたので、なんとなく使えるのかな？というところから、実際にいじってみました。

# 準備
[前の記事](https://qiita.com/exyz_cbr/items/2bffb8cdd2cad7b95f88)までの手順でVoice kitが使えるようになっている事とします。
また、サンプルコードは前の記事でコピー＆編集したものを続けて使用します。

後述しますが、Voice HATにLEDを接続するため、LEDを準備してください。

# デバイス登録の更新
既に登録されているデバイスの情報を更新し、デバイスが特定のアクションに反応するようにします。
まずデバイスモデルの情報を更新します。

```
(env) pi@raspberrypi:~/AIY-voice-kit-python $ googlesamples-assistant-devicetool register-model --manufacturer "Assistant SDK developer" --product-name "Assistant SDK light" --type LIGHT --model <model-name> --trait action.devices.traits.OnOff --trait action.devices.traits.Brightness --trait action.devices.traits.ColorTemperature
```

ここで出てくる`--type`と`--trait`の種類は[Google Assistant SDKのドキュメント](https://developers.google.com/assistant/sdk/reference/device-registration/model-and-instance-schemas)を参考に選択します。上記の例では、ライトのオンオフ・明るさ・色の調整をするオプションをつけていることになります。

コマンドを実行し、うまく更新されたら、念のため確認します。以下のような出力が得られるはずです。

```
(env) pi@raspberrypi:~/AIY-voice-kit-python $ googlesamples-assistant-devicetool list --model
Device Model Id: <model-name>
        Project Id: <Project ID>
        Device Type: action.devices.types.LIGHT
        Trait action.devices.traits.Brightness
        Trait action.devices.traits.ColorTemperature
        Trait action.devices.traits.OnOff

```

確認ができたらデバイス情報を更新します。

```
(env) pi@raspberrypi:~/AIY-voice-kit-python $ googlesamples-assistant-devicetool register-device --device <device-id> --model <model-name> --client-type SERVICE
```

ここまでエラー無く行けばおそらく情報の更新は完了しています。



# Voice HATにLEDをつなぐ
[AIY ProjectのページにあるVoice HATのGPIO](https://aiyprojects.withgoogle.com/voice/#makers-guide-4-1--connecting-additional-sensors)と、Voice kitに付属のマニュアルP.58を参考に、LEDをつなぎます。
今回はフルカラーLEDを使って、Servo0にLED赤、Servo1にLED緑、Servo2にLED青を接続しました。

![voicehat.jpg](https://qiita-image-store.s3.amazonaws.com/0/27114/f8ce06b9-bd33-dcdd-2191-fb2cd01e35a7.jpeg)

# サンプルコードの追加改造
デバイスアクションが実行された際にGPIOを直接たたいてLEDを光らせるようサンプルコードを改造します。
[前の記事](https://qiita.com/exyz_cbr/items/2bffb8cdd2cad7b95f88)で編集したファイルを引き続き使用します。
PWMを使ったりすればよかったのかもですが、あくまでサンプルなのでかなり手抜きです、ご了承ください。

## hotword.py

```
--- hotword.py.1        2018-02-21 02:42:00.071501759 +0900
+++ hotword.py  2018-02-22 03:59:04.228139279 +0900
@@ -21,6 +21,8 @@ import argparse
 import os.path
 import json

+import RPi.GPIO as GPIO
+
 import google.auth.transport.requests
 import google.oauth2.credentials

@@ -34,6 +36,10 @@ from google.assistant.library.file_helpe

 DEVICE_API_URL = 'https://embeddedassistant.googleapis.com/v1alpha2'

+# GPIO Pin no.
+RED_PIN = 26
+GREEN_PIN = 6
+BLUE_PIN = 13

 def process_device_actions(event, device_id):
     if 'inputs' in event.args:
@@ -48,6 +54,30 @@ def process_device_actions(event, device
                                         yield e['command'], e['params']
                                     else:
                                         yield e['command'], None
+                                    if e['command'] == 'action.devices.commands.OnOff':
+                                        if e['params']['on']:
+                                            GPIO.output(RED_PIN,GPIO.HIGH)
+                                            GPIO.output(GREEN_PIN,GPIO.HIGH)
+                                            GPIO.output(BLUE_PIN,GPIO.HIGH)
+                                        else :
+                                            GPIO.output(RED_PIN,GPIO.LOW)
+                                            GPIO.output(GREEN_PIN,GPIO.LOW)
+                                            GPIO.output(BLUE_PIN,GPIO.LOW)
+                                    if e['command'] == 'action.devices.commands.ColorAbsolute':
+                                        print(e['params']['color']['spectrumRGB'])
+                                        if e['params']['color']["spectrumRGB"] & 0xff0000:
+                                            GPIO.output(RED_PIN,GPIO.HIGH)
+                                        else:
+                                            GPIO.output(RED_PIN,GPIO.LOW)
+                                        if e['params']['color']["spectrumRGB"] & 0x00ff00:
+                                            GPIO.output(GREEN_PIN,GPIO.HIGH)
+                                        else:
+                                            GPIO.output(GREEN_PIN,GPIO.LOW)
+                                        if e['params']['color']["spectrumRGB"] & 0x0000ff:
+                                            GPIO.output(BLUE_PIN,GPIO.HIGH)
+                                        else:
+                                            GPIO.output(BLUE_PIN,GPIO.LOW)
+


 def process_event(event, device_id):
@@ -139,6 +169,13 @@ def main():
         credentials = google.oauth2.credentials.Credentials(token=None,
                                                             **json.load(f))

+    # GPIO setup
+    GPIO.setmode(GPIO.BCM)
+    GPIO.setwarnings(False)
+    GPIO.setup(RED_PIN,GPIO.OUT)
+    GPIO.setup(GREEN_PIN,GPIO.OUT)
+    GPIO.setup(BLUE_PIN,GPIO.OUT)
+
     with Assistant(credentials, args.device_model_id) as assistant:
         events = assistant.start()

```


## pushtotalk.py

```
--- pushtotalk.py.1     2018-02-21 02:18:19.228083473 +0900
+++ pushtotalk.py       2018-02-22 04:04:27.796660481 +0900
@@ -22,6 +22,8 @@ import os.path
 import sys
 import uuid

+import RPi.GPIO as GPIO
+
 import click
 import grpc
 import google.auth.transport.grpc
@@ -54,6 +56,10 @@ DIALOG_FOLLOW_ON = embedded_assistant_pb
 CLOSE_MICROPHONE = embedded_assistant_pb2.DialogStateOut.CLOSE_MICROPHONE
 DEFAULT_GRPC_DEADLINE = 60 * 3 + 5

+# GPIO Pin no.
+RED_PIN = 26
+GREEN_PIN = 6
+BLUE_PIN = 13

 class SampleAssistant(object):
     """Sample Assistant that supports conversations and device actions.
@@ -296,6 +302,13 @@ def main(api_endpoint, credentials, proj

         $ python -m googlesamples.assistant -i <input file> -o <output file>
     """
+    # GPIO setup
+    GPIO.setmode(GPIO.BCM)
+    GPIO.setwarnings(False)
+    GPIO.setup(RED_PIN,GPIO.OUT)
+    GPIO.setup(GREEN_PIN,GPIO.OUT)
+    GPIO.setup(BLUE_PIN,GPIO.OUT)
+
     # Setup logging.
     logging.basicConfig(level=logging.DEBUG if verbose else logging.INFO)

@@ -363,8 +376,33 @@ def main(api_endpoint, credentials, proj
     def onoff(on):
         if on:
             logging.info('Turning device on')
+            GPIO.output(RED_PIN,GPIO.HIGH)
+            GPIO.output(GREEN_PIN,GPIO.HIGH)
+            GPIO.output(BLUE_PIN,GPIO.HIGH)
         else:
             logging.info('Turning device off')
+            GPIO.output(RED_PIN,GPIO.LOW)
+            GPIO.output(GREEN_PIN,GPIO.LOW)
+            GPIO.output(BLUE_PIN,GPIO.LOW)
+
+    @device_handler.command('action.devices.commands.ColorAbsolute')
+    def colorabsolute(color):
+        logging.info(color["spectrumRGB"])
+        if color["spectrumRGB"] & 0xff0000:
+            logging.info('RED')
+            GPIO.output(RED_PIN,GPIO.HIGH)
+        else:
+            GPIO.output(RED_PIN,GPIO.LOW)
+        if color["spectrumRGB"] & 0x00ff00:
+            logging.info('GREEN')
+            GPIO.output(GREEN_PIN,GPIO.HIGH)
+        else:
+            GPIO.output(GREEN_PIN,GPIO.LOW)
+        if color["spectrumRGB"] & 0x0000ff:
+            logging.info('BLUE')
+            GPIO.output(BLUE_PIN,GPIO.HIGH)
+        else:
+            GPIO.output(BLUE_PIN,GPIO.LOW)

     if not device_id or not device_model_id:
         try:

```
# 実際試してみる
hotwordの場合、「OK Google、ライトをつけて」というようなワードでLEDが点灯します。同じく、「ライトを消して」「ライトを青にして」「ライトを赤に」等でLEDが制御できるようになります。
pushtotalkの場合はいちいちボタンを押す必要がありますが、あとは同じ動作になります。