Home AssistantでTTS(Text To Speech)を利用する。google-home-notifierの一歩先へ。


Home AssistantにはTTS(Text To Speech)機能が標準で用意されています。
任意のテキストをChromecastでGoogle Homeなどに喋らせることができます。
ブラウザでダッシュボード画面を開いてテキストボックスに入力して喋らせたり、REST APIで呼ぶことも可能です。

下記のようなことができます。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Home AssistantのText to Speech(TTS)でGoogle Home系にキャストできるのよさげ。<br>google-home-notifierはGoogle翻訳TTSを非公式に使ってて壊れやく微妙だったけど<br>これはCloud Text-to-SpeechやMicrosoftのTTS(無料)を利用できるので品質良し。<br>REST APIもあるのでESP32からも叩けそう。 <a href="https://t.co/UPsNsLmbjA">pic.twitter.com/UPsNsLmbjA</a></p>&mdash; ode (@odetarou) <a href="https://twitter.com/odetarou/status/1352635690236796933?ref_src=twsrc%5Etfw">January 22, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# VS google-home-notifer

google-home-notiferはGoogle翻訳を非公式に使ったTTSなのでよく壊れるのと品質がいまいちなので
正式なAPIを使ってTTSを実現できるHome Assistantのほうが良かったです（Cloud Text-to-Speechや他のTTSを利用可）。
ホームオートメーション用途で他にも色々機能が充実してるのと、OSSで常に最新に更新し続けられて管理されているのでオレオレコードをメンテしなくてすむのが助かります。

