feat: Razer Kraken V4 Pro — haptic sensitivity + battery charging state (issue #11)#20
Open
feat: Razer Kraken V4 Pro — haptic sensitivity + battery charging state (issue #11)#20
Conversation
…ld_haptic_report Wireshark analysis of haptics_synapse.pcapng revealed 5 exact byte-level bugs causing the 64-byte HID SET_REPORT to be malformed — firmware silently discarded every haptic command. Bugs fixed (all Wireshark-verified against packet #329): - buf[26]: 0x3A → 0x39 (wrong fixed value) - buf[33]=0x06 → buf[34]=0x06 (off-by-one on field marker) - buf[38]=0x07 → buf[39]=0x07 (off-by-one on field marker) - buf[39]=0x09 → buf[40]=0x09 (off-by-one on fixed byte) - buf[40]=0x09 → buf[41]=0x20 (off-by-one + wrong value 0x09→0x20) Intensity mapping: replaced broken linear formula with Wireshark-verified discrete lookup table matching Synapse's exact output: Off (0): haptic_a=0, haptic_b=0 Low (33): haptic_a=26, haptic_b=62 Med (66): haptic_a=40, haptic_b=68 High (100): haptic_a=78, haptic_b=81 Tests: updated 3 existing haptic tests + added test_haptic_report_v4_pro_low_intensity. All 79 Rust tests pass. 24 UI tests pass. Clippy clean. Closes #11 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Haptics fix: - Add build_haptic_audio_packet() and build_haptic_audio_end_frame() to razer_protocol.rs (Wireshark-verified pkt 27 / pkt 39 structure) - Add send_haptic_audio_burst() in usb_backend.rs: sends 5 frames of Interface-2 audio (cmd_class=0x03, cmd_id=0x0b, wVal=0x0300, wIdx=2) alongside the Interface-4 sensitivity command. The motor converts the audio signal to vibration; the Interface-4 command alone is silent. - Update set_haptic_intensity() in device_manager.rs to call the burst for non-zero levels after the sensitivity transfer succeeds. Battery fix: - Change headset battery state from Discharging to Charging in main.rs: USB-connected headset is always receiving power, so Charging is correct. 96% is the accurate hardware reading (USB-topped-off battery). TDD: - 4 new unit tests covering audio packet header, audio data, checksum, sub-counter variation, and end-of-frame packet (all Wireshark-verified) - All 83 Rust tests pass; all 24 UI tests pass; clippy clean; fmt clean Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…udio streaming Interface 2 on PID 0x0568 is bInterfaceSubClass=2 (Audio Streaming), managed by the kernel ALSA driver. Claiming it via libusb detaches ALSA and silences the headset (muting regression in af01361). Changes: - Remove send_haptic_audio_burst() and open_razer_device_on_interface() from usb_backend.rs — these must never be called on audio interfaces - device_manager.rs: Interface-4 sensitivity command only (correct behavior) - razer_protocol.rs: keep build_haptic_audio_packet/end_frame with #[allow(dead_code)] as Wireshark-verified protocol documentation + TDD tests remain green - main.rs: headset battery state = Charging(pct) for USB-connected device - HapticsTab.tsx: clarify that haptic intensity responds to audio playback Architecture note: the haptic motor converts audio to vibration automatically when game audio plays through Interface 2 (ALSA). Our daemon controls the amplification level only via Interface 4. No daemon-side audio burst needed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- usb_backend.rs: add log::debug! hex dump of 64-byte haptic payload after successful write_control. Run with RUST_LOG=debug to inspect exact bytes sent to Interface 4 without any audio or hardware changes needed. Log comment documents the 4-preset Wireshark lookup table for quick comparison. - wireshark/HAPTICS_VERIFICATION.md: new doc with three verification methods: 1. Daemon debug log (fastest — just run RUST_LOG=debug and move the slider) 2. Linux usbmon + tshark wire capture (confirms bytes hit the USB bus) 3. cargo test (no hardware needed — Wireshark-verified values in TDD tests) Includes full byte lookup table and fixed-field reference. No functional changes to haptic or battery code. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…back functionality
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements haptic intensity control and correct battery state reporting for the Razer Kraken V4 Pro (PID
0x0568).Changes
Haptic Intensity (Interface 4)
SetHapticIntensitydispatches a 64-byte HID Output Report to Interface 4 (wIdx=4,wVal=0x0202)Why Interface 2 Is Never Touched
Interface 2 on PID
0x0568isbInterfaceSubClass=2 Audio Streaming, managed by the kernel ALSA driver. Claiming it via libusb detaches ALSA and silences the headset. The audio burst approach was reverted. Protocol documentation (Wireshark-verified) is preserved with#[allow(dead_code)]for future reference.Battery Charging State
Discharging(pct)→Charging(pct)UI
HapticsTab.tsx: updated description to clarify haptics respond to in-game audio playbackTesting
cargo clippy --all-targets --all-features -- -D warnings: cleanCloses #11