fix: code review points

# Conflicts:
#	lib/logitech_receiver/hidpp20_constants.py
This commit is contained in:
Caio Quirino 2026-03-25 22:33:02 +01:00
parent 308dc7a1d6
commit a3d2dbd869
No known key found for this signature in database
4 changed files with 445 additions and 113 deletions

View File

@ -0,0 +1,374 @@
# Logitech PRO X 2 Superstrike - Solaar CLI Reference
This document describes all available settings for the Logitech PRO X 2 Superstrike mouse via the Solaar CLI.
## Device Identification
| Property | Value |
|----------|-------|
| Name | PRO X 2 Superstrike |
| WPID | 40BD |
| Protocol | HID++ 4.2 |
| Kind | mouse |
## General CLI Syntax
```bash
# List all settings for device
solaar config <device>
# Read a specific setting
solaar config <device> <setting-name>
# Write a specific setting
solaar config <device> <setting-name> <value>
```
The `<device>` can be:
- Device number (e.g., `1`)
- Device name (e.g., `"PRO X 2 Superstrike"`)
- Serial number (e.g., `A1C55DB2`)
---
## Available Settings
### 1. Onboard Profiles
Controls whether the device uses its onboard profile or host-controlled settings.
| Property | Value |
|----------|-------|
| Setting Name | `onboard_profiles` |
| Type | Choice |
| Possible Values | `Disabled`, `Profile 1` |
**Commands:**
```bash
# Read current value
solaar config 1 onboard_profiles
# Set to disabled (allows host control of DPI, report rate, etc.)
solaar config 1 onboard_profiles Disabled
# Set to Profile 1 (use onboard profile)
solaar config 1 onboard_profiles "Profile 1"
```
**Note:** Many settings require `onboard_profiles` to be set to `Disabled` to be effective.
---
### 2. Report Rate
Controls the frequency of device movement reports.
| Property | Value |
|----------|-------|
| Setting Name | `report_rate_extended` |
| Type | Choice |
| Possible Values | `8ms`, `4ms`, `2ms`, `1ms`, `500us`, `250us`, `125us` |
**Commands:**
```bash
# Read current value
solaar config 1 report_rate_extended
# Set to 1ms (1000Hz)
solaar config 1 report_rate_extended 1ms
# Set to 500us (2000Hz)
solaar config 1 report_rate_extended 500us
# Set to 125us (8000Hz)
solaar config 1 report_rate_extended 125us
```
**Polling Rate Reference:**
| Value | Polling Rate |
|-------|--------------|
| `8ms` | 125 Hz |
| `4ms` | 250 Hz |
| `2ms` | 500 Hz |
| `1ms` | 1000 Hz |
| `500us` | 2000 Hz |
| `250us` | 4000 Hz |
| `125us` | 8000 Hz |
---
### 3. Sensitivity (DPI)
Controls mouse movement sensitivity.
| Property | Value |
|----------|-------|
| Setting Name | `dpi_extended` |
| Type | Complex (X, Y, LOD) |
| DPI Range | 100 - 32000 |
| LOD Values | `LOW`, `HIGH` |
**Commands:**
```bash
# Read current value
solaar config 1 dpi_extended
# Set DPI (format: {X:<value>, Y:<value>, LOD:<value>})
solaar config 1 dpi_extended "{X:800, Y:800, LOD:HIGH}"
# Set to 1600 DPI
solaar config 1 dpi_extended "{X:1600, Y:1600, LOD:HIGH}"
# Set different X and Y sensitivity
solaar config 1 dpi_extended "{X:800, Y:1600, LOD:LOW}"
```
---
## HITS Tuning Settings (Hall-Effect Inductive Trigger Switch)
These settings control the advanced click behavior of the PRO X 2 Superstrike's hall-effect switches.
### 4. Actuation Point
Controls how deep the button must be pressed to register a click.
| Property | Value |
|----------|-------|
| Setting Name (Left) | `superstrike-tuning_actuation-0` |
| Setting Name (Right) | `superstrike-tuning_actuation-1` |
| Type | Range |
| Range | 1 - 10 |
| Default | 5 |
**Value Interpretation:**
- `1` = Shallowest (hair trigger, minimal press)
- `10` = Deepest (full press required)
**Commands:**
```bash
# Read left button actuation
solaar config 1 superstrike-tuning_actuation-0
# Read right button actuation
solaar config 1 superstrike-tuning_actuation-1
# Set left button to shallow actuation (hair trigger)
solaar config 1 superstrike-tuning_actuation-0 1
# Set left button to deep actuation
solaar config 1 superstrike-tuning_actuation-0 10
# Set right button to medium actuation
solaar config 1 superstrike-tuning_actuation-1 5
```
---
### 5. Rapid Trigger Level
Controls the rapid trigger sensitivity, which allows the button to re-actuate quickly after partial release.
| Property | Value |
|----------|-------|
| Setting Name (Left) | `superstrike-tuning_rapid-trigger-level-0` |
| Setting Name (Right) | `superstrike-tuning_rapid-trigger-level-1` |
| Type | Range |
| Range | 1 - 5 |
| Default | 3 |
**Value Interpretation:**
- `1` = Fastest (most sensitive, smallest movement to re-trigger)
- `5` = Slowest (least sensitive, larger movement needed)
**Note:** Rapid trigger cannot be disabled on this device. The minimum level is 1.
**Commands:**
```bash
# Read left button rapid trigger level
solaar config 1 superstrike-tuning_rapid-trigger-level-0
# Read right button rapid trigger level
solaar config 1 superstrike-tuning_rapid-trigger-level-1
# Set left button to fastest rapid trigger
solaar config 1 superstrike-tuning_rapid-trigger-level-0 1
# Set left button to slowest rapid trigger
solaar config 1 superstrike-tuning_rapid-trigger-level-0 5
# Set right button to medium rapid trigger
solaar config 1 superstrike-tuning_rapid-trigger-level-1 3
```
---
### 6. Click Haptics
Controls the intensity of the haptic feedback when clicking.
| Property | Value |
|----------|-------|
| Setting Name (Left) | `superstrike-tuning_haptics-0` |
| Setting Name (Right) | `superstrike-tuning_haptics-1` |
| Type | Range |
| Range | 0 - 5 |
| Default | 3 |
**Value Interpretation:**
- `0` = Off (no haptic feedback)
- `1` = Minimal
- `2` = Light
- `3` = Medium
- `4` = Strong
- `5` = Strongest (maximum haptic feedback)
**Commands:**
```bash
# Read left button haptics level
solaar config 1 superstrike-tuning_haptics-0
# Read right button haptics level
solaar config 1 superstrike-tuning_haptics-1
# Disable haptics on left button
solaar config 1 superstrike-tuning_haptics-0 0
# Set left button to maximum haptics
solaar config 1 superstrike-tuning_haptics-0 5
# Set right button to medium haptics
solaar config 1 superstrike-tuning_haptics-1 3
```
---
## Complete Settings Summary
| Setting | CLI Name | Type | Range/Values | Button-Specific |
|---------|----------|------|--------------|-----------------|
| Onboard Profiles | `onboard_profiles` | Choice | `Disabled`, `Profile 1` | No |
| Report Rate | `report_rate_extended` | Choice | `8ms` to `125us` | No |
| Sensitivity | `dpi_extended` | Complex | 100-32000 DPI | No |
| Actuation Point | `superstrike-tuning_actuation-{0,1}` | Range | 1-10 | Yes |
| Rapid Trigger | `superstrike-tuning_rapid-trigger-level-{0,1}` | Range | 1-5 | Yes |
| Click Haptics | `superstrike-tuning_haptics-{0,1}` | Range | 0-5 | Yes |
---
## Batch Configuration Examples
### Gaming Profile (Fast Response)
```bash
#!/bin/bash
# Gaming profile: fast actuation, sensitive rapid trigger, medium haptics
solaar config 1 onboard_profiles Disabled
solaar config 1 report_rate_extended 125us
solaar config 1 dpi_extended "{X:800, Y:800, LOD:HIGH}"
# Left button - hair trigger
solaar config 1 superstrike-tuning_actuation-0 1
solaar config 1 superstrike-tuning_rapid-trigger-level-0 1
solaar config 1 superstrike-tuning_haptics-0 3
# Right button - hair trigger
solaar config 1 superstrike-tuning_actuation-1 1
solaar config 1 superstrike-tuning_rapid-trigger-level-1 1
solaar config 1 superstrike-tuning_haptics-1 3
```
### Productivity Profile (Comfortable)
```bash
#!/bin/bash
# Productivity profile: deeper actuation, slower rapid trigger, strong haptics
solaar config 1 onboard_profiles Disabled
solaar config 1 report_rate_extended 1ms
solaar config 1 dpi_extended "{X:1600, Y:1600, LOD:HIGH}"
# Left button - comfortable click
solaar config 1 superstrike-tuning_actuation-0 7
solaar config 1 superstrike-tuning_rapid-trigger-level-0 4
solaar config 1 superstrike-tuning_haptics-0 5
# Right button - comfortable click
solaar config 1 superstrike-tuning_actuation-1 7
solaar config 1 superstrike-tuning_rapid-trigger-level-1 4
solaar config 1 superstrike-tuning_haptics-1 5
```
### Silent Profile (No Haptics)
```bash
#!/bin/bash
# Silent profile: no haptic feedback
solaar config 1 superstrike-tuning_haptics-0 0
solaar config 1 superstrike-tuning_haptics-1 0
```
---
## Programmatic Usage
### Reading All Settings (JSON-like parsing)
```bash
# Get all settings as output
solaar config 1 2>/dev/null | grep "^[a-z]" | while read line; do
setting=$(echo "$line" | cut -d'=' -f1 | tr -d ' ')
value=$(echo "$line" | cut -d'=' -f2 | tr -d ' ')
echo "{\"setting\": \"$setting\", \"value\": \"$value\"}"
done
```
### Reading a Single Setting Value
```bash
# Extract just the value
solaar config 1 superstrike-tuning_actuation-0 2>/dev/null | grep "^superstrike" | cut -d'=' -f2 | tr -d ' '
```
### Error Handling
```bash
# Check if command succeeded
if solaar config 1 superstrike-tuning_actuation-0 5 2>/dev/null; then
echo "Setting applied successfully"
else
echo "Failed to apply setting"
fi
```
---
## Exit Codes
| Code | Meaning |
|------|---------|
| 0 | Success |
| 1 | Error (device not found, invalid setting, invalid value) |
---
## Notes
1. **Device Discovery**: Use `solaar show` to list all connected devices and their indices.
2. **Persistence**: Settings are saved to `~/.config/solaar/config.yaml` and automatically reapplied when the device reconnects.
3. **Onboard Profiles**: When `onboard_profiles` is set to `Profile 1`, some settings (DPI, report rate) are controlled by the device's onboard memory and cannot be changed via Solaar.
4. **HITS Settings**: The actuation, rapid trigger, and haptics settings are stored in the device and persist across reconnections, regardless of the onboard profile setting.
5. **Button Index**: `0` = Left button, `1` = Right button.

