Haply Inverse SDK
Das Haply Inverse SDK ist eine sprachunabhängige WebSocket- und HTTP-Schnittstelle zu den Haply Geräten Haply – dem Inverse3, Inverse3x, Minverse, VerseGrip und Wireless VerseGrip. Es läuft als lokaler Dienst, der die Geräteerkennung, serielle Kommunikation, Sicherheitsüberwachung und Statusübertragung übernimmt – so muss Ihre Anwendung lediglich JSON über einen Socket kommunizieren.
Zu seinen Funktionen gehören:
- Geräteerkennung und -verwaltung – listet angeschlossene Haply automatisch über eine HTTP-REST-API auf und konfiguriert sie.
- Echtzeit-Statusübertragung – übermittelt den Gerätestatus mit haptischen Steuerungsraten (mehrere kHz) über WebSockets.
- Befehlsverarbeitung – führt Kraft- und Positionsbefehle mit hoher Genauigkeit aus, um ein präzises haptisches Feedback zu gewährleisten.
- Hintergrundbetrieb – läuft als lokaler Dienst und hält die Geräte ohne Benutzereingriff betriebsbereit.
Mit dem Haply installieren
Der einfachste Weg, um loszulegen, ist der Haply – eine Desktop-Anwendung zum Installieren, Ausführen, Konfigurieren, Testen und Überwachen Ihrer Haply . Sie hält die Firmware auf dem neuesten Stand, bündelt den Inverse Service und enthält Demos, damit Sie Ihre Hardware überprüfen können, bevor Sie auch nur eine Zeile Code schreiben.

Haply Hub
Laden Sie die neueste Version des Haply Hub herunter
Laden Sie den Hub herunter und installieren Sie ihn, schließen Sie Ihr Gerät an, und der Hub führt Sie durch alle Firmware-Updates. Nach der Installation läuft der Inverse Service automatisch im Hintergrund, sobald der Hub geöffnet ist.
Sie können eine bestimmte Version des Inverse Service auch ohne den Hub als Systemdienst (Windows) oder Daemon (Linux/macOS) installieren. Den Link zum Installationsprogramm sowie eine Anleitung finden Sie unter „Ausführen des Dienstes “.
Kurzes Beispiel
Stelle eine Verbindung zum Dienst her, lies die Cursorposition des ersten Inverse3 aus und sende ein Keepalive ohne Kraft, damit der Dienst weiterhin Status-Frames überträgt:
- Python
- JavaScript (Node)
- C++ (nlohmann)
- C++ (Glaze)
- Rost
import asyncio, json, websockets
async def main():
async with websockets.connect("ws://localhost:10001") as ws:
# Handshake: register profile and send a zero-force keepalive
first_state = json.loads(await ws.recv())
device_id = first_state["inverse3"][0]["device_id"]
keepalive = {"inverse3": [{
"device_id": device_id,
"commands": {"set_cursor_force": {"vector": {"x": 0, "y": 0, "z": 0}}}
}]}
while True:
state = json.loads(await ws.recv())
pos = state["inverse3"][0]["state"]["cursor_position"]
print(f"pos: {pos}")
await ws.send(json.dumps(keepalive))
asyncio.run(main())
import WebSocket from 'ws'
const ws = new WebSocket('ws://localhost:10001')
let keepalive
ws.on('message', (msg) => {
const state = JSON.parse(msg)
if (!keepalive) {
const deviceId = state.inverse3[0].device_id
keepalive = JSON.stringify({
inverse3: [
{
device_id: deviceId,
commands: { set_cursor_force: { vector: { x: 0, y: 0, z: 0 } } },
},
],
})
}
console.log('pos:', state.inverse3[0].state.cursor_position)
ws.send(keepalive)
})
#include <external/libhv.h>
#include <nlohmann/json.hpp>
int main() {
hv::WebSocketClient ws;
nlohmann::json keepalive;
ws.onmessage = [&](const std::string& msg) {
auto state = nlohmann::json::parse(msg);
if (keepalive.is_null()) {
keepalive = {{"inverse3", nlohmann::json::array({{
{"device_id", state["inverse3"][0]["device_id"]},
{"commands", {{"set_cursor_force",
{{"vector", {{"x", 0}, {"y", 0}, {"z", 0}}}}}}}
}})}};
}
auto pos = state["inverse3"][0]["state"]["cursor_position"];
std::cout << "pos: " << pos << "\n";
ws.send(keepalive.dump());
};
ws.open("ws://localhost:10001");
std::cin.get();
}
Verwendung von Glaze für JSON-Reflexion zur Kompilierungszeit. Deklarieren Sie die Mindestform, die Sie lesen und schreiben; der Rest wird ignoriert.
#include <external/libhv.h>
#include <glaze/glaze.hpp>
struct vec3 { float x{}, y{}, z{}; };
struct inverse_state { vec3 cursor_position; };
struct inverse_device { std::string device_id; inverse_state state; };
struct devices_message { std::vector<inverse_device> inverse3; };
struct set_cursor_force_cmd { vec3 vector; };
struct device_cmd { std::string device_id;
struct { std::optional<set_cursor_force_cmd> set_cursor_force; } commands; };
struct commands_message { std::vector<device_cmd> inverse3; };
int main() {
hv::WebSocketClient ws;
commands_message keepalive;
ws.onmessage = [&](const std::string& msg) {
devices_message state{};
if (glz::read_json(state, msg)) return;
if (keepalive.inverse3.empty()) {
keepalive.inverse3.push_back({state.inverse3[0].device_id});
keepalive.inverse3[0].commands.set_cursor_force = set_cursor_force_cmd{};
}
printf("pos: %f %f %f\n", state.inverse3[0].state.cursor_position.x,
state.inverse3[0].state.cursor_position.y,
state.inverse3[0].state.cursor_position.z);
std::string out; (void)glz::write_json(keepalive, out);
ws.send(out);
};
ws.open("ws://localhost:10001");
std::cin.get();
}
Anschauliches Beispiel unter Verwendung von tokio-tungstenite + serde_json.
use futures_util::{SinkExt, StreamExt};
use serde_json::json;
use tokio_tungstenite::{connect_async, tungstenite::Message};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let (mut ws, _) = connect_async("ws://localhost:10001").await?;
let mut keepalive: Option<String> = None;
while let Some(Ok(Message::Text(msg))) = ws.next().await {
let state: serde_json::Value = serde_json::from_str(&msg)?;
let device_id = state["inverse3"][0]["device_id"].as_str().unwrap();
if keepalive.is_none() {
keepalive = Some(json!({
"inverse3": [{
"device_id": device_id,
"commands": { "set_cursor_force": { "vector": { "x": 0, "y": 0, "z": 0 } } }
}]
}).to_string());
}
println!("pos: {}", state["inverse3"][0]["state"]["cursor_position"]);
ws.send(Message::Text(keepalive.clone().unwrap())).await?;
}
Ok(())
}
Ändern Sie die Kraftwerte mit Bedacht. Plötzliche hohe Kraftwerte können das Gerät beschädigen oder zu unerwartetem Verhalten führen.
Informationen zu den Regeln für den Nachrichten-Envelope, die Ports und den Content-Type finden Sie auf der Seite „JSON-Konventionen “.
Weitere Beispiele
Umfassende Tutorials zu Python, C++ (nlohmann) und C++ (Glaze) – mit Themen wie Force Feedback, Positionssteuerung, Konfigurationen mit mehreren Geräten, Halterungs- und Basiskonfiguration sowie Event-Streaming – finden Sie auf der Seite „Tutorials “.