1.はじめに
googleAssistantアプリにスマート家電のふりをしたサービスを登録することができました。スイッチのオン・オフ操作をサーバー上の何らかのスイッチに連動させることも可能ですが、自分なりのゴールとしては、自分が持っているmp3ファイルをリストに従って再生させることです。BGMを鳴らしたり、止めたりしたい。
googleAssistantのドキュメントにあるデバイスリストを眺めているとSPEAKER
とかSOUNDBAR
とかいったものがあります。オーディオサービス用っぽくて、これが使えると良さそうです。
とりあえずデバイスとして登録されたときにgoogleAssistant上にどういう表示がされるのか興味があったのでSOUNDBAR
を作成することにしました。
2.デバイス定義
デバイス定義は外部のJSONファイルに収めるようにしたので、最初に作ったモックアップの時にハードコーディングしたような苦労はしませんでした。どちらかというと、登録した後が面倒だったのですがそれについては後回しにします。
ドキュメントにあるサンプルを参考にデバイス定義を作成しました。ただしこのフォーマットはgoogleのものではなく、自作サービス(スマートホームデバイスとして操作できるようにする)で使用しているものです。googleAssistantが使用しているJSONフォーマットと部分的に互換性はあります。
(UTF-8)
{
"devices": [
{
"id": "s001",
"type": "action.devices.types.SOUNDBAR",
"traits": [
"action.devices.traits.OnOff",
"action.devices.traits.MediaState",
"action.devices.traits.Volume",
"action.devices.traits.TransportControl",
"action.devices.traits.InputSelector"
],
"name": {
"name": "ジュークボックス"
},
"willReportState": true,
"attributes": {
"transportControlSupportedCommands": [
"NEXT",
"STOP"
],
"volumeMaxLevel": 10,
"volumeCanMuteAndUnmute": true,
"supportActivityState": true,
"supportPlaybackState": true,
"availableInputs": [
{
"key": "リスト0",
"names": [
{
"lang": "ja",
"name_synonym": [
"リスト0",
"list0"
]
}
]
}
],
"deviceInfo": {
"manufacturer": "SELF MANUFACTURER",
"model": "BGM server",
"hwVersion": "0.02",
"swVersion": "0.90"
}
}
}
]
],
"conditions": {
"s001": {
"status": "SUCCESS",
"online": true,
"on": false,
"currentVolume": 10,
"isMuted": false,
"activityState": "ACTIVE",
"playbackState": "PLAYING",
"currentInput": "リスト0"
}
}
}
意外と手こずったのは"availableInputs"
のフォーマットです。デバイスを登録すると解るのですが、googleAssistant上には操作できないアイコンがあるだけです。音声で操作しなければならないのですが、その操作例が見当たりません。見当たらなかったような気がします。
特にavailableInputsはいわゆるプレイリスト相当に使えるですが、その切り替え操作が解りませんでした。
またname_synonym
が重要で、最初は英語なら大丈夫だろうと思っていたのですがlist0
を「ルート検索」に高い確率で聞き間違えるのでlang:'ja'
で明示し、UTF-8のマルチバイトで書いてしまうのが良いようです。さらに言えば、googleAssistantというか、Android側なのか、いろいろと漢字変換したりもするので、「入力名」には注意した方が良いようです。
また、googleAssistant内部での単語解釈に優先順位があるらしく、「BGM」とか「ミュージック」とかいった一般名詞を使うとストリーミングサービスとして解釈されがちです。
とにかく喋りつかれた記憶しかないのですが、それでも思いつくまま探り探り喋っているうちに、徐々に操作できるようになりました。
googleAssistantに音声コマンドが通ると、action.devices.EXECUTE
インテントが送られてくるので、あとは送られてきたJSONからパラメータを拾っていき、条件に合わせてロジックを動かせばよいことになります。それと、操作内容によってはデバイス側のステータスを変更する必要もあります。On/Offなどの生死状態や、設定している入力(currentInput
)などをステータスとして持ちます(が、それほど厳密に状態を見ているようでもなさそうでもあり……)。
SOUNDBARデバイスに使える全てのコマンドを試してはいないのですが、主だったコマンド(トレイト)は次のようなものでした。
2.1.「OK、グーグル。ジュークボックス、プレイ」
音声指示的には再生指示ですがトレイトとして使われるのはaction.devices.commands.mediaResume
になります。送られてくるJSONは次のようになります。
{"inputs":[
{
"context": {"locale_country":"JP","locale_language":"ja"},
"intent": "action.devices.EXECUTE",
"payload":{
"commands":[
{
"devices":[
{"id":"s001"}
],
"execution":[
{
"command":"action.devices.commands.mediaResume"
}
]
}
]
}
}
],
"requestId":"153123590........"
}
2.2.「OK、グーグル。ジュークボックスの入力をリスト0に切り替え」
本来はHDMI1,2,3などの切り替え用途のようですが、これを再生リストの切り替えに使います。action.devices.commands.SetInput
が使われました。
{"inputs":[
{"context":{
"locale_country":"JP",
"locale_language":"ja"
},
"intent":"action.devices.EXECUTE",
"payload":{
"commands":[
{
"devices":[
{"id":"s001"}
],
"execution":[
{
"command":"action.devices.commands.SetInput",
"params":{
"newInput":"リスト1"
}
}
]
}
]
}
}
],
"requestId":"3989949..........."
}
2.3.「OK、グーグル。ジュークボックス、ストップ」
action.devices.commands.mediaStop
もあるのですが、action.devices.commands.OnOff
が送られてきました。なぜそういう使い分けをしているのかは解りません。
{"inputs":[
{"context":{
"locale_country":"JP",
"locale_language":"ja"
},
"intent":"action.devices.EXECUTE",
"payload":{
"commands":[
{
"devices":[
{"id":"s001"}
],
"execution":[
{
"command":"action.devices.commands.OnOff",
"params":{"on":false}
}
]
}
]
}
}
],
"requestId":"9590154.........."
}
3.再生デバイス
肝心の再生デバイスですがコアとしてはgoogle-Home-Player(google-home-notifierの代替となりそうなgoogle-home-playerを試してみる/@miso_develop)を使いました。同期再生というか、再生の終わりを検出できるのが利点です。
4.おわりに
最初は単純にPHPのexec()で実行させましたが、この方式だとgoogleAssistantへの応答が遅れ、「通信できませんでした」と言われます。pcntl_fork()/pcntl_exec()を使い、バックグラウンドサービス化してしまうのが良さそうです。
音楽再生サーバの作成については稿を改めたいと思います。