Zum Hauptinhalt springen
Version: 3.5.x

Simulation Kanal

Übersicht

Der Simulationskanal ist ein bidirektionaler WebSocket-Kanal mit hoher Übertragungsrate, der zum Austausch von Gerätestatusdaten und zum Senden von Sitzungs- oder Gerätebefehlen dient.

Standard-URL: ws://localhost:10001

Der Port kann in der Konfiguration geändert werden.

Kernvertrag (wichtig):

  • Wenn sich ein Client verbindet, sendet der Dienst eine erste Bestandsmeldung, die die vollständige Liste der Geräte enthält.
  • Danach sendet der Server für jede vom Client empfangene Nachricht genau eine Statusaktualisierungsnachricht.
  • Jedes Status-Update enthält den Status (und den Zustand) aller Geräte.
Module

Auf dieser Seite werden das Kernprotokoll und die Befehle des Simulationskanals beschrieben.

Zusätzliche Module/Funktionen können sich registrieren und das System um eigene Befehle und/oder Statusfelder erweitern. Diese sind separat im Abschnitt „Module“ dokumentiert.

Protokollregeln

Eine Antwort pro Client-Nachricht

Der Dienst sendet für jede vom Client empfangene Nachricht eine Statusaktualisierungsnachricht, die den Status aller Geräte enthält.

Das bedeutet:

  • Wenn Sie einen aktuellen Überblick über den Gerätestatus benötigen, müssen Sie einen Befehl senden (einen Sitzungsbefehl, einen Abfragebefehl oder einen Gerätebefehl).
  • Der Kanal verhält sich wie eine durch Client-Nachrichten gesteuerte „Tick“-Schleife.

Abfrage ohne Kraftanwendung (Sondenbefehle)

Wenn Sie Zustandsänderungen beobachten möchten, ohne Kräfte anzuwenden oder Simulationsparameter zu ändern, verwenden Sie die Abfragebefehle:

  • probe_position für inverse3
  • probe_orientation für Verse-Griffe

Abfragebefehle enthalten keine Befehlsdaten, sondern lösen lediglich Abfragen von Geräteinformationen aus, damit die Positions- und Orientierungsdaten bei der nächsten Statusaktualisierung auf dem neuesten Stand sind.

Möglicherweise benötigen Sie keine Sondenbefehle

Simulationssitzungen, die bereits Steuerbefehle senden (set_cursor_force, set_cursor_position, usw.) tun nicht Es ist nicht erforderlich, Abfragebefehle zu senden – der Gerätestatus ist immer im Antwortrahmen enthalten. Abfragen dienen dazu, nur zur Überwachung Sitzungen (z. B. Haply ), die den Gerätestatus abfragen müssen, ohne das Gerät anzusteuern.

Konfiguration vs. Zustand

  • Die Ausgangsbestand Die Nachricht enthält das Gerät config, stateund status.
  • Regulär Aktuelles aus dem Bundesstaat Nachrichten enthalten Gerät state und status.

Wenn Sie erneut einen Snapshot einschließlich der Konfiguration benötigen, verwenden Sie session.force_render_full_state.

configure vs commands

Gerätemeldungen unterstützen zwei separate Zuordnungen: configure und commands. Sie dienen unterschiedlichen Zwecken:

KarteZweckHäufigkeit
configureEinmalige dauerhafte Konfiguration: Voreinstellungen, Basis, Einbindung, Filter, ModuleinstellungenEinmal senden oder bei Änderung
commandsPro Takt, nicht persistent: Kraft, Position, DrehmomenteMuss bei jedem Tick gesendet werden

Einträge in commands (set_cursor_force, set_cursor_position, set_angular_torques, set_angular_position) werden angewendet einmal pro Takt und wird nicht gespeichert -- Wenn Sie die Übertragung beenden, hört der Effekt sofort auf und das Gerät kehrt in den Ruhezustand zurück. Verwenden Sie configure für alles, was über mehrere Ticks hinweg bestehen bleiben soll, ohne erneut gesendet zu werden.

set_transform ist ein Sonderfall

