HHD now has a simple and fully featured HTTP endpoint, which allows configuring
all available settings.
All endpoints below should be prefixed with /api/v1/
.
By default, the endpoint is restricted to localhost, and is only available through
the use of a token.
This token is automatically generated in ~/.config/hhd/token
and can be changed
afterwards by the user as well.
The authentication is achieved through HTTP basic auth with a bearer token.
This means that all requests to /api/v1
require the header Authorization
with content Bearer <token>
.
To retrieve the user, you can either ask the user for it (they can retrieve it
with hhd token
), or read it from ~/.config/hhd/token
with either superuser
or that user's permissions.
The API is on purpose very simple.
The settings endpoint settings
returns the currently available settings as a
JSON.
All the available settings types can be found in src/hhd/plugins.py
.
HHD ensures the json will have all the listed values in plugins.py, so you
may not check if they exist.
Each setting has a title which is meant to be shown in the UI and an optional
hint meant to be shown under a hover hint or ?
button.
Each setting may include a set of tags, that work like classes.
For example, a keyboard mapping setting may have the tags
[razer_lycosa_123, razer_kbd, keyboard, advanced]
, which would allow the UI
to customize the presentation based on the specific device make, manufacturer,
or if neither are supported, show a generic keyboard remapper.
Tags are ordered by specificity, so razer_lycosa_123
overrides razer_kbd
.
The tag advanced
can be used as a hint to hide the setting in simplified UIs (TBD).
Essentially, under the type Settings
are all the available settings, which
are self explanatory.
event
: Meant to simulate a one off event, like a reset. Set to true and hhd will remove it once it's applied (unused).bool
: Checkbox settingmultiple
: Radial/dropdown setting. Options is a dictionary of values to UI friendly titles.discrete
: Allows a number of fixed integer or floating values (options is listed in increasing order). You may handle the same as multiple.float
: Floating point setting, with optional min, max valuesint
: Integer setting, same as abovecolor
: Broken and unused right now
Each setting can be set to a single value coherent for its type (except color, tbd).
Settings are grouped within containers with a type container
, which has
an ordered dictionary of children.
The key of the dictionary is the id that will be used for the option.
Containers can be nested within containers, and the id of each container is
appended to the option name.
HHD features settings sections, which are the outermost layer. This allows you to only focus at the settings necessary for each UI component (TDP, controllers, hhd settings).
Here is an example that you will receive in json form from /settings
(in yaml):
version: <hex>
hhd:
http:
type: container
tags: [hhd-http]
title: REST API Configuration (BETA)
hint: >-
Settings for configuring the http endpoint of HHD.
children:
enable:
type: bool
title: Enable REST API.
hint: >-
Enables the rest API of Handheld Daemon
default: False
port:
type: int
title: REST API Port
hint: >-
Which port should the REST API be on?
min: 1024
max: 49151
default: 5335
The example above will result in the following default state:
hhd:
http:
enable: False
port: 5335
Settings can be viewed both as nested dictionaries and as a single dictionary. The following states are identical according to HHD.
hhd.http:
enable: False
port: 5335
hhd.http.enable: False
hhd.http.port: 5335
hhd:
http:
enable: False
hhd.http.port: 5335
You also receive a version hex, which contains whether the settings have changed and would prompt you to redraw your UI. Currently, HHD settings do not change after service start, but future plugins that rely on autodetection may start when e.g., a controller is connected. This will make the settings change.
HHD always performs validation for the currently loaded settings, so using a stale state will not create problems.
The final setting type is mode
, which is a special type of container.
It is meant to be displayed as an accordion, with a specific sub-container
shown at a time.
controllers.legion_go:
type: container
tags: [lgc]
title: Legion Controllers Configuration
# ...
children:
xinput:
type: mode
# ...
default: ds5e
modes:
disabled:
type: container
# ...
children:
shortcuts:
# ...
ds5e:
type: container
# ...
children:
led_support:
# ...
The above will create the following default state:
controllers.legion_go.xinput.mode: ds5e
controllers.legion_go.xinput.disabled.shortcuts: disabled
controllers.legion_go.xinput.ds5e.led_support: True
Example call (token disabled):
curl -i http://localhost:5335/api/v1/settings
HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.11.6
Date: ...
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
WWW-Authenticate: Bearer
{"controllers": {"legion_go": ...
The state endpoint with GET
returns the current app state in JSON form.
Currently, only the nested dictionary form is returned, e.g.:
hhd:
http:
enable: False
port: 5335
However, a future option will allow returning a single dictionary:
hhd.http.enable: False
hhd.http.port: 5335
You can also POST
to the same endpoint with a mixed state presentation, which
may include some options inlined and some as nested dictionaries.
You only need to send changed options and HHD will merge them to the current
state internally.
The POST
endpoint will lock, apply the settings under HHD
, and will return
the updated state.
Warning: the post endpoint may lock for up to 5+ seconds. Use a separate fetch thread/promise! Typically, it will be much less than 1 second.
Example call (token disabled):
curl -i http://localhost:5335/api/v1/state
HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.11.6
Date: ...
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
WWW-Authenticate: Bearer
{"controllers": {"legion_go": {"xinput": {"mode": "ds5e", "disabled": {"shortcuts": true}, "ds5e": {"led_support": true}}, "gyro": true, "accel": true, "gyro_fix": 100, "swap_legion": "disabled", "share_to_qam": true, "touchpad_mode": "crop_end", "debug": false, "shortcuts": true}}, "hhd": {"http": {"enable": true, "port": 5335, "localhost": true, "token": false}}, "version": "af6eb199"}%
HHD contains a profile system for changing multiple settings at a time. This can be done per game, when switching windows, etc.
The profile
endpoint has 4 sub-endpoints: list
, apply
, get
, set
, del
.
Only characters and spaces are supported for the profile name. HHD will silently strip other characters from the name.
The list
GET
endpoint returns a list of the available profiles.
curl -i http://localhost:5335/api/v1/profile/list
HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.11.6
Date: ...
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
WWW-Authenticate: Bearer
["test"]%
The apply
GET
endpoint applies the selected profiles in the specified order
and returns the new HHD state.
The applied profiles are supplied as query arguments.
You may apply multiple profiles at a time, by nesting them as query parameters.
curl -i http://localhost:5335/api/v1/profile/apply\?profile\=\&profile\=test2
HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.11.6
Date: ...
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
WWW-Authenticate: Bearer
{"controllers": {"legion_go": {"xinput": {"mode": "ds5e", "disabled": {"shortcuts": true}, "ds5e": {"led_support": true}}, "gyro": true, "accel": true, "gyro_fix": 100, "swap_legion": "disabled", "share_to_qam": true, "touchpad_mode": "crop_end", "debug": false, "shortcuts": true}}, "hhd": {"http": {"enable": true, "port": 5335, "localhost": true, "token": false}}, "version": "af6eb199"}%
The set endpoint allows you to update the contents of a profile.
The response contains the updated profile.
The set
endpoint replaces the whole profile and validates it, unlike the state
endpoint which merges it to the current state.
curl -i -X POST -d '{"controllers.legion_go.shortcuts": false}' http://localhost:5335/api/v1/profile/set\?profile\=test
HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.11.6
Date: ...
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
WWW-Authenticate: Bearer
{"controllers": {"legion_go": {"shortcuts": false}}, "version": "af6eb199"}%
The get
GET
endpoint allows you to retrieve the contents of a profile.
curl -i http://localhost:5335/api/v1/profile/get\?profile\=test
HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.11.6
Date: ...
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
WWW-Authenticate: Bearer
{"controllers": {"legion_go": {"shortcuts": false}}, "version": "af6eb199"}%
The del
GET
endpoint deletes the provided profile.
# Profile exists
curl -i http://localhost:5335/api/v1/profile/del\?profile\=test
HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.11.6
Date: ...
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
WWW-Authenticate: Bearer
# Profile does not exist
curl -i http://localhost:5335/api/v1/profile/del\?profile\=test
HTTP/1.0 400 Bad Request
Server: BaseHTTP/0.6 Python/3.11.6
Date: ...
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
WWW-Authenticate: Bearer
Content-type: text / plain
Handheld Daemon Error:
Profile 'test' not found.%
The v1
API will always return a JSON
object with status code 200 if called properly.
When called improperly, it will return the following status codes:
- 401: Unauthorized: your token is invalid.
- 404: The endpoint you tried to access does not exist.
- 400: You supplied invalid parameters.
The content of the response will be a human readable explanation in text form. You may choose to display that to the user, through a modal or portal.
You can query the version of the HHD V1 API to determine which features are available and whether the user should update either your app or HHD. The version is 1 now and this endpoint requires authentication. It might not require authentication in the future.
curl -i http://localhost:5335/api/v1/version
HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.11.6
Date: ...
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
WWW-Authenticate: Bearer
{"version": 1}%