{"id":51037416,"url":"https://github.com/orthory/lowtech","last_synced_at":"2026-06-22T07:32:18.718Z","repository":{"id":365525776,"uuid":"1272499505","full_name":"orthory/lowtech","owner":"orthory","description":"Minimal, no-UI CLI for Logitech HID++ mice: device info, DPI, live button-press watch, and button remapping written to the mouse's onboard memory (persists with nothing running). macOS + Linux.","archived":false,"fork":false,"pushed_at":"2026-06-17T17:23:05.000Z","size":40,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-17T19:19:54.397Z","etag":null,"topics":["cli","hid","hidapi","hidpp","linux","logitech","macos","mouse","no-ui","onboard-profiles","solaar"],"latest_commit_sha":null,"homepage":"https://orthory.github.io/lowtech/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/orthory.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-17T17:05:12.000Z","updated_at":"2026-06-17T17:24:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/orthory/lowtech","commit_stats":null,"previous_names":["orthory/lowtech"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/orthory/lowtech","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orthory%2Flowtech","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orthory%2Flowtech/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orthory%2Flowtech/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orthory%2Flowtech/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/orthory","download_url":"https://codeload.github.com/orthory/lowtech/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orthory%2Flowtech/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34639705,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-22T02:00:06.391Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","hid","hidapi","hidpp","linux","logitech","macos","mouse","no-ui","onboard-profiles","solaar"],"created_at":"2026-06-22T07:32:16.880Z","updated_at":"2026-06-22T07:32:18.708Z","avatar_url":"https://github.com/orthory.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lowtech\n\nA minimal port of the useful core of [Solaar](https://github.com/pwr-Solaar/Solaar)\nfor Logitech HID++ devices — **no UI**, just the things you actually want from a\nterminal: see your devices, read DPI/battery, watch button presses, and\n**reassign mouse buttons**. Single self-contained binary, system libraries\nstatically linked.\n\nBuilt and verified against a **Logitech PRO X SUPERLIGHT 2** on a Logitech Bolt\nreceiver, on macOS (Apple Silicon). Also builds on **Linux** (hidraw backend) —\nsee [Build](#build).\n\n## Features\n\n- **Read everything** — device name, HID++ version, feature list, battery, DPI.\n- **Watch button presses** live in the terminal.\n- **Remap buttons into the mouse's onboard memory** — persists across reboots\n  and machines with **no driver or daemon running**.\n- **Map to any standard HID action**, not just a vendor app's presets: mouse\n  buttons, keyboard keys + modifiers, or consumer/media codes (incl. browser\n  Back/Forward).\n- **Safe writes** — CRC-validated, original backed up, read-back verified, and\n  reversible (`restore`).\n- **One small static binary**, no UI. macOS (IOKit) and Linux (hidraw).\n\n## A bit of lore\n\nLogitech G HUB just didn't work for me, so I figured I'd port\n[Solaar][solaar] — the excellent Linux tool for Logitech devices — into\nsomething I could actually run. Then it turned out Solaar didn't really support\nthe mouse I had at the time, the LIGHTSPEED \"2c\" (PRO X SUPERLIGHT 2): its\nonboard profiles use a format newer than Solaar handles, so it couldn't remap\nthe buttons. So this became its own thing — **deliberately minimal**, just the\nhandful of features I actually use, talking HID++ straight to the device.\n\nIt runs on **macOS and Linux**, but feature parity with Solaar or G HUB isn't the goal — it's built for setting up a minimal onboard mapping once and forgetting about it: the config lives on the mouse, so nothing has to keep running.\n\nHuge thanks to **Solaar**: the HID++ feature list, control IDs, and the initial\nhardware/button mappings used here were derived from its work.\n\n[solaar]: https://github.com/pwr-Solaar/Solaar\n\n## Why not just use Solaar?\n\nSolaar is a large Python + GTK application. On macOS:\n\n- `make install_udev` **can't work** — it `sudo cp`s rules into `/etc/udev/rules.d/`\n  and runs `udevadm`, but **udev is Linux-only**. Those rules only grant device\n  permissions on Linux; macOS uses a different model entirely (TCC).\n- It needs PyGObject/GTK, dbus, pyyaml, etc. installed via Homebrew.\n- For newer gaming mice it doesn't do what you want anyway: the PRO X\n  SUPERLIGHT 2 reports **onboard-profile format 7**, and Solaar only supports\n  formats ≤ 5 (it silently bails), so it **cannot remap this mouse's buttons**.\n\n`lowtech` talks HID++ directly over the receiver's vendor HID interface\n(usage page `0xFF00`), which needs **no special permission** on macOS.\n\n## What it does\n\n```\nlowtech list                      enumerate receivers and reachable devices\nlowtech show                      name, type, HID++ version, battery, DPI, feature list\nlowtech dpi [VALUE]               read DPI (or set it to VALUE — see note)\nlowtech buttons                   list the onboard-profile button assignments\nlowtech assign \u003cBUTTON\u003e \u003cTARGET\u003e  remap a button (writes the mouse's flash)\nlowtech restore                   restore the profile sector from the local backup\nlowtech watch [--raw]             print live mouse button presses\n```\n\nThe CLI uses [clap](https://crates.io/crates/clap): `lowtech --help`,\n`lowtech \u003ccmd\u003e --help`, and `lowtech --version` all work.\n\nUse `-s`/`--slot \u003cSLOT\u003e` (global) to pick a device — a receiver device number\n`1`..`6`, or `ff` for a directly-connected device. Omitted, it uses the first\nreachable device (e.g. `lowtech show`, `lowtech --slot 2 buttons`).\n\n`watch` reads the Logitech mouse's HID input report and prints button presses\n(move the mouse briefly so it can classify the motion bytes, then press buttons).\n`--raw` dumps the raw HID reports for debugging. Buttons decode with the standard\nHID order (bit0=Left, 1=Right, 2=Middle, 3=Back, 4=Forward, ...).\n\n### Examples\n\n```console\n$ lowtech list\nLogitech HID++ interfaces: 1\n  [receiver] Logitech receiver  (HID++ 1.0)\n  [slot 1] PRO X SUPERLIGHT 2c  (HID++ 4.2, 33 features)\n\n$ lowtech show\nDevice [slot 1]\n  Name:     PRO X SUPERLIGHT 2c\n  Type:     Mouse (3)\n  HID++:    4.2\n  Battery:  49% (discharging)\n  DPI:      800  (LOD=2)\n  Features: 33\n    ...\n\n$ lowtech buttons\nOnboard profile (sector 1, 5 buttons), CRC OK:\n  button 1: Left click   [80 01 00 01]\n  button 2: Right click  [80 01 00 02]\n  button 3: Middle click [80 01 00 04]\n  button 4: Back         [80 01 00 08]\n  button 5: Forward      [80 01 00 10]\n\n# remap the \"forward\" button to middle-click\n$ lowtech assign 5 middle\nbacked up original sector to ./lowtech-backup-sector1.bin\nbutton 5: Forward -\u003e Middle click\nverified: readback matches (Middle click)\n\n# put it back\n$ lowtech assign 5 forward\n```\n\n`assign` targets:\n\n- mouse buttons: `left right middle back forward button6 button7 button8`\n- DPI: `dpi-cycle dpi-up dpi-down dpi-default`\n- browser navigation: `ac-back` / `ac-forward` (HID consumer codes, OS-agnostic),\n  or `nav-back` / `nav-forward` (the macOS Cmd+[ / Cmd+] shortcut)\n- `disable`\n- advanced: `consumer:XXXX` (hex consumer code), `key:MM:KK` (hex modifier+keycode),\n  or a raw 8-hex-digit spec (e.g. `80010004`)\n\nBecause `lowtech` writes the raw HID++ button spec, you're **not limited to the\npresets a vendor app exposes**. G HUB only lets you pick from its own list;\nhere a button can be any standard HID action — any mouse button, any keyboard\nkey + modifiers (`key:MM:KK`), or any consumer/media usage (`consumer:XXXX`) —\nwritten straight into the onboard profile.\n\n### Optional: make the side buttons send browser Back/Forward\n\nBy default the side buttons send raw HID mouse buttons 4 and 5. Most browsers\ntreat those as Back/Forward, but if yours doesn't you can remap them in the\nonboard profile to send explicit navigation events — no driver needed:\n\n```console\n# button 4 = back side button, button 5 = forward side button\n$ lowtech assign 4 ac-back       # HID consumer \"AC Back\"  (macOS/Windows/Linux)\n$ lowtech assign 5 ac-forward    # HID consumer \"AC Forward\"\n# or, for the macOS browser keyboard shortcut instead:\n$ lowtech assign 4 nav-back      # Cmd+[\n$ lowtech assign 5 nav-forward   # Cmd+]\n```\n\n`ac-back`/`ac-forward` are the HID-standard, OS-agnostic codes for browser\nnavigation. The change is written to onboard memory and persists with no driver\nrunning; `lowtech restore` reverts it.\n\n## How button assignment works (and why it's safe)\n\nThis mouse has no `0x1B04` (Reprogrammable Controls). Buttons live in the\n**onboard profile** (`0x8100`), as 4-byte specs inside a 255-byte profile sector\nthat ends with a CRC-16/CCITT-FALSE checksum. In format 7 the button array sits\nat **offset 48** (format ≤5 used offset 32; that 16-byte shift is why Solaar's\nparser misreads this mouse).\n\n`assign` is conservative:\n\n1. Reads the active profile sector and **validates its CRC** — refuses to touch\n   it if the format isn't understood.\n2. Writes a **backup** of the original bytes to `./lowtech-backup-sector\u003cN\u003e.bin`.\n3. Patches only the 4 bytes of the target button, recomputes the CRC, writes the\n   sector (`startWrite` / `writeData` / `endWrite`).\n4. **Reads back** and verifies both the CRC and the changed button.\n\nIf anything looks wrong it tells you to run `lowtech restore`.\n\n\u003e Note: `dpi \u003cvalue\u003e` (the host-side `0x2202` write) is rejected while the mouse\n\u003e is in onboard mode (`0x05 logitech internal`) — on this mouse DPI is governed\n\u003e by the onboard profile, same as buttons.\n\n## Build\n\nSame command on either OS — the `hidapi` backend is selected per platform in\n`Cargo.toml` (`macos-shared-device` on macOS, `linux-static-hidraw` on Linux):\n\n```\ncargo build --release\n./target/release/lowtech list\n```\n\nOr drive it with the **Makefile**: `make build`, `make install` (via cargo), `make deps` (shows the dynamic-library deps), and on Linux `make install-udev` — the working counterpart to Solaar's broken `make install_udev`. `make help` lists all targets.\n\n### macOS\n\nThe bundled C hidapi is **statically compiled in** — no `libhidapi.dylib`\nruntime dependency. `otool -L` shows only Apple's own system frameworks\n(IOKit / CoreFoundation / AppKit) and libSystem, which **cannot** be statically\nlinked on macOS by design:\n\n```\n$ otool -L target/release/lowtech\n    /System/Library/Frameworks/IOKit.framework/...\n    /System/Library/Frameworks/CoreFoundation.framework/...\n    /System/Library/Frameworks/AppKit.framework/...\n    /usr/lib/libiconv.2.dylib\n    /usr/lib/libSystem.B.dylib\n```\n\n`watch` (reading the mouse input report) may require **Input Monitoring**\npermission: System Settings → Privacy \u0026 Security → Input Monitoring → enable\nyour terminal, then quit \u0026 reopen it. The HID++ commands don't need it.\n\n### Linux\n\nhidapi is compiled in statically using the **hidraw** backend (the same one\nSolaar uses). `libudev` remains a dynamic dependency for device enumeration:\n\n```\n$ ldd target/release/lowtech\n    libudev.so.1 =\u003e ...\n    libc.so.6 =\u003e ...\n```\n\nFor a fully self-contained binary, build against musl with a static\n`libudev`/`libusb` (e.g. `cargo build --release --target x86_64-unknown-linux-musl`).\n\n**Permissions:** by default `hidraw` nodes are root-only. Install the bundled\nudev rule so your user can talk to Logitech devices without `sudo` (this is the\npiece Solaar's broken `make install_udev` was trying to do):\n\n```\nsudo cp linux/42-logitech-hidpp.rules /etc/udev/rules.d/\nsudo udevadm control --reload-rules \u0026\u0026 sudo udevadm trigger\n```\n\nThen replug the receiver.\n\n## Layout\n\n- `src/hidpp.rs` — HID++ transport: framing, request/reply, ping, feature discovery.\n- `src/features.rs` — id→name tables and DPI (0x2202) read/set.\n- `src/onboard.rs` — onboard-profile (0x8100) read + safe button remap.\n- `src/main.rs` — CLI.\n- `tools/probe.py`, `tools/onboard_dump.py` — the read-only Python scripts used to\n  reverse-engineer the protocol and the format-7 sector layout against the real\n  device (run via the system `libhidapi`). Kept for reference.\n\n## Status / limitations\n\n- Tested on PRO X SUPERLIGHT 2c via a Bolt receiver, macOS arm64. The Linux\n  build (hidraw) shares all the code but has had less hands-on testing.\n- `watch` decodes the button bitfield from the mouse input report (byte 1). The\n  bit→name mapping follows the standard HID layout; press each button to confirm.\n- `dpi` set, persistent (`0x1C00`) remapping, profiles/macros, RGB, and the\n  generic `0x1B04` remap path are not implemented (not needed for this mouse).\n- Talking to paired devices through the receiver and to direct USB/BT devices is\n  supported; only single-receiver setups have been exercised.\n\n## Credits\n\nBuilt on the shoulders of [Solaar](https://github.com/pwr-Solaar/Solaar)\n(GPL-2.0-or-later). The HID++ feature ids, control ids, and onboard-profile /\nbutton-spec encodings used here were derived from Solaar's implementation — this\nproject would not exist without it. `lowtech` is an independent, deliberately\nminimal reimplementation; any mistakes are mine (I mean Claude's).\n\n## License\n\nGPL-2.0-or-later — see [LICENSE](LICENSE). The HID++ feature/control ids and onboard-profile / button-spec encodings are derived from [Solaar](https://github.com/pwr-Solaar/Solaar) (GPL-2.0-or-later), so lowtech carries the same license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Forthory%2Flowtech","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Forthory%2Flowtech","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Forthory%2Flowtech/lists"}