주요 내용으로 이동

시뮬레이션 채널

개요​

시뮬레이션 채널은장치 상태를교환하고세션 또는장치 명령을전송하는 데 사용되는 고주파 양방향 WebSocket 채널입니다.

기본 URL: ws://localhost:10001

포트는 설정에서 변경할 수 있습니다.

핵심 약정 (중요):

  • 클라이언트가 연결되면 서비스는 전체 장치 목록이 포함된 ‘Initial Inventory’ 메시지를 전송합니다.
  • 그 후, 클라이언트에서 전송해 오는각 메시지에대해 서버는 정확히하나의 State Update 메시지를 전송합니다.
  • 각 State Update에는모든장치의 상태(state 및 status)가 포함됩니다.
모듈

이 페이지에서는 시뮬레이션 채널의핵심 프로토콜과명령어를 소개합니다.

다른 모듈/기능은 각각의 명령어 및/또는 상태 필드를 통해 시스템을 등록하고 확장할 수 있습니다. 이에 대한 내용은 ‘모듈’ 장에서 별도로 다루고 있습니다.

협정 규칙​

각 클라이언트 메시지마다 하나의 응답이 대응됩니다.​

클라이언트에서 전송해 온각메시지에 대해, 서비스는모든기기의 상태를 포함하는 State Update 메시지를하나씩 전송합니다.

이는 다음을 의미합니다:

  • 장치 상태에 대한 최신 스냅샷이 필요한 경우,특정 명령(세션 명령, 탐지 명령 또는 장치 명령 중 하나)을전송해야 합니다.
  • 이 채널의 작동 방식은 클라이언트 메시지에 의해 구동되는 “틱(tick)” 루프와 유사합니다.

힘을 가하지 않는 폴링(탐지 명령)​

상태 변화를 관찰하고 싶지만힘을 가하거나시뮬레이션 매개변수를 수정하지 않으려면 다음 탐지 명령을 사용하십시오:

  • probe_position ~에 사용 inverse3
  • probe_orientation Verse 그립용

탐지 명령에는 명령 데이터가 포함되어 있지 않으며, 장치 정보를 강제로 조회하여 다음 State Update에서 위치/방향 데이터가 최신 상태로 유지되도록 하는 데만 사용됩니다.

명령을 탐지할 필요가 없을 수도 있습니다.