# Home Assistantって何？
まだ未導入の場合は別途こちらの記事を参考にしてください
[Google HomeでIFTTT(有料化)の代わりにHome Assistantが便利だったので画像多めで導入解説] (https://qiita.com/odetarou/items/4407d06d030c83180364)
下記の箇所だけ読めばOKです。
"Home Assistantセットアップ" SDカードにイメージを書き込んでラズパイに指すぐらいで簡単です。
"RM Mini3(赤外線リモコン)連携" デバイスを持っていなくても読むことで雰囲気を理解できると思います。

# サポートしているTTSサービス

下記URLよりサポートしているTTSサービスのインテグレーション一覧が見れます。
https://www.home-assistant.io/integrations/#text-to-speech

メジャーどころを抜粋すると

- Cloud Text-to-Speech (Google Cloud Platform)
- Google Translate Text-to-Speech（node.jsやesp32などのgoogle home notifyでお馴染みのGoogle翻訳を非公式に使ったTTS。よく壊れるのと品質がいまいちなのでCloud Text-to-Speechがおすすめ）
- Amazon Polly (AWS)
- Microsoft Text-to-Speech (Azure)

などなど利用可能です。大御所は一通りサポートしていますね。

利用の流れとしては
各TTSサービスごとのAPIキーなどを取得してconfiguration.yamlに書けばサービスとして利用可能です。

# Nginxリバースプロキシの導入(HTTPでアクセスできない場合)
Google Assistant連携等でHome Assistant側がSSLのみの設定になっている場合はNginxリバースプロキシの設定が必要になります。
HTTPでアクセスできる場合はSKIPして大丈夫です。
SSLとHTTPの両方でアクセスできるようになるため今回のTTSやらないでもいれておいたほうが便利かと思います。
Addonで用意されているため簡単に導入できます。

Google HomeへCastするにはGoogle Home側からHome Assistantに音声データを取得できる必要があるのですが
SSLの場合は証明書が正しい必要があるためIP指定だと証明書エラーになってしまいます（ドメイン指定だと外部扱いでルーターにアクセスしてしまう）
そのためIP指定＆HTTPで行うのが基本になります。

今までSSLに設定していたPort 8123がHTTPになり、443がSSLになります。80はOptionで設定できますが8123がHome Assistantの標準なのと他のaddonで利用できるために標準ではOFFになっているようです。

SSLアクセスにはHome AssistantおすすめのDuckDNSを利用している前提で話を進めます。
[無料のDynamic DNS(Duck DNS)& 無料のSSL(Let’s Encrypt)] (https://qiita.com/odetarou/items/4407d06d030c83180364#%E7%84%A1%E6%96%99%E3%81%AEdynamic-dnsduck-dns-%E7%84%A1%E6%96%99%E3%81%AEssllets-encrypt)

## インストール方法

Addonをいれます。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/b0d92a5f-8328-6bd8-59a5-47acc7781ab5.png">

次のページでINSTALLを押してインストール完了です。

configuration.yamlでhttpの箇所で下記SSL設定部分削除とproxy関連の許可設定を追記します。
不要かもですがRestart HASSで再起動しておいたほうが確実そうです。
こちらが再起動で反映されるとSSL → HTTPでのアクセスになり `http://IPアドレス:8123`でアクセスできるようになります。
追記2021/12/07: proxy関連の許可関連を記載 2021.7以降のアップデートで破壊的変更があったため対応必要になりました。
https://community.home-assistant.io/t/http-integration-is-not-set-up-for-reverse-proxies/313284

```.yaml
http:
  ssl_certificate: /ssl/fullchain.pem ← 削除
  ssl_key: /ssl/privkey.pem ← 削除
  ↓追記する
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.30.33.0/24
```

Nginx AddonのConfigurationタブへ移動し
YAMLのdomainに取得したドメインを設定します。

```.yaml
domain: 取得したドメイン.duckdns.org
certfile: fullchain.pem
keyfile: privkey.pem
hsts: max-age=31536000; includeSubDomains
cloudflare: false
customize:
  active: false
  default: nginx_proxy_default*.conf
  servers: nginx_proxy/*.conf
```

Saveを押して保存します。

Infoタブに戻りStartリンクを押します。反映まで数分待ちます。
その間に自宅のルーターの設定でWANの443ポートがラズパイのIPの8123に転送になっていたのを443ポートに転送するようにしてください(ポートフォワーディング）

その後Nginxが起動したころあいをみて
`http://IPアドレス:8123`
`https://取得したドメイン.duckdns.org`
でアクセスできるのを確認してください。


参考記事（英語）
Home Assistant internal URL without HTTPS? - Configuration - Home Assistant Community
https://community.home-assistant.io/t/home-assistant-internal-url-without-https/238200
Nginx AddonのDoc。紹介した内容が書かれています。
https://github.com/home-assistant/addons/blob/master/nginx_proxy/DOCS.md

# 各サービスのインストール

好きなTTSを選んでいれてみてください。
下記にそれぞれのインストール方法を説明します。
最初にまずは楽に設定してTTSを試したい場合はGoogle Translateを。
本格運用するなら無難なのはGoogle Text To SpeechかMicrosoft Text To Speechがおすすめです。
Microsoftのは初めて使用したのですが品質がGoogleよりいいかもと思いました。

## Google Translate
アカウント登録なしに利用できるのがメリットですが
非公式利用のため壊れやすいです。品質もよくありません。
設定が簡単なためまずはお試しに使うとよさそうです。
私は最初に使おうとしたところ早速壊れていて使用できなかったのですがHome AssistantのVersionを最新にしたら使用できました。
Home AssistantのVersionアップの仕方は別項目に記載しました。

configuration.yamlに下記を記載して再起動します。
これで利用可能になります。

```.yaml
tts:
  - platform: google_translate
    language: "ja"
```

service名: `tts.google_translate_say`
が生成されて利用できます。

下記のスクリプトを作成して実行するとcastのテストが可能です。

```.yaml
service: tts.google_translate_say
entity_id: media_player.グーグルホームのendity_id
data:
  message: こんにちわ。おげんきですか？
```

下記URLはうまく動かない場合があったので調べた時のissue。最新版のHome Assistsantでは直っていました。最新版へ反映されるまでの間はカスタムコンポーネントを登録して回避するなども話されいました。
Did google_tts change once again? No TTS from google_translate for · Issue #43801 · home-assistant/core
https://github.com/home-assistant/core/issues/43801
tts.google_translate_say no longer works with Google Home · Issue #38346 · home-assistant/core
https://github.com/home-assistant/core/issues/38346

公式Doc
https://www.home-assistant.io/integrations/google_translate/


## Google Text To Speech

品質の良いWaveNetを利用した場合100万文字までは毎月無料で利用可能

1. GCPのプロジェクト作成
https://console.cloud.google.com/cloud-resource-manager
"プロジェクトを作成"で作成する

2. 課金を有効にする
※ 通常は無料枠で収まります。
下記URLを参考に課金が有効になっているかを確認して下さい
https://cloud.google.com/billing/docs/how-to/modify-project

3. APIを有効にする
下記URLを開き"Text-to-Speech"で検索してCloud Text-to-Speech APIを有効にします。
https://console.cloud.google.com/apis/library

4. サービスアカウントキーファイルを取得する
https://console.cloud.google.com/apis/credentials/serviceaccountkey
サービス アカウントのセレクトボックスで"新しいサービスアカウント"を選択
"サービスアカウント名"に任意の名前を入力(home-assistantなど)
"ロール"は選択しません。
作成ボタンを押します（ロールが割り当てられていないとでますがロール無しを選択してください）
API Keyのjsonファイルがダウンロードされます。

5. Home AssistantのFile EditorでAPI Keyのjsonファイルをconfig/直下にアップロードします。私はSERVICE_ACCOUNT.JSONという名前にしてアップロードしました。

6. configuration.yamlに下記を記載して再起動します。pitch, speedなどは自分の好みの値を探してみてください。
これで利用可能になります。

```.yaml
tts:
  - platform: google_cloud
    key_file: SERVICE_ACCOUNT.JSON
    language: ja-JP
    gender: female
    voice: ja-JP-Wavenet-B
    pitch: 4
#    speed: 0.9
    profiles:
      - medium-bluetooth-speaker-class-device
```

service名: `tts.google_cloud_say`
が生成されて利用できます。

下記のスクリプトを作成して実行するとcastのテストが可能です。

```.yaml
service: tts.google_cloud_say
entity_id: media_player.グーグルホームのendity_id
data:
  message: こんにちわ。おげんきですか？
```


公式Doc パラメータ値などものっています。
https://www.home-assistant.io/integrations/google_cloud/

profilesの値一覧は下記参照
https://cloud.google.com/text-to-speech/docs/audio-profiles

## Microsoft Text To Speech

無料プランの場合
品質のよいニューラル音声だと1 か月あたり 0.5 million(日本語で25万文字) 文字まで無料
Standard音声だと1 か月あたり 5 million(日本語で250万文字) 文字まで無料
中国語、日本語、韓国語の各文字は、請求対象として2文字としてカウントされます。

英語ドキュメントですが下記記事を参考にAPIキーを取得してください（Chromeの翻訳など利用すれば問題なかったです）
What is the Speech service? - Azure Cognitive Services | Microsoft Docs
https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/overview#try-the-speech-service-for-free

おおまかな流れとしてはazureアカウント作成して、音声のリソースを作成します。
リージョンが日本だとニューラル音声に対応していないため米国東部で作成する点が注意点でした。（ニューラル音声指定しても使えないーとはまりました。。）

configuration.yamlに下記を記載して再起動します。pitchなどは自分の好みの値を探してみてください。
これで利用可能になります。

```.yaml
tts:
  - platform: microsoft
    api_key: 取得したAPIキー
    language: ja-jp
    gender: Female
    region: eastus
    type: NanamiNeural
    # rate: 20
    # volume: -50
    # pitch: high
    # contour: (0, 0) (100, 100)
```


service名: `tts.microsoft_say`
が生成されて利用できます。

下記のスクリプトを作成して実行するとcastのテストが可能です。

```.yaml
service: tts.microsoft_say
entity_id: media_player.グーグルホームのendity_id
data:
  message: こんにちわ。おげんきですか？
```

コマンドでのmp3データ作成

```.bash
curl --location --request POST 'https://eastus.tts.speech.microsoft.com/cognitiveservices/v1' \
--header 'Ocp-Apim-Subscription-Key: 取得したAPIキー' \
--header 'Content-Type: application/ssml+xml' \
--header 'X-Microsoft-OutputFormat: audio-16khz-128kbitrate-mono-mp3' \
--header 'User-Agent: curl' \
--data-raw '<speak version='\''1.0'\'' xml:lang='\''en-US'\''>
    <voice xml:lang='\''ja-JP'\'' xml:gender='\''Female'\'' name='\''ja-JP-NanamiNeural'\''>
        こんにちわ。テストです。
    </voice>
</speak>' > output.mp3
```

コマンドで使用可能なvoice一覧表示

```.bash
curl --location --request GET 'https://eastus.tts.speech.microsoft.com/cognitiveservices/voices/list' \
    --header 'Ocp-Apim-Subscription-Key: 取得したAPIキー'
```
japaneastだとニューラルの音声は使用できないのでeastusを使用（2021/01時点）


typeに指定できる音声一覧
https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/language-support#text-to-speech

- ニューラル
    - 女性
        - NanamiNeural
        - KeitaNeural
- Standard
    - 女性
        - Ayumi
        - HarukaRUS
    - 男性
        - Ichiro

公式Doc
https://www.home-assistant.io/integrations/microsoft/

価格
Cognitive Services のSpeech Services | Microsoft Azure https://azure.microsoft.com/ja-jp/pricing/details/cognitive-services/speech-services/

Text to Speech API リファレンス (REST) - Speech Service - Azure Cognitive Services | Microsoft Docs
https://docs.microsoft.com/ja-jp/azure/cognitive-services/speech-service/rest-text-to-speech#get-a-list-of-voices

Text-to-speech quickstart - Speech service - Azure Cognitive Services | Microsoft Docs
https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/get-started-text-to-speech?tabs=script%2Cwindowsinstall&pivots=programming-language-curl

## Amazon Polly

最初の1年のみ無料で、その後は1ヶ月最低4$(約440円)ぐらいかかるようなので私は利用しませんでした。
https://aws.amazon.com/jp/polly/pricing/

公式Doc
https://www.home-assistant.io/integrations/amazon_polly/

# ダッシュボードでのTTS用のテキストボックス用意


画面イメージ
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/1a8b0e42-4e3d-8a5b-49d1-d285920aee8c.png">

ダッシュボードにこのようなカードを追加できます。
スマートフォンのブラウザやHome Assistantアプリからテキストボックスに喋らせたいて言葉を入力してCast実行することが可能です。
複数のGoogle Homeから選択することも、複数台にまとめてCastもできます。

## 設定方法

まずは3つほど部品を作成する下準備を行います。

### ヘルパーで読み上げるテキストを作成する。
読み上げる文章のテキストボックスになります。

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/16fd4106-753a-83d7-1a33-b4e84d067fe0.png">

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/2a5994b5-4a5b-fbae-7d4a-d8896e871ee0.png">

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/c189b982-f995-3feb-407d-f0378ce3fc54.png">

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/dcf9e130-cc4f-b1b4-7ff4-22241da33d07.png">

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/3b463f44-35ce-b1bd-f551-f02c7237fa70.png">

### ヘルパーで通知先のドロップダウンを作成する。
Google Homeが複数台ある場合の選択肢になります。1台のみの場合は作成不要です。

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/61f53af3-7724-9a8c-3965-7181c548b9a7.png">

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/a34dc629-7cbf-8699-fd08-2c094cf32fb1.png">

テキストの時と同様に作成した通知先ドロップダウンを選択してエンティティIDを"input_select.tts_to"に設定する。（画像省略）

### TTS実行用のスクリプトを作成する
設定 → スクリプト → スクリプトを追加

GUIでは設定できないためYAML編集モードで設定します。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/4118a9f6-9638-863c-26a1-7a27f4783923.png">

下記のYAMLを入力し保存します。各位自分の環境に応じた値に書き換えて下さい。
※こちらのサンプルではserviceはCloud Text-to-Speechになっていますので、他のTTSを利用する場合はserviceを指定して下さい（tts.google_cloud_say
, tts.microsoft_say, tts.google_translate_say）
※なんとなく見ればわかると思いますがif文で通知先の文字列に応じて実際のGoogle HomeのエンティティIDに置き換えしています。複数指定することで同時に再生が可能です。

```.yaml
alias: TTS実行
sequence:
  - service: tts.google_cloud_say
    data_template:
      entity_id: |
        {%- if is_state("input_select.tts_to", "全て") -%}
          media_player.koarahub, media_player.living_nesthub, media_player.kumasan, media_player.panda, media_player.daidokoro, media_player.senmenjo
        {%- elif is_state("input_select.tts_to", "コアラ") -%}
          media_player.koarahub
        {%- elif is_state("input_select.tts_to", "リビング") -%}
          media_player.living_nesthub, media_player.daidokoro
        {%- elif is_state("input_select.tts_to", "くまさん") -%}
          media_player.kumasan
        {%- elif is_state("input_select.tts_to", "パンダ") -%}
          media_player.panda
        {%- elif is_state("input_select.tts_to", "洗面所") -%}
          media_player.senmenjo
        {%- endif -%}
    data:
      message: '{{ states.input_text.tts_input.state }}'
mode: single
```

ここらへんの構文をもっと追いかける場合は下記公式Docが参考になります。テンプレートエンジンとしてPythonのJinja2を利用しています。
Templating - Home Assistant
https://www.home-assistant.io/docs/configuration/templating/

### カードを追加する
ようやく下準備が整ったため実際にダッシュボードにカードを追加していきます。

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/8e7576b1-8a71-7439-0158-360785d31a7d.png">

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/2e61bcc8-68ca-c750-5167-b314f8dc4f4d.png">

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/23410e3b-b668-e065-64ff-ee8ee8c5aecd.png">

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/ab395b53-3a1f-4d60-35c5-b0db2a384f1f.png">

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/24a3ad66-71b5-f07c-4e5c-0278b3c5d79b.png">

これで完成です。テキストボックスに喋らせたい言葉を入力して試してみましょう！

## おまけ：ウィジェットの拡張
通常のtext_inputは表示名が場所をとる＆テキストボックス自体が右隅に小さくおいやられるのでフルでテキストボックスを表示するプラグインのようです。
動作未確認です。
githubを見ると更新されていないので動かない可能性がありそうですが参考にはなるかと思います。
https://community.home-assistant.io/t/lovelace-text-input-row/112002

# REST APIの利用方法

プロファイルからAPI Tokenを発行します。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/f3bf2870-121f-5ce6-899d-9828b11a853e.png">


curlでヘッダーにAPI Tokenをつけて呼ぶ。

TTS APIをREST APIで呼ぶ。Cloud Text-to-Speechのmp3の取得urlがjsonで返ってくる。

```.bash
    curl -X POST -H "Authorization: Bearer 取得したAPIトークン" \
           -H "Content-Type: application/json" \
           -d '{"message": "了解しました。後のことはまかせてね。", "platform": "google_cloud"}' \
           http://IPアドレス:8123/api/tts_get_url
```

TTS(Cloud Text-to-Speech)をcastするサービスをREST APIで呼ぶ。実際にGoogle Homeにcastされる。

```.bash
    curl -X POST -H "Authorization: Bearer 取得したAPIトークン" \
           -H "Content-Type: application/json" \
           -d '{"message": "了解しました。後のことはまかせてね。", "entity_id": "googleホームのエンティティID"}' \
           http://IPアドレス:8123/api/services/tts/google_cloud_say
``` 





REST API | Home Assistant Developer Docs https://developers.home-assistant.io/docs/api/rest/

# Home Assistantのアップデート方法
たまに更新して最新版にしてあげましょう。機能が増えたりいいことがあるかもしれません。脆弱性などもたまにあるようですし。
※Google Translateはこわれやすいので最新にしないと動かないケースがあります。

Supervisorにあります。CoreとOSどちらも最新にしましょう。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/462f13f1-6c7d-3d8e-8263-5a46b5c44fbf.png">


# Taskerとの連携
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">AndroidのTaskerを使うとプッシュ通知をトリガーにAPI叩けるのでGoogle HomeにキャストするAPI叩くと便利。<br>Panasonicの洗濯機が洗濯終わったらプッシュ通知するので、それをトリガーにキャストしてリビングにお知らせするように。<br>ここらへんの自由度はAndroidの強み！ <a href="https://t.co/o9mjQUY5pi">pic.twitter.com/o9mjQUY5pi</a></p>&mdash; ode (@odetarou) <a href="https://twitter.com/odetarou/status/1352640977937211392?ref_src=twsrc%5Etfw">January 22, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>