Playbookが成功したらGoogleHomeに褒めてもらう

[Ansible Advent Calender 2018](https://qiita.com/advent-calendar/2018/ansible) 23日目の記事です。

昨日はsugitkさんで、[Ansibleをとりまくさまざまな技術について](https://rheb.hatenablog.com/entry/ansible_technical_materials) でした。

さて皆様におきましてはPlaybookが成功したらどう認識しているでしょうか？
notificationモジュールを使用してSlackやメールで通知するようにしている方もいるでしょう。

音声で認識できたらなと思い、モジュール作ってみました。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Playbookが成功したらGoogle Homeが褒めてくれるの作った <a href="https://t.co/ENt4ITbuZa">pic.twitter.com/ENt4ITbuZa</a></p>&mdash; 浅間丸改二 (@ken5owata) <a href="https://twitter.com/ken5owata/status/1076368132795559937?ref_src=twsrc%5Etfw">2018年12月22日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

使うのは Google Home Mini です。 ~~最近は3000円で投げ売りされてry...~~

###下調べ
まずどうやってGoogle Home Miniを操作するの？ってことでググッて以下を見つけました。

https://github.com/balloob/pychromecast
Google Home Miniもchromecast機器の1種になるので、How to useの記載やExampleのソースでGoogle Home Miniも制御可能です。

https://github.com/harperreed/google-home-notifier-python
[google-home-notifier](https://github.com/noelportugal/google-home-notifier) を参考にしPythonで実装したようです。
ローカルでWebサーバを立ち上げてGoogleHomeにmp3ファイルのURLを渡して再生させています。

上記例を参考にして、まずmp3ファイルを再生させるモジュールを作りました。

```python:googlehome_mp3.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-

from __future__ import absolute_import, division, print_function
__metaclass__ = type

from ansible.module_utils.basic import AnsibleModule

ANSIBLE_METADATA = {'metadata_version': '1.1',
                    'status': ['stableinterface'],
                    'supported_by': 'community'}

import pychromecast
import time

def main():

    ansible_facts = dict()
    module = AnsibleModule(
        argument_spec=dict(
            device_name=dict(type='str', required=True),
            mp3=dict(type='str', required=True)
        )
    )
    device = module.params['device_name']
    mp3_url = module.params['mp3']
    casts = pychromecast.get_chromecasts()
    cast = next(cc for cc in casts if cc.device.friendly_name == device)
    time.sleep(1)
    if not cast.is_idle:
        cast.quit_app()
        time.sleep(5)

    cast.play_media((mp3_url), "video/mp3")
    time.sleep(2)
    module.exit_json(msg="OK", changed=False)

if __name__ == '__main__':
    main()
```

で↑に対してこんなyamlを書いて実行すると、

```yml:googlehome_mp3.yml
---
- hosts: localhost
  connection: local
  gather_facts: False

  tasks:
  - name: Google home mp3
    googlehome_mp3:
      device_name: "1号機"
      mp3: "https://s3.us-east-2.amazonaws.com/xxxxxxx/serenade.mp3"

```

mp3が再生されます。
※セレナーデは寝台特急で流れるオルゴールのやつ。


```
pi@raspberrypi:an $ ansible-playbook -i hosts googlehome_mp3.yml

PLAY [localhost] ****************************************************************************************************

TASK [Google home mp3] **********************************************************************************************
ok: [localhost]

PLAY RECAP **********************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0

pi@raspberrypi:an $
```

###テキスト→音声の作成

mp3の再生はできました。
次は引数で渡したセリフを音声にする必要があります。

これには Google Cloud Text-to-Speech を使用して、mp3ファイルを作成します。

https://cloud.google.com/text-to-speech/
https://cloud.google.com/text-to-speech/docs/create-audio#text-to-speech-ssml-python

このAPI使うために以下の設定が必要となります。
・GCPの利用登録
・APIの有効化
・認証情報の作成

こちらのページを参考に設定をします。
https://blog.apar.jp/web/9893/

利用するための設定が終了したらサンプルのコードを落としてきて動かしてみます。
https://github.com/GoogleCloudPlatform/python-docs-samples/tree/master/texttospeech/cloud-client

```
$ git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git
$ python3 -m pip install -r python-docs-samples/texttospeech/cloud-client/requirements.txt
```

設定とインストールが完了したら、サンプルのquickstart.pyを実行してみます。
実行すると、output.mp3が作成されます。

```
pi@raspberrypi:cloud-client $ python3 quickstart.py
Audio content written to file "output.mp3"
pi@raspberrypi:cloud-client $ ls output.mp3
output.mp3
pi@raspberrypi:cloud-client $
```

デフォルトでは英語のVoiceで作成されますが、日本語も対応しています。
quickstart.py の language_code='ja_JP'にすると日本語の音声で作成されます。 
対応している音声の言語はサンプルのlist_voics.pyで取得できます。

###googlehome_speechモジュールを作成

quickstart.pyのコードをgooglehome_mp3.pyに付け足して、任意のセリフを喋らせることができるモジュールを作ります。
ソースはこんな感じになりました。

```python:googlehome_speech.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-

from __future__ import absolute_import, division, print_function
__metaclass__ = type

from ansible.module_utils.basic import AnsibleModule

ANSIBLE_METADATA = {'metadata_version': '1.1',
                    'status': ['stableinterface'],
                    'supported_by': 'community'}

import pychromecast
import time


def create_mp3(mp3_file, mesg):
    from google.cloud import texttospeech
    client = texttospeech.TextToSpeechClient()

    synthesis_input = texttospeech.types.SynthesisInput(text=mesg)
    voice = texttospeech.types.VoiceSelectionParams(
        language_code='ja_JP',
        ssml_gender=texttospeech.enums.SsmlVoiceGender.NEUTRAL)

    audio_config = texttospeech.types.AudioConfig(
        audio_encoding=texttospeech.enums.AudioEncoding.MP3)

    response = client.synthesize_speech(synthesis_input, voice, audio_config)

    with open(mp3_file, 'wb') as out:
        out.write(response.audio_content)

def main():

    ansible_facts = dict()
    module = AnsibleModule(
        argument_spec=dict(
            device_name=dict(type='str', required=True),
            mp3_server=dict(type='str', required=True),
            mp3_folder=dict(type='str', required=True),
            mp3_file=dict(type='str', required=True),
            texttospeech=dict(type='str', required=True)
        )
    )


    device = module.params['device_name']
    mp3_server = module.params['mp3_server']
    mp3_folder = module.params['mp3_folder']
    mp3_file = module.params['mp3_file']
    texttospeech = module.params['texttospeech']

    file_path = mp3_folder + "/" + mp3_file
    create_mp3(file_path, texttospeech)

    casts = pychromecast.get_chromecasts()
    cast = next(cc for cc in casts if cc.device.friendly_name == device)
    time.sleep(1)
    if not cast.is_idle:
        cast.quit_app()
        time.sleep(5)

    mp3_path = mp3_server + "/" + mp3_file
    cast.play_media((mp3_path), "video/mp3")
    time.sleep(2)
    module.exit_json(msg="OK", changed=False)

if __name__ == '__main__':
    main()
```

mp3ファイルをGoogle Homeが取得できる場所に置く必要があるので、ラズパイにnginxを入れてフォルダを公開しておきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/146663/d84849f7-a582-019f-8b24-24f2e3b68319.png)

このフォルダにmp3ファイルが生成されるようにPlaybook内で指定します。
Google Homeにここに生成されたmp3ファイルのURLを渡すことで、音声が再生されます。


Playbookはこんな感じにして、前のタスクが成功したらGoogle Homeで褒めてもらうようにして実行します。

```yml:googlehome_speech.yml
---
- hosts: localhost
  connection: local
  gather_facts: False

  tasks:
  - name: command
    command: hostname
    register: result

  - name: Google home speech
    googlehome_speech:
      device_name: "1号機"
      mp3_server: "http://xxx.xxx.xxx.xxx/mp3"
      mp3_folder: "/xxx/mp3"
      mp3_file: "output.mp3"
      texttospeech: "おめでとうPlaybookが成功したよ"
    when: result is succeeded
```

前のタスクが成功するとGoogle Homeにおめでとうと言ってもらえます。

```
pi@raspberrypi:an $ ansible-playbook -i hosts googlehome_speech.yml 

PLAY [localhost] *****************************************************************************************

TASK [command] *******************************************************************************************
changed: [localhost]

TASK [Google home speech] ********************************************************************************
ok: [localhost]

PLAY RECAP ***********************************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0   

pi@raspberrypi:an $ 
```

###おわりに

また、つまらぬモジュールを作ってしまいました。
Ansibleユーザの皆さまに置かれましては何とぞ大目に見て頂きたくお願いします。 :bow_tone1:

ソースは以下に置いておきます。
https://github.com/sato-ken/mymodules

Goole Homeではなく、早見沙織さんの音声で褒められたいsatokenでした。