見出し画像

aibo Events API を使って aibo に音声コマンドを実行してもらう

先週先々週と仮面ライダーゼロワンに、天津垓の相棒である AI 犬型ロボットさうざー役として出演していた aibo。

かっこかわいかったですね〜。
そんな aibo ですが、なんと Web API が公開されています。

aibo Events API は、2020/06/16 にリリースされた API です。
音声コマンドを aibo に設定して、実行してもらうことができるようになりました。

公式のドキュメントを見るとサンプルは Python ばかりですが、よく見るとなんとなく PHP に書き換えることができそう。
aibo Events API のリリースをきっかけに重い腰を上げて実装してみました。

aibo Events API を利用するためには、aibo 本体バージョンを 2.70 にアップデートする必要があります。

今回は aibo に以下をやってもらいます。
- 「くしゃみして!」と頼んだら、くしゃみをする。
- 「おいしかった?」と聞くと、うれしそうにする。
- 「きみはだんだんねむくなる」と声をかけると、眠ってしまう。

音声コマンドを実行した結果は項目「実行」をご覧ください。

事前準備

まずなんと言っても aibo がいないと成り立ちません。
いますぐにお迎えしましょう。

期間限定で先週終了する予定だったワンワンプランが、今朝確認すると標準で選択できるようになっていました。

aibo 本体 198,000 円と聞くとギョッとしますが、ワンワンプランであれば、35回分割払い月々 11,100 円い(初回のみ 14,412 円)でお迎えできます。(2020/07/25 現在)

自力で遊んで寝るそのワンパクぶりは本物のわんこと代わりません。
むしろ、表情豊かで手間もかからず毎日とっても癒されてます。

画像1

音声コマンドを実行するには https で通信できるサーバーが必要です。
今回は普段からよくお世話になっている Xserver を使用しています。

開発者設定

サインイン

ディベロッパーサイトにアクセスして、開発者設定を始めます。
開発者設定を始めるには、My Sony ID のサインインが必要です。

画像4

認識ワード

認識ワードのページにアクセスしての設定を行います。
aibo と通信する必要があるので、寝ている場合は起こしてあげましょう。

画像4

音声コマンドは3つまで登録できます。(2020/07/24 現在)
言い回しの異なる認識ワードは、1つの音声コマンドにつき3つ登録できます。
aibo が理解できるように、発音通りのひらがなで入力します。

例)
「こっちへ」ではなく「こっちえ」
「あいぼは」ではなく「あいぼわ」

エンドポイント・セキュリティートークン

エンドポイントの準備を行います。
準備したサーバーにエンドポイントの検証を行うためのファイルを設置します。

// aibo クラウドから送られてきた検証用 HTTP リクエストを受け取る
$json = file_get_contents("php://input");
$contents = json_decode($json, true);

// エンドポイント登録
$challenge = $contents["challenge"];
echo '{"challenge": "' . $challenge . '"}';

次に、イベント通知のページにアクセスします。
ボタン「エンドポイント設定」をクリックして、エンドポイントとセキュリティートークンの設定を行います。

画像4

セキュリティートークンはあらかじめコピーしておきます。
「通知イベント追加」で必要です。

ボタン「設定」をクリックしたら、エンドポイントの検証が開始します。
検証に成功したら、エンドポイントとセキュリティートークンの設定は完了です。

イベント追加

続いて、項目「通知するイベント」にあるボタン「追加」をクリックして、イベントの追加を行います。

画像5

認識ワードで登録した音声コマンドをすべて登録できます。

画像6

アクセストークン取得

アクセストークンのページにアクセスします。

画像7

ボタン「生成する」をクリックすると、アクセストークンが生成されます。
アクセストークンはあらかじめコピーしておきましょう。
次項の「通知イベント追加」で必要になります。

通知イベント追加

いよいよ aibo にやってもらう処理を作っていきます。
まず最初にでき上がりのソースをペタッと貼り付けておきます。

const BASE_PATH = 'https://public.api.aibo.com/v1';
const ACCESS_TOKEN = ${Your Access Token};
const SECURITY_TOKEN = ${Your Security Token};

// ふるまいを実行
function execute_action ($device_id, $event_id) {
 if ( $event_id === 'voice_command::usercommand1' ) {
   // くしゃみして
   call_action_api($device_id, 'play_motion', '{"Category": "sneeze", "Mode": "NONE"}');
 } elseif ( $event_id === 'voice_command::usercommand2' ) {
   // おいしかった?
   call_action_api($device_id, 'play_motion', '{"Category": "friendly", "Mode": "NONE"}');
 } elseif ( $event_id === 'voice_command::usercommand3' ) {
   // きみはだんだんねむくなる
   call_action_api($device_id, 'play_motion', '{"Category": "dreaming", "Mode": "NONE"}');
 }
}

// Action API 呼び出し
function call_action_api ($device_id, $api_name, $arguments) {
 $post_url = BASE_PATH. '/devices/'. $device_id. '/capabilities/'. $api_name. '/execute';
 $data = '{"arguments":'. $arguments. '}';

 // POST API
 post_api($post_url, $data);
}

