centurion: Headset icon kind at construction (#3226)
* device: seed Centurion device kind=headset at construction Centurion-transport devices have no static descriptor and are not receiver-paired, so their kind was only resolved by an online feature scan. A headset powered off when Solaar started showed no kind, hence no headphone icon in the tray/window — unlike receiver-paired mice and keyboards, whose kind comes from the receiver's persistent pairing registers. Every Centurion-transport device seen so far is a headset, and the centurion flag is known at construction time. Seed _kind=headset there so the icon is correct offline and on first run. Drop the now-dead online _infer_kind_centurion() feature scan and its Centurion branch in the kind property. * centurion: seed child headset kind via pairing_info The construction-time kind=headset seeding only covered the direct create_device path. G522-style devices reach a CenturionReceiver, whose notify_devices() builds the headset as a child Device with device_info=None and sets dev.centurion=True only after __init__ — so the __init__ guard never fired and the child kept kind=None. Set kind=headset in the pairing_info dict the receiver already passes into Device.__init__, which covers the bridge path. Both paths now seed the kind offline.
This commit is contained in:
parent
e77746818e
commit
badce9bb07
|
|
@ -35,6 +35,7 @@ from solaar import configuration
|
|||
from . import base
|
||||
from . import exceptions
|
||||
from . import hidpp10
|
||||
from . import hidpp10_constants
|
||||
from .centurion_constants import CenturionCoreFeature
|
||||
from .common import Alert
|
||||
from .common import Battery
|
||||
|
|
@ -378,7 +379,7 @@ class CenturionReceiver:
|
|||
# Create child Device with receiver=self, number=1
|
||||
pairing_info = {
|
||||
"wpid": self.product_id,
|
||||
"kind": None,
|
||||
"kind": hidpp10_constants.DEVICE_KIND.headset, # every Centurion-transport device so far is a headset
|
||||
"serial": None,
|
||||
"polling": None,
|
||||
"power_switch": None,
|
||||
|
|
|
|||
|
|
@ -146,6 +146,8 @@ class Device:
|
|||
self.status_callback = None # for changes to other potentially visible aspects
|
||||
self.wpid = pairing_info["wpid"] if pairing_info else None # the Wireless PID is unique per device model
|
||||
self._kind = pairing_info["kind"] if pairing_info else None # mouse, keyboard, etc (see hidpp10.DEVICE_KIND)
|
||||
if self._kind is None and self.centurion:
|
||||
self._kind = hidpp10_constants.DEVICE_KIND.headset # every Centurion-transport device so far is a headset
|
||||
self._serial = pairing_info["serial"] if pairing_info else None # serial number (an 8-char hex string)
|
||||
self._polling_rate = pairing_info["polling"] if pairing_info else None
|
||||
self._power_switch = pairing_info["power_switch"] if pairing_info else None
|
||||
|
|
@ -322,27 +324,12 @@ class Device:
|
|||
|
||||
@property
|
||||
def kind(self):
|
||||
# Centurion devices are seeded with kind=headset at construction, so
|
||||
# this online lookup only runs for descriptor-less HID++ 2.0 devices.
|
||||
if not self._kind and self.online and self.protocol >= 2.0:
|
||||
if self.centurion:
|
||||
self._kind = self._infer_kind_centurion()
|
||||
else:
|
||||
self._kind = _hidpp20.get_kind(self)
|
||||
self._kind = _hidpp20.get_kind(self)
|
||||
return self._kind or "?"
|
||||
|
||||
def _infer_kind_centurion(self):
|
||||
"""Infer device kind from Centurion features (sub-device or top-level)."""
|
||||
# Check sub-device features (wireless via bridge)
|
||||
for feature in getattr(self, "_centurion_sub_features", ()):
|
||||
if isinstance(feature, int) and 0x0600 <= feature <= 0x06FF:
|
||||
return hidpp10_constants.DEVICE_KIND.headset
|
||||
# Check top-level features (direct USB connection, no bridge)
|
||||
if self.features:
|
||||
for feature, _index in self.features.enumerate():
|
||||
feat_int = int(feature) if isinstance(feature, int) else 0
|
||||
if 0x0600 <= feat_int <= 0x06FF:
|
||||
return hidpp10_constants.DEVICE_KIND.headset
|
||||
return None
|
||||
|
||||
@property
|
||||
def firmware(self) -> tuple[common.FirmwareInfo]:
|
||||
if self._firmware is None and self.online:
|
||||
|
|
|
|||
|
|
@ -403,7 +403,6 @@ class Device:
|
|||
gestures = device.Device.gestures
|
||||
__hash__ = device.Device.__hash__
|
||||
feature_request = device.Device.feature_request
|
||||
_infer_kind_centurion = device.Device._infer_kind_centurion
|
||||
|
||||
def __post_init__(self):
|
||||
self._name = self.name
|
||||
|
|
|
|||
|
|
@ -108,6 +108,10 @@ def test_create_centurion_device():
|
|||
assert test_device.hidpp_long is True
|
||||
assert int(test_device.handle) in base._centurion_handles
|
||||
|
||||
# kind is seeded at construction, so the headset icon shows even offline
|
||||
test_device.online = False
|
||||
assert test_device.kind == "headset"
|
||||
|
||||
# Clean up
|
||||
base._centurion_handles.discard(int(test_device.handle))
|
||||
|
||||
|
|
|
|||
|
|
@ -1116,21 +1116,6 @@ def test_centurion_sub_device_hardware_info():
|
|||
assert product_id == 0x0AF7
|
||||
|
||||
|
||||
def test_centurion_kind_inference():
|
||||
"""Centurion device with 0x06xx audio features infers kind=headset."""
|
||||
dev = fake_hidpp.Device("CENT_KIND", True, 2.6, fake_hidpp.r_centurion_headset, centurion=True)
|
||||
dev._centurion_sub_features = {
|
||||
hidpp20_constants.SupportedFeature.HEADSET_AUDIO_SIDETONE,
|
||||
hidpp20_constants.SupportedFeature.HEADSET_MIC_MUTE,
|
||||
}
|
||||
|
||||
kind = dev._infer_kind_centurion()
|
||||
|
||||
from logitech_receiver import hidpp10_constants
|
||||
|
||||
assert kind == hidpp10_constants.DEVICE_KIND.headset
|
||||
|
||||
|
||||
# --- CenturionReceiver tests ---
|
||||
|
||||
|
||||
|
|
@ -1228,6 +1213,21 @@ def test_centurion_receiver_container_with_device():
|
|||
recv[2]
|
||||
|
||||
|
||||
def test_centurion_receiver_child_device_kind_headset():
|
||||
"""notify_devices() seeds the headset child with kind=headset via pairing_info,
|
||||
so the headphone icon shows even when the headset is powered off at startup."""
|
||||
info = FakeCenturionDeviceInfo(product="PRO X 2 LIGHTSPEED")
|
||||
recv = CenturionReceiver(FakeLowLevel(), 0x99, info)
|
||||
recv._pending = False
|
||||
recv._dongle_features = []
|
||||
|
||||
recv.notify_devices()
|
||||
|
||||
child = recv[1]
|
||||
child.online = False
|
||||
assert child.kind == "headset"
|
||||
|
||||
|
||||
def test_centurion_receiver_enable_connection_notifications():
|
||||
"""CenturionReceiver.enable_connection_notifications() returns False."""
|
||||
info = FakeCenturionDeviceInfo()
|
||||
|
|
|
|||
Loading…
Reference in New Issue