set_transform lebt in commands aber ist anhaltend — Der Dienst behält die zuletzt gesendete Transformation bei, bis Sie eine neue senden. Sie müssen nicht haben es bei jedem Tick zu übertragen. Da es jedoch für die Kamera- und Szenennavigation gedacht ist, ist das Streaming mit Tick-Rate (z. B. eine Transformation pro Frame, wenn der Benutzer die Szene schwenkt) das erwartete Nutzungsmuster für die Navigation mit kontinuierlicher Bewegung.

Koordinatensysteme

Haply standardmäßig ein rechtshändiges Koordinatensystem mit Z-Achse nach oben.

Zwei Konfigurations-Einträge beeinflussen, wie Koordinaten interpretiert und zurückgegeben werden:

  • session.configure.basis : Ändert die Zuordnung zwischen Haply und dem Koordinatensystem Ihrer Anwendung (siehe Grundlage (siehe unten).
  • inverse3[*].configure.preset : Wählt eine benannte Fabrikkonfiguration aus, die den Ursprung des Arbeitsbereichs (z. B. arm_front_centered setzt (0, 0, 0) (im Workspace-Center). Siehe den Abschnitt „Konfigurieren“ für das jeweilige Gerät.

Nachrichtenformate

In diesem Abschnitt werden die allgemeinen Rahmen- und Nachrichtentypen beschrieben. Ausführliche Beispiele finden Sie weiter unten in diesem Dokument.

Gerätegruppen

Die Nachrichten werden auf der obersten Ebene nach Gerätetyp gruppiert (z. B. inverse3, verse_grip, wireless_verse_grip). Jeder Gerätetyp-Schlüssel ist einem Array von gerätespezifischen Objekten zugeordnet.

Anfänglicher Bestand (Server → Client)

Wird unmittelbar nach dem Aufbau einer WebSocket-Verbindung einmal gesendet.

Jeder Eintrag enthält:

  • device_id
  • config
  • state
  • status

Siehe: Beispiel: Payload für den Anfangsbestand

Statusaktualisierung (Server → Client)

Wird einmal pro Client-Nachricht gesendet.

Jeder Eintrag enthält:

  • device_id
  • state
  • status

Siehe: Beispiel: Payload für Statusaktualisierung

Sitzungsbefehlshüllkurve (Client → Server)

Sitzungsbefehle sind Aktionen, die für die aktuelle Verbindung/Sitzung gelten und nicht gerätespezifisch sind.

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

Gerätebefehls-Envelope (Client → Server)

Gerätebefehle werden unter dem Gerätetyp-Schlüssel als Array gesendet, sodass Befehle in einer einzigen Nachricht an ein oder mehrere Geräte gesendet werden können. Jeder Geräteeintrag unterstützt sowohl eine configure Karte und ein commands Karte.

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

Sie können mehrere Einträge hinzufügen, um mehrere Geräte desselben Typs in einer einzigen Nachricht anzusteuern. Beachten Sie, dass commands ist ein Verzeichnis und kann mehrere Befehle für ein bestimmtes Gerät enthalten, jedoch nur einen pro Befehlstyp.

Die execute Feld

Jeder Befehl oder Konfigurations-Eintrag akzeptiert einen optionalen "execute" boolesch (Standard true). Einstellen "execute": false damit der Eintrag analysiert, aber nicht angewendet wird. Dies ist nützlich für reflektionsbasierte Serializer (z. B. Unity JsonUtility) die stets jedes Feld ausgeben.

"set_cursor_force": { "execute": false, "vector": { "x": 0.0, "y": 0.0, "z": 0.0 } }

Befehlsübersicht

Sitzung

Vollständigen Status erzwingen

Fordern Sie eine Momentaufnahme aller Gerätestatus und -konfigurationen an.

{
"session": {
"force_render_full_state": {}
}
}

Sitzung konfigurieren

Die persistente Konfiguration auf Sitzungsebene wird über session.configure. Dies entspricht der Geräteebene configure Kartenmuster.

Profil

Legt den Namen des Sitzungsprofils fest – dieser wird von Haply verwendet, um die Simulation zu erkennen und die gerätespezifischen Anpassungen pro App zu speichern.

{
"session": {
"configure": {
"profile": {
"name": "my_profile"
}
}
}
}
Basis (auf Sitzungsebene)

Legt die Basiszuordnung für die gesamte Sitzung fest. Die Basiszuordnung beschreibt, wie Haply in das Koordinatensystem Ihrer Anwendung umgewandelt werden; nachdem sie festgelegt wurde, werden alle Gerätestatus in dieser Basis zurückgegeben und alle Werte, die Sie senden, werden in dieser Basis interpretiert.

Die Permutationsfolge wird relativ zu Haply rechtshändigem / Z-nach-oben- Koordinatensystem ausgedrückt – eine Permutation von X, Y, Z, optional mit dem Präfix + oder -. Beispiele:

  • XYZ, ZYX, +Y-Z+X, X-ZY
  • YZX bedeutet, dass dein Y Hat Haply vielleicht recht, dein Z ist Haply Stürmer, dein X Ist Haply an der Reihe?

Beispiel für eine linkshändige Z-Achse (Unreal, X-YZ):

{
"session": {
"configure": {
"basis": { "permutation": "X-YZ" }
}
}
}
Migration von session.set_basis

Die Konvention für die Achsenbezeichnungen hat sich zwischen den älteren Versionen geändert session.set_basis und session.configure.basis. Eine Permutation, die unter dem alten Befehl funktionierte, kann unter dem neuen Befehl eine umgekehrte Transformation ergeben – kehren Sie die Vorzeichen der Achsen bei Bedarf um (z. B. X-ZY wird XZ-Y).

Alle Gerätebefehle

Prüfen (alle Geräte)

Verwenden Sie Abfragebefehle, um aktuelle Positions- und Orientierungsdaten abzurufen, ohne Kräfte oder andere Simulationsänderungen anzuwenden.

  • inverse3: probe_position
  • Verse-Griffe (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": {}
}
}
]
}

