未来は近づいています。 約10年前、私は音声コマンドの助けを借りて車を始動させるとは考えられませんでした!
近年、私は興味を持って音声アシスタントの急速な発展を見てきました。 Google Home Miniのリリース後、「おもちゃ」の価格がだいたい適切になったので、試してみることにしました。 最初のプロジェクトは、自動起動、座標制御、バッテリー電圧、および車のアラームによって与えられるその他のパラメーターのために、音声アシスタントとStarLine GSMモジュールの統合です。 それでは、行きましょうか?
Google Homeが存在する必要はありません。以下で説明するすべては、電話のGoogle Assistantアプリケーションで動作します。 StarLine M31 GSM / GPSモジュールをインストールしていますが、StarLineのすべてのGSMアラームで動作するはずです。
Google Assistantのアプリケーションの一般的なレイアウト
- Google Home / Google Assistantは、音声をテキストに、またはその逆に変換し、さらに標準のGoogleサービスとやり取りします。 Googleの用語でActionというアプリケーションを呼び出すと、要求がDialogFlow(図のAPI.AI)に送信されます。
- DialogFlow-ダイアログスキームの決定、自然言語でのリクエストテキストの処理、エンティティの選択、回答の生成、および必要に応じてWebHookを呼び出すことによる外部世界とのやり取りを担当します。
- WebHook-外の世界との相互作用のためのWEBサービス。 ダイアログブランチ(インテント)+リクエストから抽出されたパラメーター(エンティティ)が入力に送られます。 出力はユーザーへの応答です。
1. DialogFlow.com
最初に、ダイアログフロー(以前のAPI.AI)でアプリケーション(エージェント)を作成する必要があります。
Google HomeにリンクされるGoogleアカウントを使用して登録します。
残念ながら、ロシア語はまだGoogle Assistantで使用できません。英語を選択します。
次に、インテントを作成する必要があります。 DialogFlowの用語の意図は、特定のアクションを担当するダイアログのブランチの1つです。 この場合、これらはGetBattery、GetTemperature、StartEngine、StopEngineになります。 また、最初に機能するデフォルトのインテントもあります。通常は、このアプリケーションで何ができるかについての挨拶と短い話です。
各インテントでは、音声コマンド(ユーザーの言う)の例を指定する必要があります(5〜10の異なるオプションが望ましい)。
デフォルトを除くすべてのインテントで、スクリプト(WebHook)にリクエストを送信する必要があるため、Fulfillment-Use webhookを設定します。
2. Starlineサーバーと対話するWebHook
DialogFlowからのリクエストからインテントを受け取り、Starlineコマンドをプルするスクリプトが必要です。 最速の方法は、これをPython + Flaskで実装することでした。
StarLineとの相互作用は、ここから + ブラウザのスニファーの関連性に刻印されています。
サーバーで実行するには、gunicornを使用しました
gunicorn -b :3333 flask.starline:app
+リバースプロキシとしてのnginx。
HTTPSが必要であることに注意してください!
starline.py
from flask import Flask, request from flask_restful import reqparse, Resource, Api, abort import requests import logging DEVICE_ID = 1234567 # Use HTTPS sniffer to find your DEVICE_ID in https://starline-online.ru/ traffic LOGIN = 'YOUR_STARLINE_EMAIL' PASS = 'YOUR_STARLINE_PASSWORD' logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') header = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:28.0) Gecko/20100101 Firefox/28.0', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest'} def start_engine(): with requests.Session() as session: t = session.get('https://starline-online.ru/', headers=header) login = session.post('https://starline-online.ru/user/login', { 'LoginForm[login]': LOGIN, 'LoginForm[pass]': PASS, 'LoginForm[rememberMe]': 'off'}, headers=header) logging.debug(login.content) r0 = session.get('https://starline-online.ru/device', headers=header) logging.debug(r0.content) r = session.post('https://starline-online.ru/device/{0}/executeCommand'.format(DEVICE_ID), { 'value': '1', 'action': 'ign', 'password': ''}, headers=header, timeout=1) logging.debug(r.status_code) logging.debug(r.content) logout = session.post('https://starline-online.ru/user/logout', { '': ''}, ) return ('Engine started!') def stop_engine(): with requests.Session() as session: t = session.get('https://starline-online.ru/', headers=header) login = session.post('https://starline-online.ru/user/login', { 'LoginForm[login]': LOGIN, 'LoginForm[pass]': PASS, 'LoginForm[rememberMe]': 'off'}, headers=header) logging.debug(login.content) r0 = session.get('https://starline-online.ru/device', headers=header) logging.debug(r0.content) r = session.post('https://starline-online.ru/device/{0}/executeCommand'.format(DEVICE_ID), { 'value': '0', 'action': 'ign', 'password': ''}, headers=header) logging.debug(r.status_code) logging.debug(r.content) logout = session.post('https://starline-online.ru/user/logout', { '': ''}, ) return ('Engine stopped!') def get_params(): with requests.Session() as session: t = session.get('https://starline-online.ru/', headers=header) login = session.post('https://starline-online.ru/user/login', { 'LoginForm[login]': LOGIN, 'LoginForm[pass]': PASS, 'LoginForm[rememberMe]': 'off'}, headers=header) logging.debug(login.content) r0 = session.get('https://starline-online.ru/device', headers=header) logging.debug(r0.content) res_dict = r0.json()['answer']['devices'][0] logout = session.post('https://starline-online.ru/user/logout', { '': ''}, ) return {'battery': res_dict['battery'], 'temperature': res_dict['ctemp']} def get_battery_text(): return ("Battery voltage {0} volts.".format(get_params()['battery'])) def get_temperature_text(): return ("Temperature: {0} degrees.".format(get_params()['temperature'])) app = Flask(__name__) app.config['BUNDLE_ERRORS'] = True api = Api(app) class ProccessGoogleRequest(Resource): def get(self): return {"status": "OK"} def post(self): req = request.get_json() logging.debug(request.get_json()) response = '' if req['result']['metadata']['intentName'] == 'GetBattery': response = get_battery_text() if req['result']['metadata']['intentName'] == 'GetTemperature': response = get_temperature_text() if req['result']['metadata']['intentName'] == 'StartEngine': response = start_engine() if req['result']['metadata']['intentName'] == 'StopEngine': response = stop_engine() if response == '': abort(400, message='Intent not detected') return {"speech": response, "displayText": response} api.add_resource(ProccessGoogleRequest, '/starline/') if __name__ == '__main__': app.run(debug=False)
はい、この機会を利用して、 スターラインチームに連絡したいと思います-皆さん、ドキュメントで通常のAPIを作成してみませんか? サードパーティ製品との統合は何倍にもなりますか?
3.シミュレーターおよび実際のデバイスでのテスト
DialogFlowでテストするには、 [統合]-> [Googleアシスタント]-> [統合設定]-> [テスト]を選択し、Googleシミュレーターのアクションに移動します
そして、これが実世界でのテスト結果です
このバージョンでは、唯一、エンジンが実際に起動するまで「エンジンが起動しました」と応答します。Starlineからの応答を待つ時間がないためです。
アイデア:
1. Google Assistantからの位置のリクエスト。車までの距離を記録します(Starlineは座標を与えることができます)。 PythonのWebHookがGoogle Homeの場所を要求する方法は不明です。
2. Google <-> Starlineの統合を簡素化するために、パスワードをハードコーディングする必要はありません。 私が理解しているように、スターラインの関与がなければ、これは不可能です。
既知の問題:
1. Googleアシスタントには、エンジンの起動ステータスに関するStarlineサーバーからの応答を待つ時間がありません。
2.テスト中は、デフォルトのアプリケーション名(呼び出し)のみを使用できます-ちょっとGoogle、 私のテストアプリに話しかけてください。
便利なリンク:
1. Googleからのビデオ
2. エンティティを使用した例