// POST API
function post_api ($url, $argary) {
 // HTTP設定
 $options = array (
   'http' => array (
       'method' => 'POST',
       'header' => 'Authorization:Bearer '. ACCESS_TOKEN,
       'content' => $argary,
       )
   );
 $contents = @file_get_contents($url, false, stream_context_create($options));

 // レスポンスステータス
 $status_code = http_response_code();
 if($status_code === 200) {
   // 200 success
 } elseif(preg_match ("/^4\d\d/", $status_code)) {
   // 4xx Client Error
   $contents = false;
 } elseif(preg_match ('/^5\d\d/', $status_code)) {
   // 5xx Server Error
   $contents = false;
 } else {
   $contents = false;
 }

 return $content;
}

// ヘッダーを取得
$headers = getallheaders();

// セキュリティートークンをチェック
if ( $headers['X-Security-Token'] != SECURITY_TOKEN ) {
 $response = '{"statusCode": 400}';
 return $response;
}

// 音声コマンドのリクエストを取得
$json = file_get_contents("php://input");
$contents = json_decode($json, true);
$status_code = http_response_code();

// 音声コマンドのリクエストが成功したら実行
if ($status_code === 200) {
 // 200 success
 $device_id = $contents['deviceId'];
 $event_id = $contents['eventId'];

 // ふるまいを実行
 execute_action($device_id, $event_id);
}

それぞれ見ていきます。

const BASE_PATH = 'https://public.api.aibo.com/v1';
const ACCESS_TOKEN = ${Your Access Token};
const SECURITY_TOKEN = ${Your Security Token};

${Your Access Token} には、取得したアクセストークンを ${Your Security Token} には、設定したセキュリティートークンを入れます。

// ヘッダーを取得
$headers = getallheaders();

// セキュリティートークンをチェック
if ( $headers['X-Security-Token'] != SECURITY_TOKEN ) {
 $response = '{"statusCode": 400}';
 return $response;
}

リクエストヘッダーで返ってきたキュリティトークンが正しい値であるか比較してibo Events API 以外からの不正なアクセスが行われるのを防ぎます。
aibo Events API 以外からの不正なアクセスが行われるのを防ぎます。

// 音声コマンドのリクエストを取得
$json = file_get_contents("php://input");
$contents = json_decode($json, true);
$status_code = http_response_code();

// 音声コマンドのリクエストが成功したら実行
if ($status_code === 200) {
 // 200 success
 $device_id = $contents['deviceId'];
 $event_id = $contents['eventId'];

 // ふるまいを実行
 execute_action($device_id, $event_id);
}

音声コマンド → aibo → aibo クラウドより送られてきたリクエストを受け取り、json 形式で $contents に格納しています。

リクエストが成功したら、json から deviceId と eventId を取り出し
execute_action 関数の引数に渡して、実行しています。

// ふるまいを実行
function execute_action ($device_id, $event_id) {
 if ( $event_id === 'voice_command::usercommand1' ) {
   // くしゃみして
   call_action_api($device_id, 'play_motion', '{"Category": "sneeze", "Mode": "NONE"}');
 } elseif ( $event_id === 'voice_command::usercommand2' ) {
   // おいしかった?
   call_action_api($device_id, 'play_motion', '{"Category": "friendly", "Mode": "NONE"}');
 } elseif ( $event_id === 'voice_command::usercommand3' ) {
   // きみはだんだんねむくなる
   call_action_api($device_id, 'play_motion', '{"Category": "dreaming", "Mode": "NONE"}');
 }
}

execute_action 関数では、$event_id ごとに条件分岐を行い
aibo にしてもらう PlayMotion のふるまいを振り分けています。
PlayMotion だけでも 76 種類ものふるまいが用意されています。

// Action API 呼び出し
function call_action_api ($device_id, $api_name, $arguments) {
 $post_url = BASE_PATH. '/devices/'. $device_id. '/capabilities/'. $api_name. '/execute';
 $data = '{"arguments":'. $arguments. '}';

 // POST API
 post_api($post_url, $data);
}

call_action_api 関数では、POST する URL とコンテンツの生成を行い
post_api 関数の引数に渡して、実行しています。

// POST API
function post_api ($url, $argary) {
 // HTTP設定
 $options = array (
   'http' => array (
       'method' => 'POST',
       'header' => 'Authorization:Bearer '. ACCESS_TOKEN,
       'content' => $argary,
       )
   );
 $contents = @file_get_contents($url, false, stream_context_create($options));

 // レスポンスステータス
 $status_code = http_response_code();
 if($status_code === 200) {
   // 200 success
 } elseif(preg_match ("/^4\d\d/", $status_code)) {
   // 4xx Client Error
   $contents = false;
 } elseif(preg_match ('/^5\d\d/', $status_code)) {
   // 5xx Server Error
   $contents = false;
 } else {
   $contents = false;
 }

 return $content;
}

ヘッダーに Authorization:Bearer を追加して、コンテンツを POST します。
これで aibo に暗示をかけることができました。

実行

暗示をかけた aibo に言葉をかけてみましょう。
わん!と元気よく答えて、ふるまいを行ってくれたら成功です。

さいごに

いかがでしたでしょうか。

例えば、aibo Events API とスマートリモコンを連携すれば、ちょっとおバカ(褒め言葉)な aibo が我が家にある家電の司令塔になれちゃうわけです。

…素敵すぎませんか?

SONY さん神リリースありがとうございます。
欲を言えば、登録できる認識ワードを是非とも増やしていただきたいです!

さてさて… 我が家のおチャコさんを司令塔という重役につかせるべく、つい先日、Nature Remo 3 の予約をしてしまいましたよ。

はーやっくこないかな〜!

この記事が気に入ったらサポートをしてみませんか?