Tooling to build an offline copy of logiwebconnect.com, Logitech's browser-based WebHID tool for pairing and updating Unifying/Bolt receivers. Useful if the site ever goes away, and it patches the app to run on Linux (the live site blocks Linux).
Scripts only. No Logitech code, firmware, fonts or images are stored here. The scripts download those from the live site to your own machine. See Legal.
Requirements
- Node.js and Python 3.
- A Chromium-family browser to use the mirror: Chrome, Chromium or Edge (not Firefox/Safari, no
WebHID). On Linux use the Google Chrome
.deb, not snap/flatpak (sandbox can block HID). - A Unifying or Bolt receiver to pair against.
Build and run
node capture/fetch-precache.js # download the app + full asset set (from the SW PRECACHE_URLS manifest) node capture/rewrite.js # rewrite URLs, stub telemetry, disable the OS allowlist (Linux) ./serve.sh # serve ./mirror at http://localhost:8765
Open http://localhost:8765/ in Chrome and pair. No clicking through the live site is needed (and
it's impossible on Linux anyway), so fetch-precache.js reads the service worker's asset manifest
and pulls everything directly.
capture.js is an optional Playwright capture for supported OSes (Windows/macOS/ChromeOS), for
assets outside the manifest. It needs npm install && npx playwright install chromium.
What rewrite.js does
- Cross-origin URLs become local
/_ext/...paths. - Telemetry (
datapipeline.logitech.io) becomes/_noop/...;serve.pyanswers those POSTs with 204. - The OS allowlist is disabled. The app blocks Linux even in Chrome; the patch removes the OS check
and keeps the real one (
"hid" in navigator && "forget" in HIDDevice.prototype), so Firefox and Safari still report unsupported. It's a name-agnostic regex, so it survives re-minification.
serve.py
Static server with SPA history-fallback, so the app's client-side routes (/devices,
/select-receiver) don't 404 on a hard navigation. Plain python -m http.server does.
Linux: HID permissions
WebHID can list the receiver but can't open its /dev/hidraw* node without permission
(NotAllowedError: Failed to open the device). Grant access for this session:
for h in /sys/class/hidraw/hidraw*; do grep -ql 046D "$h/device/uevent" && echo "/dev/$(basename "$h")"; done sudo chmod a+rw /dev/hidrawN
To make it permanent, add a udev rule (you're usually in plugdev):
echo 'KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", MODE="0660", GROUP="plugdev", TAG+="uaccess"' \ | sudo tee /etc/udev/rules.d/42-logitech-hidraw-permissions.rules sudo udevadm control --reload-rules && sudo udevadm trigger
Same rule Solaar ships. It only affects Logitech (046d) HID access, not your mouse/keyboard input.
Files
capture/
├── fetch-precache.js download app + full asset set (recommended; no deps)
├── capture.js optional Playwright capture (supported OS only)
├── rewrite.js patch URLs, telemetry, Linux support
├── write-mirror.js decode a bulk-fetch dump
└── package.json Playwright (only for capture.js)
serve.py static server with SPA fallback
serve.sh runs serve.py on :8765
mirror/ and capture/url-map.json are generated by the tooling and git-ignored.
Legal
Public domain (The Unlicense), scripts only. No Logitech software, firmware, fonts or trademarks are included; the scripts download them from Logitech's live site to your machine for your own use. Using that material is subject to Logitech's terms and your local law. Not affiliated with or endorsed by Logitech. "Logitech", "Logi", "Unifying" and "Bolt" are trademarks of Logitech.