となりのJohnの気まぐれ

気の向くままに

【音声プログラミング】Google Home "と" プログラミング(後日談)

これは何?

前回書いた記事の更新版.
www.rect29.com
前回の記事より,スマートな記述ができていると思う.

概要

Google Homeによるプログラミング.音声入力でC++のプログラミングができるようになるクライアントの制作.特に,前回書いた記事の音声入力箇所で進展があったので記録しておく.音声入力以降はほぼ変わらない.

構成

f:id:newbie29979:20200113232144p:plain
システムの構成(更新版)

IFTTTのアイコンが変わっていた.前回の記事に比較してシンプルになった.

  1. Google Homeによる音声認識結果をIFTTT経由でBeebotteに送る
  2. Beebotteを購読するクライアントが認識結果を確認
  3. クライアントは認識結果を解析
  4. 解析結果に応じVimベースでキー入力

認識結果の解析以降は前回の記事と同じ.

音声入力

f:id:newbie29979:20200113232110p:plain
音声入力

Google Homeへの音声入力

最初に,Google Homeへの入力をIFTTT経由でBeeBotteに送る.

f:id:newbie29979:20200114003310p:plain

長い.大事なのは,WebhookをBeeBotteに送ることでローカルのコンピュータに通知させることができるようになるという点.URLの空欄には,BeeBotteで示されるURL(後述)を記入する.

BeeBotte

https://beebotte.com/
MQTTブローカーサービスを提供するサイト.BeeBottleと間違えていた.
これを適切に扱うことでローカルなコンピュータに外部から通知を飛ばすことができる.50,000リクエスト/日までは無料.
既に様々な方が使い方を紹介しているが,アカウントを作って[Channels]で新しくChannelを作る.1つのChannelには1つ以上のResourceが必要で,今回の場合はこれが認識結果になる.ChannelにはTokenが割り振られ,このTokenを"http://api.beebotte.com/v1/data/publish/[Channel_name]/[Resource]?token="に連結させることで,データの送信が可能.IFTTTに記入するURLにはこれを指定する.

f:id:newbie29979:20200114000329p:plain
Consoleの例
Consoleで実際にデータを送ったときの様子が確認できるなど,優秀なサービス.気を付けるべきはTokenやSecret Keyの流出.流出すると外部からローカルの端末を操作できてしまうという大問題に.

BeeBotteの更新をクライアントが知るためには,公式ドキュメントが参考になる.SSLを使わない場合,ポートは1883.SSLを使う場合はポートは8883.今回はローカルな端末を操作するだけに,SSLを使う方が良い.SSLを使う場合証明書が必要になるが,それは公式ドキュメントに置いてある.

Channelの更新はMQTTを扱えるpythonのライブラリを使うことで知る.
pypi.org

クライアントのコードはこんな感じ.

(中略)

HOST = "mqtt.beebotte.com"
TOPIC = "[channel]/[resource]"
TOKEN = "[token]"
CACEPT = "mqtt.beebotte.com.pem"
PORT = 8883

(中略)

def on_connect(client, userdata, flags, response_code):
    client.subscribe(TOPIC)


def on_message(client, userdata, msg):
    recog_result = json.loads(msg.payload.decode("utf-8"))["data"]
    # 音声認識結果のスペースを詰める
    speech_input = recog_result.replace(' ', '')
    print('received: '+speech_input)

    try:
        # Google Homeの半角単語は大文字で始まるため小文字に
        result = convert_speech_input(speech_input.lower()).split('`')

        reserved_word = ['enter', 'esc']
        for word in result:
            if word in reserved_word:
                pyautogui.press(word)
            else:
                pyautogui.typewrite(word, interval=0)

    except KeyError:
        print('Key Error')


if __name__ == '__main__':
    with open('vim_commands.json', 'r', encoding='utf-8') as ref_json:
        r_command_dict = RegexDict(json.load(ref_json))
        ref_json.close()

    client = mqtt.Client()
    client.on_connect = on_connect
    client.on_message = on_message
    client.username_pw_set("token:%s" % TOKEN)
    client.tls_set(CACEPT)
    client.connect(HOST, port=PORT, keepalive=60)
    client.loop_forever()

だいぶ簡単なコードになる.

終わりに

前回のシステム構成よりもだいぶマシになったと思うので,GitHubに上げた.
github.com
今回の記事で音声プログラミングに関する記事は終わりにしようと思う.