Because a 600 MB software suite with background telemetry, auto-updating services, and a RGB lighting SDK shouldn't be the only way to know if your headset is dying.
A lightweight Windows system tray application that displays the real-time battery level of the Razer Nari wireless headset β built entirely through USB protocol reverse engineering, with zero dependency on Razer software.
The Razer Nari is a capable wireless headset. Its companion software, Razer Synapse, is not.
Synapse installs a constellation of background services β RazerNahimic, RazerGameScannerService, RazerCentralService, among others β that collectively consume anywhere from 300 to 600 MB of RAM at idle, register themselves for autostart without asking, and maintain persistent network connections to Razer's telemetry infrastructure. This means your headset usage patterns, audio settings, and hardware identifiers are being transmitted to external servers as a condition of knowing your battery percentage.
The interface itself buries the battery indicator under multiple clicks inside a large, slow-loading overlay. For a single piece of information β how much battery does my headset have? β the friction is remarkable.
NariMeter answers that question with a glanceable tray icon, ~345 KB on disk, and no network activity whatsoever.
- Battery percentage displayed directly in the Windows system tray
- Color-coded icons reflecting charge level at a glance
- Dedicated charging icon when the USB cable is connected
- Fully charged indicator when the cable is connected and battery is at 100%
- Headphone icon when the headset is powered off or disconnected
- Hover tooltip with current status
- Low battery notifications β configurable warn and critical thresholds
- Fully charged notification β alerts when the headset reaches 100%
- Run at Startup toggle in the right-click menu (via Windows registry, no installer required)
- Fully portable β single
.exe, no installation, no registry pollution beyond the optional startup entry
| Icon | State |
|---|---|
| π’ Green battery | Connected and discharging β battery > 50% |
| π‘ Yellow battery | Connected and discharging β battery > 20% |
| π΄ Red battery | Connected and discharging β battery β€ 20% |
| β‘ Charging battery | USB cable connected, actively charging |
| π’ Green battery | USB cable connected, 100% fully charged |
| π§ Headphone | Headset powered off or dongle disconnected |
- Windows 10 or later (x64)
- .NET 8 Runtime β required, free, one-click install
- Razer Nari wireless headset with USB dongle connected
- WinUSB driver installed on Interface 5 of the dongle (see setup below)
For developers building from source: .NET 8 SDK is required instead of the runtime.
Download and run the installer from dotnet.microsoft.com. Many users will already have this installed.
NariMeter communicates with the Nari dongle at the USB protocol level, bypassing Razer's driver stack entirely. For this to work, the WinUSB generic driver must be bound to Interface 5 of the dongle.
- Download and run Zadig
- In the menu, select Options β List All Devices
- Locate
Razer Nariin the dropdown β select the entry corresponding to Interface 5 - Set the target driver to WinUSB
- Click Replace Driver
This does not affect the headset's audio functionality. Only the HID interface used for battery reporting is replaced. Razer Synapse will lose the ability to communicate with the dongle on this interface, which is entirely the point.
Download NariMeter.exe from the Releases page and run it. No installation required. The tray icon will appear within a few seconds of the dongle being recognized.
NariMeter was built without access to any Razer documentation. The battery reporting protocol was discovered entirely through USB traffic analysis.
The Razer Nari dongle presents itself to Windows as a USB composite device with multiple interfaces. Using USBPcap and Wireshark, all HID traffic between Razer Synapse and the dongle was captured during normal operation.
The device identifiers are:
Vendor ID: 0x1532 (Razer Inc.)
Product ID: 0x051C (Nari dongle)
Interface: 5
Interface 5 is the HID interface responsible for device status reporting, separate from the audio and standard HID interfaces used for button input.
Synapse communicates with Interface 5 via USB HID control transfers β SET_REPORT followed by GET_REPORT β a standard HID pattern for querying device state.
The captured SET_REPORT payload that triggers a battery response is:
FF 0A 00 FD 04 12 F1 02 05 00 00 ... (64 bytes total)
This is sent as a Class request to the interface:
bmRequestType: 0x21 (Host β Device, Class, Interface)
bRequest: 0x09 (SET_REPORT)
wValue: 0x03FF
wIndex: 5 (Interface number)
wLength: 64
A subsequent GET_REPORT request retrieves the 64-byte response from the device:
bmRequestType: 0xA1 (Device β Host, Class, Interface)
bRequest: 0x01 (GET_REPORT)
wValue: 0x03FF
wIndex: 5
wLength: 64
Within the response buffer, the relevant fields are:
| Byte offset | Value | Meaning |
|---|---|---|
[1] |
0x01 |
Device idle / headset not active |
[2] |
0x00 |
Confirms idle state |
[9] |
0x05 |
USB cable connected (charging) |
[9] |
0x06 |
USB cable connected, battery fully charged |
[9] |
0x03 |
USB cable disconnected (discharging) |
[12:13] |
uint16 big-endian | Battery voltage in millivolts β primary source for percentage calculation |
[14] |
uint8 | Battery percentage reported by device firmware β used as sanity check only |
The firmware value at response[14] is not used as the primary battery percentage. Empirical testing revealed that the Nari firmware updates this value in large discrete jumps β for example, holding at 80% for several hours before dropping directly to 50% on a change of only ~8 mV. This is a firmware calibration limitation, not a reflection of actual charge level.
NariMeter instead derives the battery percentage from the raw millivolt reading at response[12:13], using a linear interpolation between the minimum and maximum observed voltages for the device:
percent = (mv - minMv) / (maxMv - minMv) Γ 100
The voltage bounds (minMv, maxMv) are calibrated adaptively per device β they start with conservative defaults and are refined automatically as the app observes readings near the low and high extremes during normal use. Calibrated values are persisted across sessions in NariMeter.state.json.
The firmware value at response[14] is retained as a sanity check: if the voltage-derived percentage and the firmware-reported percentage diverge by more than 40 percentage points, the reading is considered anomalous and the last known good value is preserved instead.
Battery percentage is displayed in steps of 5% and transitions smoothly β the displayed value moves at most 5% per poll cycle regardless of how large the underlying change is, preventing sudden jumps in the tray icon.
The device reports charge state natively via response[9]:
isCharging = response[9] == 0x05;
isFullyCharged = response[9] == 0x06;isFullyCharged short-circuits all stabilization and percentage calculation paths in BatteryReader β it is checked first and treated as authoritative. The tray icon and tooltip also treat Charging && BatteryPercent >= 100 as Fully Charged to cover the trickle charge window before the firmware byte transitions: the firmware can remain on 0x05 for several minutes after the cell reaches capacity while voltage stabilizes at 4200 mV.
Raw USB readings are noisy, particularly at startup when the host controller and device are still negotiating. NariMeter implements independent debounce thresholds in UsbDevice.cs:
- Powered on is only confirmed after 4 consecutive active readings (4 Γ 2s = 8 seconds)
- Powered off is confirmed after 4 consecutive idle readings
Battery level is polled every 30 seconds when active, which is sufficient given the slow rate of battery discharge.
Program.cs
βββ TrayApp.cs β ApplicationContext, timer orchestration, tray icon management
βββ BatteryReader.cs β Charge state logic, stabilization, state persistence
βββ UsbDevice.cs β USB HID control transfers, static buffers, debounce state machine
βββ HeadsetState.cs β Immutable state record, ChargeStatus enum, tooltip formatting
βββ StateStore.cs β JSON persistence of last known percentage and calibrated mV bounds
βββ StartupManager.cs β Windows registry autostart toggle (HKCU\...\Run)
NariMeter supports optional Windows balloon notifications, toggled via the right-click menu under Show Notifications:
- Low Battery Warning β triggered at a configurable threshold (default: 20%)
- Battery Critical β triggered at a configurable threshold (default: 10%)
- Fully Charged β triggered when the headset reaches 100% while on cable
Warn and critical thresholds are configurable independently via the right-click menu and persist across sessions.
Requires: .NET 8 SDK
git clone https://github.com/indina853/NariMeter.git
cd NariMeter
dotnet buildPublishing a portable executable:
dotnet publish -c ReleaseOutput: bin\Release\net8.0-windows\win-x64\publish\NariMeter.exe
The published executable requires the .NET 8 Runtime on the target machine.
| Metric | Value |
|---|---|
| Executable size | ~345 KB |
| RAM usage (steady state) | ~15 MB |
| CPU usage | < 0.1% |
| Network activity | None |
| Disk writes | Only on battery % change and settings updates |
| Poll interval β state | 2 seconds |
| Poll interval β battery | 30 seconds |
NariMeter/
βββ BatteryReader.cs β Battery charge state logic
βββ HeadsetState.cs β State record and charge status definitions
βββ Program.cs β Entry point
βββ StartupManager.cs β Run at startup via Windows registry
βββ StateStore.cs β Battery % and settings persistence (JSON)
βββ TrayApp.cs β Tray icon, timers, notifications, menu
βββ UsbDevice.cs β USB HID communication layer
βββ App.ico β Application icon (task manager, Explorer)
βββ Headphone.ico β Tray: powered off / disconnected state
βββ BatteryGreen.ico β Tray: battery > 50% or fully charged
βββ BatteryYellow.ico β Tray: battery > 20%
βββ BatteryRed.ico β Tray: battery β€ 20%
βββ BatteryCharging.ico β Tray: charging
βββ NariMeter.csproj
βββ RunTimeConfig.template.json
Pull requests are welcome. If you own a different Razer wireless headset and want to investigate whether the same protocol applies, the starting point is capturing USB traffic with USBPcap + Wireshark while Synapse queries your device, then comparing the SET_REPORT payload and response byte layout against the Nari protocol documented above.
MIT β see LICENSE