Transformation festlegen (alle Geräte)

Lege die Arbeitsbereichstransformation (vom Geräteraum in den Anwendungsraum) für ein Gerät fest.

Im Gegensatz zu den anderen commands Einträge, set_transform ist anhaltend — Der Dienst behält den zuletzt gesendeten Wert bei, bis Sie einen neuen senden. Sie müssen ihn nicht bei jedem Tick übermitteln, aber da er für die Kamera- und Szenennavigation gedacht ist, ist das Streamen mit Tick-Rate das erwartete Nutzungsmuster für kontinuierliche Bewegungen (z. B. eine Transformation pro Frame, während der Benutzer die Szene schwenkt).

{
"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-Griff / kabelloser Verse-Griff

Bei Verse-Grip- und Wireless-Verse-Grip-Geräten gilt nur die rotation Komponente der Transformation wirkt sich aus – der Griff gibt lediglich die Ausrichtung an, daher gibt es nichts für position oder scale zu skalieren oder zu verschieben. Das position und scale Felder werden (und im Snapshot wiedergegeben) ausschließlich aus Gründen der Schemakonsistenz mit Inverse3 akzeptiert; sie bleiben auf ihren Standardwerten (position = {0,0,0}, scale = {1,1,1}) ist gängige Praxis.

Inverse3 konfigurieren

Konfigurationseinträge werden in der configure map. Sie sind persistent – einmalig senden oder bei Änderung.

Voreinstellung

{
"inverse3": [
{
"device_id": "049D",
"configure": {
"preset": { "preset": "arm_front_centered" }
}
}
]
}

Mögliche Werte: device_defaults, arm_front, arm_front_centered, led_front, led_front_centered, custom.

Grundlage (auf Geräteebene)

{
"inverse3": [
{
"device_id": "049D",
"configure": {
"basis": { "permutation": "ZXY" }
}
}
]
}

Halterung

Legt die Transformationsfunktion für die Geräte-Einbindung fest. Beachten Sie, dass transform Verpackung innen mount.

{
"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 }
}
}
}
}
]
}
Asymmetrie des Mount-Schemas

