シミュレーションチャネル
概要
シミュレーションチャネルは、デバイスの状態を交換したり、セッションやデバイスへのコマンドを送信したりするために使用される、高頻度の双方向WebSocketチャネルです。
デフォルトのURL: ws://localhost:10001
ポートは設定で変更できます。
核心的な取り決め(重要):
- クライアントが接続すると、サーバーは完全なデバイス一覧を含む「Initial Inventory」メッセージを送信します。
- その後、クライアントから送信される各メッセージに対して、サーバーは必ず1つの「State Update」メッセージを送信します。
- 各「State Update」には、すべてのデバイスの状態(state および status)が含まれています。
このページでは、シミュレーションチャネルの主要なプロトコルとコマンドについて説明します。
その他のモジュールや機能は、それぞれのコマンドやステータスフィールドを通じて登録・拡張することができます。これらについては、「モジュール」の章で別途説明しています。
協定の規則
クライアントからの各メッセージに対して、1回の応答が行われます。
クライアントから送信される各メッセージに対して、サービスはすべてのデバイスの状態を含む「State Update」メッセージを1つ送信します。
これは、次のような意味です:
- デバイスの状態に関する最新のスナップショットが必要な場合は、何らかのコマンド(セッションコマンド、検出コマンド、またはデバイスコマンドのいずれか)を送信する必要があります。
- このチャネルの動作は、クライアントからのメッセージによって駆動される「ティック」ループに似ています。
力を加えないポーリング(プローブコマンド)
状態の変化を観察したいが、力を加えたりシミュレーションパラメータを変更したりしたくない場合は、プローブコマンドを使用してください:
probe_position用途:inverse3probe_orientationVerseグリップ用
検出コマンドにはコマンドデータは含まれておらず、デバイス情報を強制的に照会し、次の「State Update」において位置・方向データが最新の状態になるようにするためのものです。
制御コマンドの送信中です(set_cursor_force、set_cursor_position など)のシミュレーションセッション不要プローブコマンドの送信――応答フレームには常にデバイスの状態が含まれます。プローブコマンドは、以下の目的で使用されます。監視のみ(例:Haply Hub)といったセッションでは、デバイスを駆動させることなく、デバイスの状態をポーリングする必要があります。
配置と状態
- 初期在庫 メッセージにはデバイスの
config、state和status。 - 通常の 州の最新情報 メッセージにはデバイスの
state和status。
設定情報を含む完全なスナップショットを再度取得するには、以下を使用してください。 session.force_render_full_state。
configure と commands
デバイスメッセージは、2つの独立したマッピングをサポートしています:configure 和 commands。それぞれの用途は異なります:
| マッピング | 用途 | 送信頻度 |
|---|---|---|
configure | ワンタイムの永続設定:プリセット、基準、インストール方法、フィルター、モジュール設定 | 1回送信するか、変更があった際に送信する |
commands | ティックごとに有効、非永続的:力、位置、トルク | すべてのティックごとに送信する必要があります |
commands の項目(set_cursor_force、set_cursor_position、set_angular_torques、set_angular_position)各ティックごとに1回適用され、保存されることはありません——送信を停止すると、効果は直ちに失われ、デバイスはアイドル状態に戻ります。ティックをまたいで継続的に有効にし、繰り返し送信する必要のない内容については、以下を使用してください。 configure。
set_transform これは特例ですset_transform 所在地: commands 以下ですが、それは持続的な——本サービスでは、
新しい変換を送信するまで、最後に送信した変換が保持されます。あなたは必要ない各ティックごとにそれを送信します。
ただし、その用途はカメラ/シーンのナビゲーションであるため、
ユーザーがシーンをパンする際(例えば、1フレームごとに1つのトランスフォームを送信する場合など)、ティック頻度で継続的に送信することが、
連続的な移動ナビゲーションにおける想定される使用パターンとなります。
座標系
Haply デフォルトでは右手座標系が使用され、Z軸は上向きです。
2つの configure エントリが、座標の解釈と返り値の形式に影響を与えます:
session.configure.basis:Haply の座標とアプリケーション座標系との間の対応関係を変更する(後述の 基準)。inverse3[*].configure.preset:ワークスペースの原点を設定するために、名前が付けられた工場出荷時設定を1つ選択します(例:arm_front_centered会将(0, 0, 0)(作業スペースの中央に配置してください)。各デバイスの「Configure」セクションを参照してください。
メッセージの形式
このセクションでは、高層メッセージのエンベロープ構造とメッセージタイプについて説明します。完全な例については、このドキュメントの後半を参照してください。
機器のグループ分け
メッセージは最上位レベルでデバイスタイプごとにグループ化されます(例: inverse3、verse_grip、wireless_verse_grip)。各デバイスタイプキーは、各デバイスオブジェクトで構成される配列に対応しています。
初期インベントリ(サーバー → クライアント)
WebSocket 接続が確立された直後に 1 回送信します。
各項目には以下が含まれます:
device_idconfigstatestatus
参照:例:初期在庫の負荷
ステータス更新(サーバー → クライアント)
クライアントからのメッセージ1件ごとに、1回送信されます。
各項目には以下が含まれます:
device_idstatestatus
セッションコマンドエンベロープ(クライアント → サーバー)
セッションコマンドは、現在の接続/セッションに対して行われる操作であり、特定のデバイスを対象とするものではありません。
{
"session": {
"<command_name>": {
"...": "..."
}
}
}
デバイスコマンドエンベロープ(クライアント → サーバー)
デバイスコマンドは、デバイスタイプキーの下に配列として配置されて送信されるため、1つのメッセージで1つまたは複数のデバイスに同時にコマンドを送信することができます。各デバイスエントリは、同時に以下の機能をサポートしています。 configure マッピングと commands マッピング。
{
"<device_type>": [
{
"device_id": "<id>",
"configure": {
"<config_key>": { "...": "..." }
},
"commands": {
"<command_name>": { "...": "..." }
}
}
]
}
1つのメッセージに複数の項目を含めることで、同じ種類の複数のデバイスに同時にコマンドを送信することができます。
なお、commands これは辞書であり、1つのデバイスに対して複数のコマンドを含めることができますが、各コマンドタイプにつき1つしか存在できません。
execute フィールド
各コマンドまたは configure エントリは、オプションの "execute" ブール値(デフォルトは true)。設定 "execute": false その後、そのエントリは解析されますが、適用されることはありません。これは、常にすべてのフィールドを出力するリフレクションベースのシリアライザ(例:Unity JsonUtility)とても役に立ちます。
"set_cursor_force": { "execute": false, "vector": { "x": 0.0, "y": 0.0, "z": 0.0 } }
命令の参考
会話
完全な状態を強制的にレンダリングする
すべてのデバイスのステータスと設定のスナップショットを取得します。
{
"session": {
"force_render_full_state": {}
}
}
会話構成
セッションレベルの永続設定は、以下の方法で session.configure 送信。これはデバイスレベルの configure マッピングモードに対応しています。
プロファイル
セッションプロファイル名を設定します。Haply Hub はこの名前に基づいてエミュレーションを識別し、各アプリケーションのデバイス微調整設定を保存します。
{
"session": {
"configure": {
"profile": {
"name": "my_profile"
}
}
}
}
基準マッピング(セッションレベル)
セッション全体に対して基準マッピングを設定します。基準マッピングは、Haply の座標がアプリケーションの座標系にどのように変換されるかを定義するものです。設定後は、すべてのデバイス状態がこの基準に基づいて返され、送信されるすべての値もこの基準に基づいて解釈されます。
文字列は、Haply の右側/Z軸上向きの座標系を基準として表され、すなわち X、Y、Z ある並べ方のうち、次のように選択することも可能です。 + あるいは - 接頭辞として。例:
XYZ、ZYX、+Y-Z+X、X-ZYYZXあなたのYこの軸は、Haply の右側に対応しており、お客様のZこの軸は、Haply の正面に対応しています。あなたのXこの軸は、Haply の上部に位置しています。
左手のZ軸方向への例(Unreal、X-YZ):
{
"session": {
"configure": {
"basis": { "permutation": "X-YZ" }
}
}
}
session.set_basis 移行旧版 session.set_basis と session.configure.basis 軸の符号に関する規約が変更されました。旧コマンドでは有効だった配置でも、新コマンドでは逆の変換が行われる可能性があります。必要に応じて、該当する軸の符号を反転させてください(例: X-ZY ~になる XZ-Y)。
すべてのデバイスコマンド
検出(すべてのデバイス)
検出コマンドを使用すれば、力を加えたり、その他のシミュレーション変更を行ったりすることなく、最新の位置・姿勢情報を取得することができます。
- inverse3:
probe_position - ヴァース・グリップ(
verse_grip、wireless_verse_grip):probe_orientation
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"probe_position": {}
}
}
],
"verse_grip": [
{
"device_id": "049D",
"commands": {
"probe_orientation": {}
}
}
],
"wireless_verse_grip": [
{
"device_id": "049D",
"commands": {
"probe_orientation": {}
}
}
]
}
変換の設定(すべてのデバイス)
デバイスに対して座標系変換(デバイス空間からアプリケーション空間へ)を設定します。
その他 commands 項目が異なるため、set_transform はい持続的な——このサービスは、新しい値が送信されるまで、ユーザーが送信した最後の値を保持します。毎ティック送信する必要はありませんが、カメラやシーンのナビゲーションを目的としているため、ユーザーがシーンをパンするなど連続的な動きを行う場面では、ティック頻度で継続的に送信すること(例えば、1フレームごとにトランスフォームを送信するなど)が想定される使用法です。
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_transform": {
"transform": {
"position": { "x": 0.0, "y": 0.0, "z": 0.0 },
"rotation": { "x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0 },
"scale": { "x": 1.0, "y": 1.0, "z": 1.0 }
}
}
}
}
]
}
Verse grip およびワイヤレス Verse grip デバイスについては、変換中の rotation 分量が影響を及ぼす――グリップは姿勢のみを報告するため、したがって position と scale 拡大・縮小やスクロールできるコンテンツはありません。position 和 scale これらのフィールドは、Inverse3 の構造と整合性を保つためにのみ受け入れられ(スナップショットに反映されます)、通常は単一の値のままにしておくのが一般的です(position = {0,0,0},scale = {1,1,1})。
Inverse3 設備構成
配置項目が承認されました configure マッピングを送信します。これらは永続的です。一度送信すれば十分ですが、変更があった場合は再度送信することもできます。
プリセット
{
"inverse3": [
{
"device_id": "049D",
"configure": {
"preset": { "preset": "arm_front_centered" }
}
}
]
}
利用可能な値:device_defaults、arm_front、arm_front_centered、led_front、led_front_centered、custom。
基準マッピング(デバイスレベル)
{
"inverse3": [
{
"device_id": "049D",
"configure": {
"basis": { "permutation": "ZXY" }
}
}
]
}
インストール
デバイスの変換設定を行います。注意 mount 内部的な transform 包装層。
{
"inverse3": [
{
"device_id": "049D",
"configure": {
"mount": {
"transform": {
"position": { "x": 0.0, "y": 0.0, "z": 0.0 },
"rotation": { "x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0 },
"scale": { "x": 1.0, "y": 1.0, "z": 1.0 }
}
}
}
}
]
}
命令一方(クライアントからサーバー側)では、ラッピングと変換が行われます:"mount": { "transform": { ... } }。
スナップショット一方(サーバー側からクライアント側へ)はフラットな構造となっています:"mount": { "position": {...}, "rotation": {...}, "scale": {...} }。
これは意図的な仕様です。コマンドでは統一されたラッピング構造を使用するよう求められていますが、スナップショットでは変換が直接シリアライズされます。スナップショットの構造をコマンドのペイロードにコピーしないようご注意ください。
減衰
均一減衰および/または方向性減衰を制御します。少なくとも1つのフィールドを含める必要があります。
{
"inverse3": [
{
"device_id": "049D",
"configure": {
"damping": { "scalar": 0.5 }
}
}
]
}
方向性ダンピングを設定することも、あるいはその両方を同時に設定することもできます:
"damping": { "scalar": 0.5, "vector": { "x": 0.0, "y": 1.0, "z": 0.0 } }
力制御
フォースゲートの減衰ゲイン(0.0~1.0)を設定します。
{
"inverse3": [
{
"device_id": "049D",
"configure": {
"force_gate": { "gain": 0.3 }
}
}
]
}
Inverse3 命令
~に inverse3 デバイスがコマンドを送信する際は、 inverse3 このキーには、1つの device_id 一致するエントリ。このコマンドはティックごとに有効になり、有効な状態を維持するには各ティックごとに再送信する必要があります。
単一のデバイスにコマンドを送信する:
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_force": {
"vector": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}
1つのメッセージで複数のデバイスにコマンドを送信する:
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_force": {
"vector": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
},
{
"device_id": "049E",
"commands": {
"set_cursor_force": {
"vector": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}
カーソルの位置を設定する
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_position": {
"position": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}
カーソルの力を設定する
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_force": {
"vector": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}
角の位置を設定する
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_angular_position": {
"angles": {
"a0": 1.0,
"a1": 2.0,
"a2": 3.0
}
}
}
}
]
}
設定トルク
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_angular_torques": {
"torques": {
"a0": 1.0,
"a1": 2.0,
"a2": 3.0
}
}
}
}
]
}
Verse Grip の配置
Verse Grip およびワイヤレス版 Verse Grip デバイスは、Inverse3 と同じ configure キー、用途は preset、basis 和 mount。
Verse Gripの拡張
set_extension_data このコマンドは、Verse Grip 拡張プロトコルの一部であり、ボードレベル拡張通信プロトコルを実装したグリップのバージョンに適用されます。
グリップの拡張データを設定する
サポートされているデータ長:
- 上り(クライアント → デバイス)は最大 20 バイト。
- 下り(デバイス → クライアント)は最大 12 バイトで、ステータス更新メッセージを通じて
state.extension_data戻る。
データ仕様:
- 配列の長さ:20 バイト
- 値の範囲:各値は0~255です
{
"wireless_verse_grip": [
{
"device_id": "049D",
"commands": {
"set_extension_data": {
"extension_data": [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
]
}
}
}
]
}
例
例:初期機器リストの積載量
WebSocket が接続されると、サービスはデバイスの完全なリストを含むメッセージを送信します。初期メッセージは以下の形式をとります。 JSON 形式:
{
"inverse3": [
{
"device_id": "04BA",
"config": {
"type": "inverse3",
"port": "COM13",
"device_info": {
"major_version": 7,
"minor_version": 1,
"id": "04BA",
"device_type": 4,
"uuid": "2D35F80DD9005F599B68F49944CB04BA"
},
"extended_device_id": "2D35F80DD9005F599B68F49944CB04BA",
"extended_firmware_version": "8C20FDC8010AA1E15AA133CDA2534874",
"gravity_compensation": {
"enabled": true,
"scaling_factor": 1
},
"handedness": "right",
"streaming_mode": "USB",
"torque_scaling": {
"enabled": true
},
"home_return": {
"enabled": false
},
"filters": {
"force_gate": { "gain": 0.3 },
"damping": { "scalar": 0 }
},
"preset": "defaults",
"basis": { "permutation": "XYZ" },
"mount": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
}
},
"state": {
"cursor_position": {
"x": 0.07842738,
"y": -0.14836666,
"z": 0.14297646
},
"cursor_velocity": {
"x": -0.011969013,
"y": 0.0012009288,
"z": -0.043197
},
"angular_position": {
"x": -69.31704,
"y": 137.62952,
"z": 19.832787
},
"angular_velocity": {
"x": 0,
"y": 0,
"z": 0
},
"body_orientation": {
"x": -0.01940918,
"y": 0.7026367,
"z": 0.00048828125,
"w": 0.7113037
},
"current_cursor_force": { "x": 0, "y": 0, "z": 0 },
"current_cursor_position": { "x": 0, "y": 0, "z": 0 },
"current_angular_torques": { "x": 0, "y": 0, "z": 0 },
"current_angular_position": { "x": 0, "y": 0, "z": 0 },
"control_domain": "cartesian",
"control_mode": "force",
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"calibrated": false,
"in_use": false,
"power_supply": true,
"ready": true,
"started": true
}
}
],
"verse_grip": [
{
"device_id": "61548",
"config": {
"type": "verse_grip",
"port": "COM3",
"preset": "device_defaults",
"basis": { "permutation": "XYZ" },
"mount": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
}
},
"state": {
"button": false,
"hall": 0,
"orientation": {
"x": -0.5019531,
"y": 0.8632202,
"z": -0.048095703,
"w": -0.022338867
},
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"error": 0,
"ready": true
}
}
],
"wireless_verse_grip": [
{
"device_id": "0",
"config": {
"type": "wireless_verse_grip",
"port": "COM6",
"major_version": 1,
"minor_version": 4,
"hardware_version": 1,
"streaming_mode": "Radio",
"preset": "device_defaults",
"basis": { "permutation": "XYZ" },
"mount": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
}
},
"state": {
"battery_level": 0.816,
"battery_voltage": 3.77,
"buttons": {
"a": false,
"b": false,
"c": false
},
"hall": 16,
"orientation": {
"x": -0.019866943,
"y": -0.017486572,
"z": 0.05508423,
"w": -0.9963989
},
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"connected": true,
"awake": true,
"ready": true
}
}
]
}
例:状態更新負荷
サービスはメッセージを受信するたびに、すべてのデバイスの状態を含むステータス更新メッセージを送信します。
デバイスの状態を確認したい場合は、あらかじめデバイスにメッセージ(例えば、検出コマンドや力値など。値がゼロであっても)を送信する必要があります。デバイスを入力ソース(例えば、位置の追跡など)として使用し、力を加えていない場合、この点は特に重要です。
ステータス更新メッセージは以下の形式を採用しています JSON 形式:
{
"inverse3": [
{
"device_id": "04BA",
"state": {
"cursor_position": {
"x": 0.07842738,
"y": -0.14836666,
"z": 0.14297646
},
"cursor_velocity": {
"x": -0.011969013,
"y": 0.0012009288,
"z": -0.043197
},
"angular_position": {
"x": -69.31704,
"y": 137.62952,
"z": 19.832787
},
"angular_velocity": {
"x": 0,
"y": 0,
"z": 0
},
"body_orientation": {
"x": -0.01940918,
"y": 0.7026367,
"z": 0.00048828125,
"w": 0.7113037
},
"current_cursor_force": { "x": 0, "y": 0, "z": 0 },
"current_cursor_position": { "x": 0, "y": 0, "z": 0 },
"current_angular_torques": { "x": 0, "y": 0, "z": 0 },
"current_angular_position": { "x": 0, "y": 0, "z": 0 },
"control_domain": "cartesian",
"control_mode": "force",
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"calibrated": false,
"in_use": false,
"power_supply": true,
"ready": true,
"started": true
}
}
],
"verse_grip": [
{
"device_id": "61548",
"state": {
"button": false,
"hall": 0,
"orientation": {
"x": -0.5019531,
"y": 0.8632202,
"z": -0.048095703,
"w": -0.022338867
},
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"error": 0,
"ready": true
}
}
],
"wireless_verse_grip": [
{
"device_id": "0",
"state": {
"battery_level": 0.816,
"battery_voltage": 3.77,
"buttons": {
"a": false,
"b": false,
"c": false
},
"hall": 16,
"orientation": {
"x": -0.019866943,
"y": -0.017486572,
"z": 0.05508423,
"w": -0.9963989
},
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"connected": true,
"awake": true,
"ready": true
}
}
],
"custom_verse_grip": [
{
"device_id": "0",
"state": {
"battery_level": 0.816,
"battery_voltage": 3.77,
"buttons": {
"a": false,
"b": false,
"c": false
},
"hall": 16,
"orientation": {
"x": -0.019866943,
"y": -0.017486572,
"z": 0.05508423,
"w": -0.9963989
},
"extension_data": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"ready": true
}
}
]
}