QMK HID Host
June 29, 2026 · View on GitHub
Host component for communicating with QMK keyboards using Raw HID feature.
Requires support on keyboard side, currently is supported by stront.
Architecture
Application is written in Rust which gives easy access to HID libraries, low-level Windows/Linux APIs and cross-platform compatibility.
Supported platforms/providers
| Windows | Linux | MacOS | |
|---|---|---|---|
| Time | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Volume | :heavy_check_mark: | :heavy_check_mark: (PulseAudio) | :heavy_check_mark: |
| Input layout | :heavy_check_mark: | :heavy_check_mark: (X11) | :heavy_check_mark: |
| Media info | :heavy_check_mark: | :heavy_check_mark: (MPRIS/D-Bus)1 | :heavy_check_mark: (Spotify) |
| Relay | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Weather | :heavy_check_mark: | :heavy_check_mark: |
MacOS is partially supported, as I don't own any Apple devices, feel free to raise PRs.
Relay mode (device-to-device communication) - experimental
This allows for communication between two or more devices. qmk-hid-host only receives information from any device and broadcasts it to all devices. The actual sending and receiving should be configured in devices' firmware, but you have to set first byte in the data array - 0xCC for sending and 0xCD for receiving.
Example for syncing layers between two devices:
Data type enum (common between qmk-hid-host and all devices)
typedef enum {
_TIME = 0xAA, // random value that does not conflict with VIA, must match companion app
_VOLUME,
_LAYOUT,
_MEDIA_ARTIST = 0xAD,
_MEDIA_TITLE = 0xAE,
_WEATHER = 0xAF,
_MEDIA_PLAYER_LINUX = 0xB0,
_MEDIA_EXTENDED = 0xB1,
_RELAY_FROM_DEVICE = 0xCC,
_RELAY_TO_DEVICE,
} hid_data_type;
Source device
typedef enum {
_LAYER = 0,
} relay_data_type;
layer_state_t layer_state_set_user(layer_state_t state) {
uint8_t data[32];
memset(data, 0, 32);
data[0] = _RELAY_FROM_DEVICE;
data[1] = _LAYER;
data[2] = get_highest_layer(state);
raw_hid_send(data, 32);
return state;
}
Destination device
typedef enum {
_LAYER = 0,
} relay_data_type;
void raw_hid_receive_kb(uint8_t *data, uint8_t length) {
if (data[0] == _RELAY_TO_DEVICE) {
switch (data[1]) {
case _LAYER:
layer_move(data[2]);
break;
}
}
}
How to run it
All files are available in latest release.
Configuration
Default configuration is set to stront. For other keyboards you need to modify the configuration file (qmk-hid-host.json).
devicessection contains a list of keyboardsproductId-pidfrom your keyboard'sinfo.json. You can get it by runningqmk-hid-host -pname- keyboard's name (optional, visible only in logs)usageandusagePage- optional, override only ifRAW_USAGE_IDandRAW_USAGE_PAGEwere redefined in firmware
layouts- list of supported keyboard layouts in two-letter format (app sends layout's index, not name)reconnectDelay- delay between reconnecting attempts in milliseconds (optional, default is 5000)weather- optional weather provider config for Linux and MacOS. The URL should return a temperature value, for examplewttr.in/Hamburg?format=%t
Minimal config
{
"devices": [
{
"productId": "0x0844"
}
],
"layouts": ["en"],
"weather": {
"url": "wttr.in/Hamburg?format=%t"
}
}
Configuration is read from file qmk-hid-host.json in the current working directory. If it is not found, then the default configuration is written to this file.
You can specify a different location for the configuration file by using --config (-c) command line option. For example:
qmk-hid-host -c $HOME/.config/qmk-hid-host/config.json
Windows
Manual/Debug mode
- Start
qmk-hid-host.exe - If needed, edit config and restart the app
Silent mode
When you verified that the application works with your keyboard, you can use qmk-hid-host.silent.exe instead (like add it to Startup). It does not have a console or logs, and can be killed only from Task Manager.
Linux
-
Update
udevrules by running script (remember to updateidVendorandidProductto your values first):sudo sh -c 'echo "KERNEL==\"hidraw*\", SUBSYSTEM==\"hidraw\", ATTRS{idVendor}==\"feed\", ATTRS{idProduct}==\"0844\", MODE=\"0666\"" > /etc/udev/rules.d/99-qmkhidhost.rules' -
Install runtime dependencies for the enabled providers:
sudo pacman -S curlLinux media info uses D-Bus/MPRIS, so the active player must expose MPRIS metadata. Spotify works out of the box. Other players, like mpv, depend on how they publish metadata.
The Linux media provider sends
_MEDIA_ARTIST(0xAD),_MEDIA_TITLE(0xAE), and_MEDIA_PLAYER_LINUX(0xB0, 8 bytes space-padded compact text) on title changes. When"extended_media": trueis set in config, it also sends_MEDIA_EXTENDED(0xB1) on play/pause/track-change events, containing: 2-byte total time (seconds, u16 LE), 2-byte current position (seconds, u16 LE), 1-byte playback status (0=stopped, 1=playing, 2=paused), 1-byte artist name length, and the artist name (up to 21 bytes).Weather uses
curland the configuredweather.url; the default expects a wttr.in response like+29°C. -
Reconnect keyboard
-
Start
qmk-hid-host, add it to autorun if needed
MacOS
Note
To configure the weather, you need to replace your Hamburg with your city (example: Cairo); here is its respective repository to see more configurations: chubin/wttr.in - ⛅ The right way to check the weather
-
Download
qmk-hid-host -
Modify
qmk-hid-host.json -
Add your layouts or your local weather, for example:
"layouts": ["ABC", "Russian"], "weather": { "url": "wttr.in/Hamburg?format=%t"if you don't know what layout are installed in you system, run qmk-hid-host with the layouts listed above, change lang and look at terminal output:
INFO qmk_hid_host::providers::layout::macos: new layout: 'ABC', layout list: ["ABC", "Russian"] INFO qmk_hid_host::providers::layout::macos: new layout: 'Russian', layout list: ["ABC", "Russian"]"new layout:" is what you need
-
start
qmk-hid-hostfrom directory where yourqmk-hid-host.jsonis locatedNote: macOS, by default, may not locate your configuration file correctly. It's recommended to start
qmk-hid-hostwith the configuration file path explicitly specified, for example:./qmk-hid-host -c ~/Downloads/macos/qmk-hid-host.json -
If you
qmk-hid-hoststuck atWaiting for keyboard...there are two common mistakes:- You're wrong with productId in your config. Check
qmk-hid-host -p - Close Vial app and try again
- You're wrong with productId in your config. Check
Development
Nix
nix develop
Native
- Install Rust
- Run
cargo run - If needed, edit
qmk-hid-host.jsonin root folder and run again
Changelog
- 2026-06-19 - add Linux weather, compact media player, and extended media HID support
- 2025-11-11 - add support for weather and spotify with MacOS
- 2024-10-03 - add support for multiple devices, restructure config
- 2024-09-15 - add MacOS support
- 2024-02-06 - add Linux support
- 2024-01-21 - remove run as windows service, add silent version instead
- 2024-01-02 - support RUST_LOG, run as windows service
- 2023-07-30 - rewritten to Rust
Footnotes
-
Tested with Spotify on Linux, where media metadata is reported correctly. ↩