Skip to main content
Version: 3.0.5

Haply Inverse Service

The Haply Inverse Service is a foundational component that integrates Haply's tactile devices with interactive applications, running as a background Windows service to enable real-time interactions. Its functionalities include:

  • Device Discovery and Management: Identifies and configures connected Haply devices automatically.
  • Ownership Management: Manages access to devices across multiple applications, ensuring smooth operation.
  • Real-Time State Streaming: Provides updates on device states at high frequencies for precise control.
  • Command Processing: Executes commands for force or position with high fidelity, enhancing the haptic feedback.
  • Background Operation: Runs in the background, maintaining device readiness without user intervention.

Run, stop, restart

On Windows:

  • Open the Services desktop app: Press Windows+R to open the Run box, enter services.msc, and then press Enter or select OK.

On Ubuntu:

  • Start
    • systemctl start haply-inverse-service.service
  • Stop
    • systemctl stop haply-inverse-service.service
  • Enable
    • systemctl enable haply-inverse-service.service
  • Disable
    • systemctl disable haply-inverse-service.service

Important files

On Windows:

  • The configuration files are located in: %PROGRAMDATA%\Haply\Inverse\*-config.json
  • The logs files are located in: %PROGRAMDATA%\Haply\Inverse\*-log.log

On Ubuntu:

  • The configuration files are located in: /etc/haply-inverse-service/*-config.json
  • The logs files are located in: /var/log/haply-inverse-service/*-log.log

Interfaces

Both the HTTP and Websocket interfaces use JSON formatted payloads.

HTTP

By default the HTTP service starts on: http://localhost:10000. Note: the port can be changed in the configuration.

Dashboard

The dashboard is available at: http://localhost:10000

Version

Method: GET

URL: http://localhost:10000/3.0/version

Response example:

{
"build_time": "2024-08-07T16:01:53Z",
"git_branch": "main",
"git_describe": "3.0.0-2-gce34c39e",
"git_hash": "ce34c39e",
"git_tag": "3.0.0",
"project_name": "haply-inverse-service",
"project_version": "3.0.0.0"
}

Devices

Method: GET

URL: http://localhost:10000/3.0/devices

Response example:

{
"inverse3": {
"049D": {
"config": {
"device_info": {
"firmware_version": 1,
"hardware_version": 7,
"id": "049D",
"model": 4,
"uuid": "5A9F3AC02E085C078AD6A4A113DE049D"
},
"extended_device_id": "5A9F3AC02E085C078AD6A4A113DE049D",
"extended_firmware_version": "77488E4644CB6057D0920B0B919C8B82",
"gravity_compensation": {
"enabled": true,
"scaling_factor": 0.75
},
"handedness": "left",
"torque_scaling": {
"enabled": true
}
},
"device_id": "049D",
"state": {
"angular_position": {
"a0": 0.0,
"a1": 0.0,
"a2": 0.0
},
"angular_velocity": {
"a0": 0.0,
"a1": 0.0,
"a2": 0.0
},
"body_orientation": {
"w": 1.0,
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"calibrated": false,
"cursor_position": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"cursor_velocity": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"in_use": false,
"mode": "idle",
"power_supply": false,
"ready": false,
"started": false
},
"status": {
"initialization_received": {
"body_orientation": true,
"device_handedness": true,
"device_info": true,
"extended_device_id": true,
"extended_firmware_version": true,
"gravity_compensation": true,
"torque_scaling": true
},
"port": "COM12"
}
}
},
"verse_grip": {
"61582": {
"button": false,
"device_id": "61582",
"error": 0,
"hall": 0,
"orientation": {
"w": 0.17022705078125,
"x": -0.24114990234375,
"y": -0.95538330078125,
"z": -0.001220703125
}
}
},
"wireless_verse_grip": {
"0": {
"battery_level": 0.6200001239776611,
"buttons": {
"a": false,
"b": false,
"c": false
},
"device_id": "0",
"hall": 15,
"orientation": {
"w": -0.906463623046875,
"x": -0.379058837890625,
"y": -0.111785888671875,
"z": -0.136688232421875
}
}
}
}

Force Scale

Method: POST

URL: http://localhost:10000/3.0/force_scale

Body example:

{
"force_scale": 0.5
}

Response example:

{
"ok": true
}

Gravity Compensation

Method: POST

URL: http://localhost:10000/3.0/gravity_compensation

Body example:

{
"device_id": "049D",
"enable": true,
"gravity_scaling_factor": 0.8
}

Response example:

{
"ok": true
}

Torque scaling

Method: POST

URL: http://localhost:10000/3.0/torque_scaling

Body example:

{
"device_id": "049D",
"enable": true
}

Response example:

{
"ok": true
}

Device Handedness

Method: POST

URL: http://localhost:10000/3.0/device_handedness

Body example:

{
"device_id": "049D",
"handedness": "right"
}

Response example:

{
"ok": true
}

Save Configuration

Only works with Inverse3 devices. Warning: Avoid saving the configuration too often, since there is a limited number of save that can be applied to any given device.

Method: POST

URL: http://localhost:10000/3.0/save_configuration

Body example:

{
"device_id": "049D"
}

Response example:

{
"ok": true
}

Websocket

The default websocket URL is ws://localhost:10000. Note: the port can be changed in the configuration.

Initial Message

The service sends a message containing the complete device list when a websocket is connected. The initial message has the following JSON format:

{
"inverse3": {
"049D": {
"config": {
"device_info": {
"firmware_version": 1,
"hardware_version": 7,
"id": "049D",
"model": 4,
"uuid": "5A9F3AC02E085C078AD6A4A113DE049D"
},
"extended_device_id": "5A9F3AC02E085C078AD6A4A113DE049D",
"extended_firmware_version": "77488E4644CB6057D0920B0B919C8B82",
"gravity_compensation": {
"enabled": true,
"scaling_factor": 0.75
},
"handedness": "left",
"torque_scaling": {
"enabled": true
}
},
"device_id": "049D",
"state": {
"angular_position": {
"a0": -90.40308380126953,
"a1": 3.390819549560547,
"a2": 0.8826223015785217
},
"angular_velocity": {
"a0": 0,
"a1": 0,
"a2": 0
},
"body_orientation": {
"w": 1,
"x": 0,
"y": 0,
"z": 0
},
"calibrated": false,
"cursor_position": {
"x": -0.022379431873559952,
"y": -0.011212021112442017,
"z": -0.11827600002288818
},
"cursor_velocity": {
"x": 0,
"y": 0,
"z": 0
},
"in_use": false,
"mode": "angle",
"power_supply": true,
"ready": true,
"started": true
},
"status": {
"initialization_received": {
"body_orientation": true,
"device_handedness": true,
"device_info": true,
"extended_device_id": true,
"extended_firmware_version": true,
"gravity_compensation": true,
"torque_scaling": true
},
"port": "COM12"
}
}
},
"verse_grip": {
"61582": {
"button": false,
"device_id": "61582",
"error": 0,
"hall": 0,
"orientation": {
"w": 0.156982421875,
"x": -0.24163818359375,
"y": -0.95751953125,
"z": -0.00079345703125
}
}
},
"wireless_verse_grip": {
"0": {
"battery_level": 0.5999999046325684,
"buttons": {
"a": false,
"b": false,
"c": false
},
"device_id": "0",
"hall": 15,
"orientation": {
"w": -0.91265869140625,
"x": -0.389984130859375,
"y": -0.075103759765625,
"z": -0.07635498046875
}
}
}
}

State Update Message

Whenever any of the available devices has a change, the service will send a state update message containing the state of all devices. The state update message has the following JSON format:

{
"inverse3": {
"049D": {
"state": {
"angular_position": {
"a0": 0,
"a1": 0,
"a2": 0
},
"angular_velocity": {
"a0": 0,
"a1": 0,
"a2": 0
},
"body_orientation": {
"w": 1,
"x": 0,
"y": 0,
"z": 0
},
"calibrated": true,
"cursor_position": {
"x": -0.022203030064702034,
"y": -0.027979673817753792,
"z": -0.1170499324798584
},
"cursor_velocity": {
"x": 0,
"y": 0,
"z": 0
},
"in_use": true,
"mode": "position",
"power_supply": false,
"ready": true,
"started": true
}
}
},
"verse_grip": {
"61582": {
"button": false,
"device_id": "61582",
"error": 0,
"hall": 0,
"orientation": {
"w": 0.15423583984375,
"x": -0.24176025390625,
"y": -0.95794677734375,
"z": -0.0015869140625
}
}
},
"wireless_verse_grip": {
"0": {
"battery_level": 0.5999999046325684,
"buttons": {
"a": false,
"b": false,
"c": false
},
"device_id": "0",
"hall": 15,
"orientation": {
"w": -0.3958740234375,
"x": 0.639190673828125,
"y": -0.404388427734375,
"z": -0.51739501953125
}
}
}
}

Command Message

In order to send commands to the inverse3 the client has to send a command message. Here is an example:

{
"inverse3": {
"049D": [
{
"command": "set_cursor_force",
"values": {
"x": 0,
"y": 0,
"z": 0
}
}
]
}
}

Also, it is possible to send commands to multiple devices in a single message. here is an example:

{
"inverse3": {
"049D": [
{
"command": "set_cursor_force",
"values": {
"x": 0,
"y": 0,
"z": 0
}
}
],
"049E": [
{
"command": "set_cursor_force",
"values": {
"x": 0,
"y": 0,
"z": 0
}
}
]
}
}

The following commands are currently available on the inverse3:

Set Cursor Position
{
"command": "set_cursor_position",
"values": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
Set Cursor Force
{
"command": "set_cursor_force",
"values": {
"x": 0.5,
"y": 0.5,
"z": 0.5
}
}
Set Angular Position
{
"command": "set_angular_position",
"values": {
"a0": 1.0,
"a1": 2.0,
"a2": 3.0
}
}
Set Angular Torque
{
"command": "set_angular_torque",
"values": {
"a0": 0.5,
"a1": 0.5,
"a2": 0.5
}
}