Die Befehl Seite (Client zu Server) umschließt die Transformation: "mount": { "transform": { ... } }. Die Momentaufnahme Die Verbindung (vom Server zum Client) ist unidirektional: "mount": { "position": {...}, "rotation": {...}, "scale": {...} }. Dies ist beabsichtigt – Befehle verwenden einen einheitlichen Wrapper, während Snapshots die Transformation direkt serialisieren. Achten Sie darauf, die Form des Snapshots nicht in die Nutzdaten eines Befehls zu kopieren.

Dämpfung

Steuert die gleichmäßige und/oder gerichtete Dämpfung. Mindestens ein Feld muss ausgefüllt werden.

{
"inverse3": [
{
"device_id": "049D",
"configure": {
"damping": { "scalar": 0.5 }
}
}
]
}

Sie können auch die Richtungsdämpfung einstellen oder beides gleichzeitig:

"damping": { "scalar": 0.5, "vector": { "x": 0.0, "y": 1.0, "z": 0.0 } }

Krafttor

Legt den Dämpfungsfaktor des Force-Gates fest (0,0 bis 1,0).

{
"inverse3": [
{
"device_id": "049D",
"configure": {
"force_gate": { "gain": 0.3 }
}
}
]
}

Inverse3

Um Befehle an einen inverse3 Gerät, fügen Sie einen Eintrag mit einer entsprechenden device_id unter dem inverse3 Taste. Befehle gelten nur für einen Takt und müssen bei jedem Takt erneut gesendet werden, um aktiv zu bleiben.

Ein Gerät steuern:

{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_force": {
"vector": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}

Mehrere Geräte mit einer einzigen Nachricht steuern:

{
"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
}
}
}
}
]
}

Cursorposition einstellen

{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_position": {
"position": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}

Cursorkraft einstellen

{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_force": {
"vector": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}

Winkellage einstellen

{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_angular_position": {
"angles": {
"a0": 1.0,
"a1": 2.0,
"a2": 3.0
}
}
}
}
]
}

Drehmomente einstellen

{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_angular_torques": {
"torques": {
"a0": 1.0,
"a1": 2.0,
"a2": 3.0
}
}
}
}
]
}

Verse Grip konfigurieren

Verse Grip und Wireless Verse Grip-Geräte unterstützen dasselbe configure Schlüssel als Inverse3 preset, basisund mount.

Erweiterter Vers-Griff

Die set_extension_data Der Befehl ist Teil des erweiterten Protokolls für Verse-Griffe und wird bei Griffversionen verwendet, die das Kommunikationsprotokoll für Board-Erweiterungen implementieren.

Daten zur Griffverlängerung einstellen

Unterstützte Datenlängen:

  • Bis zu 20 Byte in Upstream-Richtung (Client → Gerät).
  • Bis zu 12 Byte in Abwärtsrichtung (Gerät → Client), die in der Statusaktualisierungsnachricht als state.extension_data.

Angaben zu den Daten:

  • Array-Länge: 20 Byte
  • Wertebereich: Jeder Wert liegt zwischen 0 und 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
]
}
}
}
]
}

Beispiele

Beispiel: Anfängliche Bestandsdaten

Der Dienst sendet eine Nachricht mit der vollständigen Geräteliste, sobald eine WebSocket-Verbindung hergestellt ist. Die erste Nachricht enthält Folgendes: JSON Format:

{
"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
}
}
]
}

Beispiel: Nutzdaten für Statusaktualisierung

Der Dienst sendet für jede empfangene Nachricht eine Statusaktualisierungsnachricht, die den Status aller Geräte enthält.

Wenn Sie den Status der Maschine erfahren möchten, müssen Sie ihr zuvor eine Nachricht senden (zum Beispiel einen Abtastbefehl oder einen Kraftwert, auch wenn die Werte Null sind). Dies ist besonders wichtig, wenn Sie Geräte als Eingangsquellen (z. B. zur Positionserfassung) verwenden, ohne Kräfte auszuüben.

Die Statusmeldung enthält Folgendes JSON Format:

{
"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
}
}
]
}