85
98

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Pythonを使って、Google Homeに喋らせてみる

Last updated at Posted at 2018-01-08

Google Homeに自発的に喋ってもらいたくなったので色々と調べてみたのですが、google-home-notifierが大多数を占めていたので、Pythonのみで書いてみました。

面倒な人向け

Gistにソースがあるので見てください

序:google-home-notifierについて(ざっくりと

node.js製の、Google Homeに対してテキストを喋らせるライブラリです。

雑にコードを読んだり、Qiitaにある記事なんかを読んところ、

  1. mDNSでLAN内のGoogle Homeを探す
  2. テキストをGoogle Text-to-Speechを使ってmp3のURLを作成する
  3. (2)のURLをGoogle Homeに渡して再生させる

といった感じになるようです。

破:google-home-notifierの仕組みはPythonのライブラリにあるか

とりあえず目についたのが、この2個。
うまい具合に不足部分を補えれば、再現に近いことができそうです。

pychromecast

Chromecastとの通信をしてくれるライブラリです。

Check out Home Assistant for a ready-made solution using PyChromecast for controlling and automating your Chromecast or Cast-enabled device like Google Home.

とREADMEにあるとおり、Google HomeはGoogleCast仕様に基づくらしく、このライブラリでそのまま拾うことができます。

pychromecast_sample
>>> import pychromecast
>>> chromecasts = pychromecast.get_chromecasts()
>>> len(chromecasts)
>>> chromecasts[0]
Chromecast(
    '192.168.0.12',
    port=8009,
    device=DeviceStatus(
        friendly_name='自室のGoogle Home',
        model_name='Google Home',
        manufacturer='Google Inc.',
        uuid=UUID('XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'),
        cast_type='audio'
    )
)

gTTS

Google Text-to-speechを利用して、テキストをmp3に変換してローカルに保存する事ができるライブラリです。

gtts_sample
>>> from gtts import gTTS
>>> tts = gTTS(text='ハローワールド', lang='ja')
>>> tts.save('./hello_world_ja.mp3')

わずか3行で変換できる優れもの。ただし、このライブラリのみではgoogle-home-notifierのようにGoogle側にあるURLを扱えないのが考えものです。
(とはいえ、色々実装するの面倒なので、今回は眼をつぶることにします)

急:実際に作ってみる

用意するもの

bottleも使って、ローカルにWebサーバーを用意します。
パスはこんな感じ。

  • /form
    • テキスト送信フォームのURL
  • /talks/xxxxxxxx
    • 保存済みテキストmp3のURL

/formの処理概要

  1. ブラウザからフォームでテキストと言語を送信
  2. POSTリクエスト内で、
    1. gTTSを使ってテキストのmp3を作成してローカルに保存
    2. pychromecastを使って、保存済みのmp3を示すURLをGoogleHomeに再生させる
  3. Google Homeが喋る

ソース抜粋

※処理の流れが終える程度に圧縮

server.py
# -*- coding: utf-8 -*-
# インポート周りは省略

BASE_DIR = Path(__file__).parent
TALK_DIR = BASE_DIR / 'var'
app = Bottle()


@app.route('/talks/<file_path:path>')
def get_talk_mp3(file_path):
    return static_file(file_path, root=TALK_DIR)


@app.route('/form', method='GET')
def get_talk_form():
    """フォーム表示"""
    content = BASE_DIR / 'template.html'
    text = request.forms.text or ''
    lang = request.forms.lang or 'ja'
    return template(
        content.open().read(), langs=gTTS.LANGUAGES, lang=lang, text=text)


@app.route('/form', method='POST')
def post_talk_form():
    """トークテキストを保存して、再生指示"""
    text_token = generate_talk(request.forms.text, request.forms.lang)
    # 面倒なので、目についたGoogleCast端末に決め打ち
    chromecasts = pychromecast.get_chromecasts()
    chromecasts[0].media_controller.play_media(
        f"http://192.168.0.20:8080/talks/{text_token}", 'audio/mp3')
    return get_talk_form()


def generate_talk(text, lang):
    """言語もテキストも一致したらキャッシュを使うように"""
    text_token = hashlib.sha256((lang + text).encode()).hexdigest()
    talk_path = TALK_DIR / text_token
    if not talk_path.exists():
        tts = gTTS(text=text, lang=lang)
        tts.save(talk_path)
    return text_token


if __name__ == '__main__':
    if not TALK_DIR.exists():
        TALK_DIR.mkdir()
    app.run(host='0.0.0.0', port='8080', reloader=True)

(時間があったら、動画でも用意します)

あとがき

google-home-notifierでも十分だったりするのですが、「この辺の仕組みを上手く広げていくと面白い何かができないかなー」なんて考えてみたりしたいですね。

こんなのもあったので、参考にしたいと思います。

85
98
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
85
98

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?