제어 명령을 전송 중입니다 (set_cursor_force、set_cursor_position (등)의 시뮬레이션 세션필요 없음탐지 명령 전송——응답 프레임에는 항상 장치 상태가 포함됩니다. 탐지 명령은 다음 용도로 사용됩니다.모니터링 전용(예: Haply Hub)와 같은 세션의 경우, 장치를 구동하지 않은 상태에서 장치 상태를 폴링해야 합니다.

구성 및 상태​

  • 초기 재고 이 메시지에는 기기의 config、state 와 status。
  • 일반적인 주별 현황 이 메시지에는 기기의 state 와 status。

구성 정보가 포함된 전체 스냅샷을 다시 가져오려면 다음을 사용하십시오. session.force_render_full_state。

configure 와 commands​

장치 알림은 두 가지 독립적인 매핑을 지원합니다:configure 와 commands. 용도가 서로 다릅니다:

매핑용도발송 빈도
configure일회성 영구 설정: 사전 설정, 기준, 설치 방식, 필터, 모듈 설정한 번 전송하거나, 변경 시마다 전송
commands틱(tick) 단위로 적용되며, 비지속적: 힘, 위치, 토크매 틱마다 반드시 전송해야 합니다

commands 해당 항목(set_cursor_force、set_cursor_position、set_angular_torques、set_angular_position)매 틱마다 한 번씩 적용되며, 저장되지는 않습니다.——전송을 중지하면 효과가 즉시 사라지고 기기는 유휴 상태로 돌아갑니다. 반복 전송 없이 틱(tick)을 넘어 지속적으로 적용되도록 하려면 다음을 사용하십시오. configure。

비고
set_transform 이는 예외적인 경우입니다.

set_transform ~에 위치한 commands 아래, 하지만 그것은지속적인——서비스는 사용자가 마지막으로 전송한 변환을, 새로운 변환을 전송할 때까지 보관합니다. 사용자는필요 없다매 틱마다 이를 전송합니다. 하지만 이 기능은 카메라/씬 내비게이션 용도로 사용되므로, 사용자가 씬을 평행 이동할 때(예: 프레임마다 변환을 하나씩 전송하는 경우) 틱 주기로 지속적으로 이를 전송하는 것이, 바로 연속적인 이동 내비게이션에서 예상되는 사용 패턴입니다.

좌표계​

Haply 기본적으로오른손좌표계를 사용하며,Z축은 위쪽을 향합니다.

다음 두 가지 configure 항목은 좌표의 해석 및 반환 방식에 영향을 미칩니다:

  • session.configure.basis: Haply 좌표와 애플리케이션 좌표계 간의 매핑 관계를 변경합니다(아래의 기준).
  • inverse3[*].configure.preset: 작업 공간 원점을 설정하는 데 사용할 이름이 지정된 공장 기본 설정을 선택하십시오(예: arm_front_centered ~할 것입니다 (0, 0, 0) (작업 공간의 중앙에 배치하십시오). 각 장치의 ‘Configure’ 섹션을 참조하십시오.

메시지 형식​

이 절에서는 고수준 메시지 엔벨로프 구조와 메시지 유형에 대해 설명합니다. 전체 예제는 이 문서의 뒷부분에서 확인할 수 있습니다.

장비 그룹화​

메시지는 최상위 수준에서 장치 유형별로 그룹화됩니다(예: inverse3、verse_grip、wireless_verse_grip). 각 장치 유형 키는 여러 장치 객체로 구성된 배열 하나를 나타냅니다.

초기 인벤토리 (서버 → 클라이언트)​

WebSocket 연결이 수립된 직후 한 번 전송합니다.

각 항목에는 다음이 포함됩니다:

  • device_id
  • config
  • state
  • status

참조:예시: 초기 재고 부하

상태 업데이트 (서버 → 클라이언트)​

각 클라이언트 메시지에 대해 한 번씩 전송됩니다.

각 항목에는 다음이 포함됩니다:

  • device_id
  • state
  • status

참조:예시: State Update 부하

세션 명령문 봉투 (클라이언트 → 서버)​

세션 명령은 현재 연결/세션에 적용되는 작업이며,특정 장치를 대상으로 하지 않습니다.

{
"session": {
"<command_name>": {
"...": "..."
}
}
}

장치 명령서 봉투 (클라이언트 → 서버)​

장치 명령은 장치 유형 키 아래에 배열 형태로 배치되어 전송되므로, 단일 메시지를 통해 하나 또는 여러 대의 장치에 동시에 명령을 보낼 수 있습니다. 각 장치 항목은 동시에 다음을 지원합니다. configure 매핑 합계 commands 매핑.

{
"<device_type>": [
{
"device_id": "<id>",
"configure": {
"<config_key>": { "...": "..." }
},
"commands": {
"<command_name>": { "...": "..." }
}
}
]
}

하나의 메시지에 여러 항목을 포함시켜 동일한 유형의 여러 기기에 동시에 명령을 보낼 수 있습니다. 다음 사항에 유의하시기 바랍니다.commands 사전으로, 특정 장치에 대해 여러 개의 명령을 포함할 수 있지만, 각 명령 유형당 하나만 존재할 수 있습니다.

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-ZY
  • YZX 다음과 같이 표시하십시오. Y 축은 Haply 의 오른쪽 부분에 해당하며, 귀하의 Z 축은 Haply 의 앞쪽을 가리키며, 귀하의 X 축은 Haply 의 상단에 위치합니다.

왼쪽 Z축 상향 예시 (언리얼,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 그립(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 네지속적인——이 서비스는 사용자가 새로운 값을 전송할 때까지 마지막으로 전송한 값을 유지합니다. 매 틱마다 해당 값을 전송할 필요는 없지만, 카메라/씬 내비게이션 용도로 사용되는 만큼, 사용자가 씬을 이동하는 등 연속적인 움직임이 발생하는 상황에서는 틱 주기에 맞춰 지속적으로 전송하는 것(예: 프레임마다 변환 값을 한 번씩 전송)이 권장되는 사용법입니다.

{
"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 그립 / 무선 Verse 그립

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": {...} }。 이는 의도된 설계입니다. 명령어는 일관된 래퍼 구조를 사용해야 하는 반면, 스냅샷은 변환을 직접 직렬화합니다. 스냅샷의 구조를 명령어 페이로드에 복사하지 않도록 주의하십시오.

감쇠​

균일 감쇠 및/또는 방향성 감쇠를 제어합니다. 필드는 최소한 하나 이상 포함되어야 합니다.

{
"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 이 키 아래에는 하나 포함되어 있습니다. device_id 일치하는 항목. 이 명령은 틱(tick)마다 적용되며, 유효성을 유지하려면 매 틱마다 다시 전송해야 합니다.

단일 기기에 명령을 전송하려면:

{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_force": {
"vector": {
"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
}
}
}
},
{
"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
}
}
]
}

예시: 상태 업데이트 페이로드​

서비스는 메시지를 하나 받을 때마다 모든 장치의 상태가 포함된 상태 업데이트 메시지를 하나씩 전송합니다.

장치의 상태를 확인하려면 반드시 사전에 해당 장치에 메시지(예: 감지 명령이나 힘 값, 값이 0인 경우에도 포함)를 전송해야 합니다. 장치를 입력 소스(예: 위치 추적)로 사용하면서 힘을 가하지 않는 경우에는 이 점이 특히 중요합니다.

상태 업데이트 알림은 다음과 같이 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
}
}
]
}