View File

@ -406,7 +406,6 @@ _D("MX Vertical Wireless Mouse", codename="MX Vertical", protocol=4.5, wpid="407
_D("Wireless Mouse Pebble M350", codename="Pebble", protocol=2.0, wpid="4080")
_D("MX Master 3 Wireless Mouse", codename="MX Master 3", protocol=4.5, wpid="4082", btid=0xB023)
_D("PRO X Wireless", kind="mouse", codename="PRO X", wpid="4093", usbid=0xC094)
_D("PRO X 2 Superstrike", kind="mouse", codename="PRO X 2 Superstrike", wpid="40BD")
_D("G9 Laser Mouse", codename="G9", usbid=0xC048, interface=1, protocol=1.0)
_D("G9x Laser Mouse", codename="G9x", usbid=0xC066, interface=1, protocol=1.0)

View File

@ -76,7 +76,7 @@ class SupportedFeature(IntEnum):
REPROG_CONTROLS_V2_2 = 0x1B02 # LogiOptions 2.10.73 features.xml
REPROG_CONTROLS_V3 = 0x1B03
REPROG_CONTROLS_V4 = 0x1B04
SUPERSTRIKE_TUNING = 0x1B0C # PRO X 2 Superstrike HITS tuning (actuation, rapid trigger, haptics)
ANALOG_BUTTONS = 0x1B0C # Analog button tuning (actuation point, rapid trigger, haptics)
FULL_KEY_CUSTOMIZATION = 0x1B05
CONTROL_LIST = 0x1B10
SWITCH_SWAPABILITY = 0x1B20

View File

@ -1809,108 +1809,76 @@ class ForceSensing(settings_new.Settings):
return setting
# PRO X 2 Superstrike HITS (Hall-Effect Inductive Trigger Switch) settings
# Analog button tuning settings (actuation point, rapid trigger, haptics)
class _SuperstrikeActuationRW(settings.FeatureRW):
"""RW for Superstrike actuation point (1-10) per button."""
class _AnalogButtonActuationRW(settings.FeatureRW):
"""RW for analog button actuation point per button."""
def __init__(self, feature, button_index, max_actuation):
super().__init__(feature, read_fnid=0x20, write_fnid=0x10) # Write is 0x10, not 0x30
self.button_index = button_index
self.max_actuation = max_actuation # Device's max value (e.g., 40)
def read(self, device, data_bytes=b""):
res = device.feature_request(self.feature, 0x20, self.button_index)
if not res:
return b"\x05" # default mid-point
# Actuation is in byte 1, scale from device range (1-max) to UI range (1-10)
device_val = res[1]
ui_val = max(1, min(10, round(device_val * 10 / self.max_actuation)))
return bytes([ui_val])
def write(self, device, data_bytes):
current = device.feature_request(self.feature, 0x20, self.button_index)
if not current:
return None
# Scale from UI range (1-10) to device range (4-max, step 4 based on observed behavior)
ui_val = data_bytes[0]
device_val = max(4, min(self.max_actuation, round(ui_val * self.max_actuation / 10)))
# Round to nearest multiple of 4 (device seems to quantize)
device_val = ((device_val + 2) // 4) * 4
# Write using func 0x10: [button_index, actuation, rt, byte3]
return device.feature_request(self.feature, 0x10, self.button_index, device_val, current[2], current[3])
class _SuperstrikeRapidTriggerLevelRW(settings.FeatureRW):
"""RW for Superstrike rapid trigger sensitivity (1=fast to 5=slow) per button."""
def __init__(self, feature, button_index, max_rt_level):
super().__init__(feature, read_fnid=0x20, write_fnid=0x10) # Write is 0x10
self.button_index = button_index
self.max_rt_level = max_rt_level # Device's max RT level (e.g., 20)
def read(self, device, data_bytes=b""):
res = device.feature_request(self.feature, 0x20, self.button_index)
if not res or res[2] == 0:
return b"\x03" # default mid-point if RT disabled
# Scale from device range (1-max) to UI range (1-5)
device_val = res[2]
ui_val = max(1, min(5, round(device_val * 5 / self.max_rt_level)))
return bytes([ui_val])
def write(self, device, data_bytes):
current = device.feature_request(self.feature, 0x20, self.button_index)
if not current:
return None
# Scale from UI range (1-5) to device range (1-max)
ui_val = data_bytes[0]
device_val = max(1, min(self.max_rt_level, round(ui_val * self.max_rt_level / 5)))
return device.feature_request(self.feature, 0x10, self.button_index, current[1], device_val, current[3])
class _SuperstrikeHapticsRW(settings.FeatureRW):
"""RW for Superstrike click haptics (0-5) per button via TUNING feature byte3.
Device stores haptics in byte3 of the TUNING response with values 0, 4, 8, 12, 16, 20.
UI range is 0-5 where 0=off and 5=strongest.
"""
def __init__(self, feature, button_index, max_haptics):
def __init__(self, feature, button_index):
super().__init__(feature, read_fnid=0x20, write_fnid=0x10)
self.button_index = button_index
self.max_haptics = max_haptics # Device's max haptics value (e.g., 20)
def read(self, device, data_bytes=b""):
res = device.feature_request(self.feature, 0x20, self.button_index)
if not res:
return b"\x03" # default mid-point
# Haptics is in byte 3, scale from device range (0-max) to UI range (0-5)
device_val = res[3]
ui_val = round(device_val * 5 / self.max_haptics)
return bytes([ui_val])
return b"\x14" # default mid-point
return bytes([res[1]])
def write(self, device, data_bytes):
current = device.feature_request(self.feature, 0x20, self.button_index)
if not current:
return None
# Scale from UI range (0-5) to device range (0-max, step 4)
ui_val = data_bytes[0]
device_val = round(ui_val * self.max_haptics / 5)
# Round to nearest multiple of 4 (device quantizes to 0, 4, 8, 12, 16, 20)
device_val = ((device_val + 2) // 4) * 4
device_val = min(device_val, self.max_haptics)
# Write: [button_index, actuation, rt, haptics]
return device.feature_request(self.feature, 0x10, self.button_index, current[1], current[2], device_val)
return device.feature_request(self.feature, 0x10, self.button_index, data_bytes[0], current[2], current[3])
class SuperstrikeTuning_(settings.Setting):
"""HITS tuning settings: actuation point, rapid trigger, and haptics configuration."""
class _AnalogButtonRapidTriggerRW(settings.FeatureRW):
"""RW for analog button rapid trigger sensitivity per button."""
name = "superstrike-tuning_"
label = _("HITS Tuning")
description = _("Configure Hall-Effect Inductive Trigger Switch settings including haptics.")
feature = _F.SUPERSTRIKE_TUNING
def __init__(self, feature, button_index):
super().__init__(feature, read_fnid=0x20, write_fnid=0x10)
self.button_index = button_index
def read(self, device, data_bytes=b""):
res = device.feature_request(self.feature, 0x20, self.button_index)
if not res:
return b"\x0a" # default mid-point
return bytes([res[2]])
def write(self, device, data_bytes):
current = device.feature_request(self.feature, 0x20, self.button_index)
if not current:
return None
return device.feature_request(self.feature, 0x10, self.button_index, current[1], data_bytes[0], current[3])
class _AnalogButtonHapticsRW(settings.FeatureRW):
"""RW for analog button click haptics per button."""
def __init__(self, feature, button_index):
super().__init__(feature, read_fnid=0x20, write_fnid=0x10)
self.button_index = button_index
def read(self, device, data_bytes=b""):
res = device.feature_request(self.feature, 0x20, self.button_index)
if not res:
return b"\x0a" # default mid-point
return bytes([res[3]])
def write(self, device, data_bytes):
current = device.feature_request(self.feature, 0x20, self.button_index)
if not current:
return None
return device.feature_request(self.feature, 0x10, self.button_index, current[1], current[2], data_bytes[0])
class AnalogButtonTuning(settings.Setting):
"""Analog button tuning: actuation point, rapid trigger, and haptics configuration."""
name = "analog-button-tuning"
label = _("Analog Button Tuning")
description = _("Configure analog button settings including actuation point, rapid trigger, and haptics.")
feature = _F.ANALOG_BUTTONS
@classmethod
def build(cls, device):
@ -1931,40 +1899,31 @@ class SuperstrikeTuning_(settings.Setting):
button_names = [_("Left Button"), _("Right Button")]
all_settings = []
# Group settings by feature type for better UI organization
# Actuation Points (UI: 1-10, device: 1-max_actuation)
for i in range(button_count):
btn_name = button_names[i] if i < len(button_names) else f"Button {i}"
rw_act = _SuperstrikeActuationRW(cls.feature, i, max_actuation)
val_act = settings_validator.RangeValidator(min_value=1, max_value=10)
rw_act = _AnalogButtonActuationRW(cls.feature, i)
val_act = settings_validator.RangeValidator(min_value=1, max_value=max_actuation)
s_act = settings.Setting(device, rw_act, val_act)
s_act.name = f"superstrike-tuning_actuation-{i}"
s_act.name = f"analog-button-tuning_actuation-{i}"
s_act.label = f"{btn_name} Actuation Point"
s_act.description = _("Actuation point depth (1=shallow, 10=deep).")
s_act.description = _("Actuation point depth (raw device value).")
all_settings.append(s_act)
# Rapid Trigger Levels (UI: 1-5, device: 1-max_rt_level)
# Note: Device doesn't support disabling rapid trigger (rt_level=0 returns error)
for i in range(button_count):
btn_name = button_names[i] if i < len(button_names) else f"Button {i}"
rw_rt_lvl = _SuperstrikeRapidTriggerLevelRW(cls.feature, i, max_rt_level)
val_rt_lvl = settings_validator.RangeValidator(min_value=1, max_value=5)
s_rt_lvl = settings.Setting(device, rw_rt_lvl, val_rt_lvl)
s_rt_lvl.name = f"superstrike-tuning_rapid-trigger-level-{i}"
s_rt_lvl.label = f"{btn_name} Rapid Trigger Level"
s_rt_lvl.description = _("Rapid trigger sensitivity (1=fast, 5=slow).")
all_settings.append(s_rt_lvl)
rw_rt = _AnalogButtonRapidTriggerRW(cls.feature, i)
val_rt = settings_validator.RangeValidator(min_value=1, max_value=max_rt_level)
s_rt = settings.Setting(device, rw_rt, val_rt)
s_rt.name = f"analog-button-tuning_rapid-trigger-{i}"
s_rt.label = f"{btn_name} Rapid Trigger"
s_rt.description = _("Rapid trigger sensitivity (raw device value).")
all_settings.append(s_rt)
# Click Haptics (UI: 0-5, device: 0-max_haptics in steps of 4)
for i in range(button_count):
btn_name = button_names[i] if i < len(button_names) else f"Button {i}"
rw_haptics = _SuperstrikeHapticsRW(cls.feature, i, max_haptics)
val_haptics = settings_validator.RangeValidator(min_value=0, max_value=5)
rw_haptics = _AnalogButtonHapticsRW(cls.feature, i)
val_haptics = settings_validator.RangeValidator(min_value=0, max_value=max_haptics)
s_haptics = settings.Setting(device, rw_haptics, val_haptics)
s_haptics.name = f"superstrike-tuning_haptics-{i}"
s_haptics.name = f"analog-button-tuning_haptics-{i}"
s_haptics.label = f"{btn_name} Click Haptics"
s_haptics.description = _("Click haptic feedback intensity (0=off, 5=strongest).")
s_haptics.description = _("Click haptic feedback intensity (raw device value, 0=off).")
all_settings.append(s_haptics)
return all_settings if all_settings else None
@ -2093,7 +2052,7 @@ SETTINGS: list[settings.Setting] = [
Gesture2Gestures, # working
Gesture2Divert,
Gesture2Params, # working
SuperstrikeTuning_,
AnalogButtonTuning,
HapticLevel,
PlayHapticWaveForm,
Sidetone,