From be83dac209898f1cd1c11657ffcf04fb5575647d Mon Sep 17 00:00:00 2001 From: MattHag <16444067+MattHag@users.noreply.github.com> Date: Sun, 2 Jun 2024 16:34:00 +0200 Subject: [PATCH] hid: Convert definition of HID registers to enum * Refactor HID Register definitions Use enums for distinct type hints, easy discovery of registers. Make constants uppercase and benefit from enum auto-completion. Related #2273 * Improve type hints: Registers --- lib/logitech_receiver/base_usb.py | 11 ++- lib/logitech_receiver/descriptors.py | 86 +++++++++---------- lib/logitech_receiver/device.py | 2 - lib/logitech_receiver/hidpp10.py | 58 ++++++------- lib/logitech_receiver/hidpp10_constants.py | 68 ++++++++------- lib/logitech_receiver/notifications.py | 14 +-- lib/logitech_receiver/receiver.py | 34 ++++---- lib/logitech_receiver/settings.py | 2 +- lib/logitech_receiver/settings_templates.py | 12 +-- lib/solaar/cli/pair.py | 1 - lib/solaar/cli/probe.py | 35 ++++---- lib/solaar/listener.py | 4 +- tests/logitech_receiver/test_hidpp10.py | 95 ++++++++++----------- 13 files changed, 213 insertions(+), 209 deletions(-) diff --git a/lib/logitech_receiver/base_usb.py b/lib/logitech_receiver/base_usb.py index 5460cf3a3..546b23768 100644 --- a/lib/logitech_receiver/base_usb.py +++ b/lib/logitech_receiver/base_usb.py @@ -26,10 +26,13 @@ from solaar.i18n import _ -# max_devices is only used for receivers that do not support reading from _R.receiver_info offset 0x03, default to 1 -# may_unpair is only used for receivers that do not support reading from _R.receiver_info offset 0x03, default to False -# unpair is for receivers that do support reading from _R.receiver_info offset 0x03, no default -## should this last be changed so that may_unpair is used for all receivers? writing to _R.receiver_pairing doesn't seem right +# max_devices is only used for receivers that do not support reading from Registers.RECEIVER_INFO offset 0x03, default +# to 1. +# may_unpair is only used for receivers that do not support reading from Registers.RECEIVER_INFO offset 0x03, +# default to False. +# unpair is for receivers that do support reading from Registers.RECEIVER_INFO offset 0x03, no default. +## should this last be changed so that may_unpair is used for all receivers? writing to Registers.RECEIVER_PAIRING +## doesn't seem right # re_pairs determines whether a receiver pairs by replacing existing pairings, default to False ## currently only one receiver is so marked - should there be more? diff --git a/lib/logitech_receiver/descriptors.py b/lib/logitech_receiver/descriptors.py index 0f5888ea0..de4a49c56 100644 --- a/lib/logitech_receiver/descriptors.py +++ b/lib/logitech_receiver/descriptors.py @@ -25,7 +25,7 @@ """ from .hidpp10_constants import DEVICE_KIND -from .hidpp10_constants import REGISTERS as REG +from .hidpp10_constants import Registers as Reg class _DeviceDescriptor: @@ -193,24 +193,24 @@ def get_btid(btid): # Keyboards -_D("Wireless Keyboard EX110", codename="EX110", protocol=1.0, wpid="0055", registers=(REG.battery_status,)) -_D("Wireless Keyboard S510", codename="S510", protocol=1.0, wpid="0056", registers=(REG.battery_status,)) -_D("Wireless Wave Keyboard K550", codename="K550", protocol=1.0, wpid="0060", registers=(REG.battery_status,)) -_D("Wireless Keyboard EX100", codename="EX100", protocol=1.0, wpid="0065", registers=(REG.battery_status,)) -_D("Wireless Keyboard MK300", codename="MK300", protocol=1.0, wpid="0068", registers=(REG.battery_status,)) -_D("Number Pad N545", codename="N545", protocol=1.0, wpid="2006", registers=(REG.battery_status,)) -_D("Wireless Compact Keyboard K340", codename="K340", protocol=1.0, wpid="2007", registers=(REG.battery_status,)) -_D("Wireless Keyboard MK700", codename="MK700", protocol=1.0, wpid="2008", registers=(REG.battery_status,)) -_D("Wireless Wave Keyboard K350", codename="K350", protocol=1.0, wpid="200A", registers=(REG.battery_status,)) -_D("Wireless Keyboard MK320", codename="MK320", protocol=1.0, wpid="200F", registers=(REG.battery_status,)) +_D("Wireless Keyboard EX110", codename="EX110", protocol=1.0, wpid="0055", registers=(Reg.BATTERY_STATUS,)) +_D("Wireless Keyboard S510", codename="S510", protocol=1.0, wpid="0056", registers=(Reg.BATTERY_STATUS,)) +_D("Wireless Wave Keyboard K550", codename="K550", protocol=1.0, wpid="0060", registers=(Reg.BATTERY_STATUS,)) +_D("Wireless Keyboard EX100", codename="EX100", protocol=1.0, wpid="0065", registers=(Reg.BATTERY_STATUS,)) +_D("Wireless Keyboard MK300", codename="MK300", protocol=1.0, wpid="0068", registers=(Reg.BATTERY_STATUS,)) +_D("Number Pad N545", codename="N545", protocol=1.0, wpid="2006", registers=(Reg.BATTERY_STATUS,)) +_D("Wireless Compact Keyboard K340", codename="K340", protocol=1.0, wpid="2007", registers=(Reg.BATTERY_STATUS,)) +_D("Wireless Keyboard MK700", codename="MK700", protocol=1.0, wpid="2008", registers=(Reg.BATTERY_STATUS,)) +_D("Wireless Wave Keyboard K350", codename="K350", protocol=1.0, wpid="200A", registers=(Reg.BATTERY_STATUS,)) +_D("Wireless Keyboard MK320", codename="MK320", protocol=1.0, wpid="200F", registers=(Reg.BATTERY_STATUS,)) _D( "Wireless Illuminated Keyboard K800", codename="K800", protocol=1.0, wpid="2010", - registers=(REG.battery_status, REG.three_leds), + registers=(Reg.BATTERY_STATUS, Reg.THREE_LEDS), ) -_D("Wireless Keyboard K520", codename="K520", protocol=1.0, wpid="2011", registers=(REG.battery_status,)) +_D("Wireless Keyboard K520", codename="K520", protocol=1.0, wpid="2011", registers=(Reg.BATTERY_STATUS,)) _D("Wireless Solar Keyboard K750", codename="K750", protocol=2.0, wpid="4002") _D("Wireless Keyboard K270 (unifying)", codename="K270", protocol=2.0, wpid="4003") _D("Wireless Keyboard K360", codename="K360", protocol=2.0, wpid="4004") @@ -235,57 +235,57 @@ def get_btid(btid): # Mice -_D("LX5 Cordless Mouse", codename="LX5", protocol=1.0, wpid="0036", registers=(REG.battery_status,)) -_D("LX7 Cordless Laser Mouse", codename="LX7", protocol=1.0, wpid="0039", registers=(REG.battery_status,)) -_D("Wireless Wave Mouse M550", codename="M550", protocol=1.0, wpid="003C", registers=(REG.battery_status,)) -_D("Wireless Mouse EX100", codename="EX100m", protocol=1.0, wpid="003F", registers=(REG.battery_status,)) -_D("Wireless Mouse M30", codename="M30", protocol=1.0, wpid="0085", registers=(REG.battery_status,)) -_D("MX610 Laser Cordless Mouse", codename="MX610", protocol=1.0, wpid="1001", registers=(REG.battery_status,)) -_D("G7 Cordless Laser Mouse", codename="G7", protocol=1.0, wpid="1002", registers=(REG.battery_status,)) -_D("V400 Laser Cordless Mouse", codename="V400", protocol=1.0, wpid="1003", registers=(REG.battery_status,)) -_D("MX610 Left-Handled Mouse", codename="MX610L", protocol=1.0, wpid="1004", registers=(REG.battery_status,)) -_D("V450 Laser Cordless Mouse", codename="V450", protocol=1.0, wpid="1005", registers=(REG.battery_status,)) +_D("LX5 Cordless Mouse", codename="LX5", protocol=1.0, wpid="0036", registers=(Reg.BATTERY_STATUS,)) +_D("LX7 Cordless Laser Mouse", codename="LX7", protocol=1.0, wpid="0039", registers=(Reg.BATTERY_STATUS,)) +_D("Wireless Wave Mouse M550", codename="M550", protocol=1.0, wpid="003C", registers=(Reg.BATTERY_STATUS,)) +_D("Wireless Mouse EX100", codename="EX100m", protocol=1.0, wpid="003F", registers=(Reg.BATTERY_STATUS,)) +_D("Wireless Mouse M30", codename="M30", protocol=1.0, wpid="0085", registers=(Reg.BATTERY_STATUS,)) +_D("MX610 Laser Cordless Mouse", codename="MX610", protocol=1.0, wpid="1001", registers=(Reg.BATTERY_STATUS,)) +_D("G7 Cordless Laser Mouse", codename="G7", protocol=1.0, wpid="1002", registers=(Reg.BATTERY_STATUS,)) +_D("V400 Laser Cordless Mouse", codename="V400", protocol=1.0, wpid="1003", registers=(Reg.BATTERY_STATUS,)) +_D("MX610 Left-Handled Mouse", codename="MX610L", protocol=1.0, wpid="1004", registers=(Reg.BATTERY_STATUS,)) +_D("V450 Laser Cordless Mouse", codename="V450", protocol=1.0, wpid="1005", registers=(Reg.BATTERY_STATUS,)) _D( "VX Revolution", codename="VX Revolution", kind=DEVICE_KIND.mouse, protocol=1.0, wpid=("1006", "100D", "0612"), - registers=(REG.battery_charge,), + registers=(Reg.BATTERY_CHARGE,), ) -_D("MX Air", codename="MX Air", protocol=1.0, kind=DEVICE_KIND.mouse, wpid=("1007", "100E"), registers=(REG.battery_charge,)) +_D("MX Air", codename="MX Air", protocol=1.0, kind=DEVICE_KIND.mouse, wpid=("1007", "100E"), registers=(Reg.BATTERY_CHARGE)) _D( "MX Revolution", codename="MX Revolution", protocol=1.0, kind=DEVICE_KIND.mouse, wpid=("1008", "100C"), - registers=(REG.battery_charge,), + registers=(Reg.BATTERY_CHARGE,), ) -_D("MX620 Laser Cordless Mouse", codename="MX620", protocol=1.0, wpid=("100A", "1016"), registers=(REG.battery_charge,)) -_D("VX Nano Cordless Laser Mouse", codename="VX Nano", protocol=1.0, wpid=("100B", "100F"), registers=(REG.battery_charge,)) -_D("V450 Nano Cordless Laser Mouse", codename="V450 Nano", protocol=1.0, wpid="1011", registers=(REG.battery_charge,)) -_D("V550 Nano Cordless Laser Mouse", codename="V550 Nano", protocol=1.0, wpid="1013", registers=(REG.battery_charge,)) +_D("MX620 Laser Cordless Mouse", codename="MX620", protocol=1.0, wpid=("100A", "1016"), registers=(Reg.BATTERY_CHARGE,)) +_D("VX Nano Cordless Laser Mouse", codename="VX Nano", protocol=1.0, wpid=("100B", "100F"), registers=(Reg.BATTERY_CHARGE,)) +_D("V450 Nano Cordless Laser Mouse", codename="V450 Nano", protocol=1.0, wpid="1011", registers=(Reg.BATTERY_CHARGE,)) +_D("V550 Nano Cordless Laser Mouse", codename="V550 Nano", protocol=1.0, wpid="1013", registers=(Reg.BATTERY_CHARGE,)) _D( "MX 1100 Cordless Laser Mouse", codename="MX 1100", protocol=1.0, kind=DEVICE_KIND.mouse, wpid="1014", - registers=(REG.battery_charge,), + registers=(Reg.BATTERY_CHARGE,), ) -_D("Anywhere Mouse MX", codename="Anywhere MX", protocol=1.0, wpid="1017", registers=(REG.battery_charge,)) +_D("Anywhere Mouse MX", codename="Anywhere MX", protocol=1.0, wpid="1017", registers=(Reg.BATTERY_CHARGE,)) _D( "Performance Mouse MX", codename="Performance MX", protocol=1.0, wpid="101A", - registers=(REG.battery_status, REG.three_leds), + registers=(Reg.BATTERY_STATUS, Reg.THREE_LEDS), ) -_D("Marathon Mouse M705 (M-R0009)", codename="M705 (M-R0009)", protocol=1.0, wpid="101B", registers=(REG.battery_charge,)) -_D("Wireless Mouse M350", codename="M350", protocol=1.0, wpid="101C", registers=(REG.battery_charge,)) -_D("Wireless Mouse M505", codename="M505/B605", protocol=1.0, wpid="101D", registers=(REG.battery_charge,)) -_D("Wireless Mouse M305", codename="M305", protocol=1.0, wpid="101F", registers=(REG.battery_status,)) +_D("Marathon Mouse M705 (M-R0009)", codename="M705 (M-R0009)", protocol=1.0, wpid="101B", registers=(Reg.BATTERY_CHARGE,)) +_D("Wireless Mouse M350", codename="M350", protocol=1.0, wpid="101C", registers=(Reg.BATTERY_CHARGE,)) +_D("Wireless Mouse M505", codename="M505/B605", protocol=1.0, wpid="101D", registers=(Reg.BATTERY_CHARGE,)) +_D("Wireless Mouse M305", codename="M305", protocol=1.0, wpid="101F", registers=(Reg.BATTERY_STATUS,)) _D("Wireless Mouse M215", codename="M215", protocol=1.0, wpid="1020") _D( "G700 Gaming Mouse", @@ -295,12 +295,12 @@ def get_btid(btid): usbid=0xC06B, interface=1, registers=( - REG.battery_status, - REG.three_leds, + Reg.BATTERY_STATUS, + Reg.THREE_LEDS, ), ) -_D("Wireless Mouse M310", codename="M310", protocol=1.0, wpid="1024", registers=(REG.battery_status,)) -_D("Wireless Mouse M510", codename="M510", protocol=1.0, wpid="1025", registers=(REG.battery_status,)) +_D("Wireless Mouse M310", codename="M310", protocol=1.0, wpid="1024", registers=(Reg.BATTERY_STATUS,)) +_D("Wireless Mouse M510", codename="M510", protocol=1.0, wpid="1025", registers=(Reg.BATTERY_STATUS,)) _D("Fujitsu Sonic Mouse", codename="Sonic", protocol=1.0, wpid="1029") _D( "G700s Gaming Mouse", @@ -310,8 +310,8 @@ def get_btid(btid): usbid=0xC07C, interface=1, registers=( - REG.battery_status, - REG.three_leds, + Reg.BATTERY_STATUS, + Reg.THREE_LEDS, ), ) _D("Couch Mouse M515", codename="M515", protocol=2.0, wpid="4007") diff --git a/lib/logitech_receiver/device.py b/lib/logitech_receiver/device.py index 8d14e6068..a627e6fc5 100644 --- a/lib/logitech_receiver/device.py +++ b/lib/logitech_receiver/device.py @@ -44,8 +44,6 @@ _hidpp10 = hidpp10.Hidpp10() _hidpp20 = hidpp20.Hidpp20() -_R = hidpp10_constants.REGISTERS -_IR = hidpp10_constants.INFO_SUBREGISTERS class LowLevelInterface(Protocol): diff --git a/lib/logitech_receiver/hidpp10.py b/lib/logitech_receiver/hidpp10.py index 10139f375..a10d3ca40 100644 --- a/lib/logitech_receiver/hidpp10.py +++ b/lib/logitech_receiver/hidpp10.py @@ -25,7 +25,7 @@ from .common import Battery from .common import BatteryLevelApproximation from .common import BatteryStatus -from .hidpp10_constants import REGISTERS +from .hidpp10_constants import Registers from .hidpp20_constants import FIRMWARE_KIND logger = logging.getLogger(__name__) @@ -52,30 +52,30 @@ def registers(self) -> list: ... -def read_register(device: Device, register_number, *params): - assert device is not None, f"tried to read register {register_number:02X} from invalid device {device}" +def read_register(device: Device, register: Registers | int, *params) -> Any: + assert device is not None, f"tried to read register {register:02X} from invalid device {device}" # support long registers by adding a 2 in front of the register number - request_id = 0x8100 | (int(register_number) & 0x2FF) + request_id = 0x8100 | (int(register) & 0x2FF) return device.request(request_id, *params) -def write_register(device: Device, register_number, *value): - assert device is not None, f"tried to write register {register_number:02X} to invalid device {device}" +def write_register(device: Device, register: Registers | int, *value) -> Any: + assert device is not None, f"tried to write register {register:02X} to invalid device {device}" # support long registers by adding a 2 in front of the register number - request_id = 0x8000 | (int(register_number) & 0x2FF) + request_id = 0x8000 | (int(register) & 0x2FF) return device.request(request_id, *value) def get_configuration_pending_flags(receiver): assert not receiver.isDevice - result = read_register(receiver, REGISTERS.devices_configuration) + result = read_register(receiver, Registers.DEVICES_CONFIGURATION) if result is not None: return ord(result[:1]) def set_configuration_pending_flags(receiver, devices): assert not receiver.isDevice - result = write_register(receiver, REGISTERS.devices_configuration, devices) + result = write_register(receiver, Registers.DEVICES_CONFIGURATION, devices) return result is not None @@ -90,7 +90,7 @@ def get_battery(self, device: Device): # let's just assume HID++ 2.0 devices do not provide the battery info in a register return - for r in (REGISTERS.battery_status, REGISTERS.battery_charge): + for r in (Registers.BATTERY_STATUS, Registers.BATTERY_CHARGE): if r in device.registers: reply = read_register(device, r) if reply: @@ -98,44 +98,44 @@ def get_battery(self, device: Device): return # the descriptor does not tell us which register this device has, try them both - reply = read_register(device, REGISTERS.battery_charge) + reply = read_register(device, Registers.BATTERY_CHARGE) if reply: # remember this for the next time - device.registers.append(REGISTERS.battery_charge) - return parse_battery_status(REGISTERS.battery_charge, reply) + device.registers.append(Registers.BATTERY_CHARGE) + return parse_battery_status(Registers.BATTERY_CHARGE, reply) - reply = read_register(device, REGISTERS.battery_status) + reply = read_register(device, Registers.BATTERY_STATUS) if reply: # remember this for the next time - device.registers.append(REGISTERS.battery_status) - return parse_battery_status(REGISTERS.battery_status, reply) + device.registers.append(Registers.BATTERY_STATUS) + return parse_battery_status(Registers.BATTERY_STATUS, reply) def get_firmware(self, device: Device): assert device is not None firmware = [None, None, None] - reply = read_register(device, REGISTERS.firmware, 0x01) + reply = read_register(device, Registers.FIRMWARE, 0x01) if not reply: # won't be able to read any of it now... return fw_version = common.strhex(reply[1:3]) fw_version = f"{fw_version[0:2]}.{fw_version[2:4]}" - reply = read_register(device, REGISTERS.firmware, 0x02) + reply = read_register(device, Registers.FIRMWARE, 0x02) if reply: fw_version += ".B" + common.strhex(reply[1:3]) fw = common.FirmwareInfo(FIRMWARE_KIND.Firmware, "", fw_version, None) firmware[0] = fw - reply = read_register(device, REGISTERS.firmware, 0x04) + reply = read_register(device, Registers.FIRMWARE, 0x04) if reply: bl_version = common.strhex(reply[1:3]) bl_version = f"{bl_version[0:2]}.{bl_version[2:4]}" bl = common.FirmwareInfo(FIRMWARE_KIND.Bootloader, "", bl_version, None) firmware[1] = bl - reply = read_register(device, REGISTERS.firmware, 0x03) + reply = read_register(device, Registers.FIRMWARE, 0x03) if reply: o_version = common.strhex(reply[1:3]) o_version = f"{o_version[0:2]}.{o_version[2:4]}" @@ -151,7 +151,7 @@ def set_3leds(self, device: Device, battery_level=None, charging=None, warning=N if not device.online: return - if REGISTERS.three_leds not in device.registers: + if Registers.THREE_LEDS not in device.registers: return if battery_level is not None: @@ -185,10 +185,10 @@ def set_3leds(self, device: Device, battery_level=None, charging=None, warning=N # turn off all leds v1, v2 = 0x11, 0x11 - write_register(device, REGISTERS.three_leds, v1, v2) + write_register(device, Registers.THREE_LEDS, v1, v2) def get_notification_flags(self, device: Device): - return self._get_register(device, REGISTERS.notifications) + return self._get_register(device, Registers.NOTIFICATIONS) def set_notification_flags(self, device: Device, *flag_bits): assert device is not None @@ -202,13 +202,13 @@ def set_notification_flags(self, device: Device, *flag_bits): flag_bits = sum(int(b) for b in flag_bits) assert flag_bits & 0x00FFFFFF == flag_bits - result = write_register(device, REGISTERS.notifications, common.int2bytes(flag_bits, 3)) + result = write_register(device, Registers.NOTIFICATIONS, common.int2bytes(flag_bits, 3)) return result is not None def get_device_features(self, device: Device): - return self._get_register(device, REGISTERS.mouse_button_flags) + return self._get_register(device, Registers.MOUSE_BUTTON_FLAGS) - def _get_register(self, device: Device, register): + def _get_register(self, device: Device, register: Registers | int): assert device is not None # Avoid a call if the device is not online, @@ -224,7 +224,7 @@ def _get_register(self, device: Device, register): return common.bytes2int(flags) -def parse_battery_status(register, reply) -> Battery | None: +def parse_battery_status(register: Registers | int, reply) -> Battery | None: def status_byte_to_charge(status_byte_: int) -> BatteryLevelApproximation: if status_byte_ == 7: charge_ = BatteryLevelApproximation.FULL @@ -262,14 +262,14 @@ def charging_byte_to_status_text(charging_byte_: int) -> BatteryStatus: status_text_ = None return status_text_ - if register == REGISTERS.battery_charge: + if register == Registers.BATTERY_CHARGE: charge = ord(reply[:1]) status_byte = ord(reply[2:3]) & 0xF0 battery_status = status_byte_to_battery_status(status_byte) return Battery(charge, None, battery_status, None) - if register == REGISTERS.battery_status: + if register == Registers.BATTERY_STATUS: status_byte = ord(reply[:1]) charging_byte = ord(reply[1:2]) diff --git a/lib/logitech_receiver/hidpp10_constants.py b/lib/logitech_receiver/hidpp10_constants.py index b9ce60a3c..90b76e425 100644 --- a/lib/logitech_receiver/hidpp10_constants.py +++ b/lib/logitech_receiver/hidpp10_constants.py @@ -14,6 +14,7 @@ ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +from enum import IntEnum from .common import NamedInts @@ -103,38 +104,47 @@ PAIRING_ERRORS = NamedInts(device_timeout=0x01, device_not_supported=0x02, too_many_devices=0x03, sequence_timeout=0x06) BOLT_PAIRING_ERRORS = NamedInts(device_timeout=0x01, failed=0x02) -"""Known registers. -Devices usually have a (small) sub-set of these. Some registers are only -applicable to certain device kinds (e.g. smooth_scroll only applies to mice.""" -REGISTERS = NamedInts( + + +class Registers(IntEnum): + """Known HID registers. + + Devices usually have a (small) sub-set of these. Some registers are only + applicable to certain device kinds (e.g. smooth_scroll only applies to mice). + """ + + # Generally applicable + NOTIFICATIONS = 0x00 + FIRMWARE = 0xF1 + # only apply to receivers - receiver_connection=0x02, - receiver_pairing=0xB2, - devices_activity=0x2B3, - receiver_info=0x2B5, - bolt_device_discovery=0xC0, - bolt_pairing=0x2C1, - bolt_uniqueId=0x02FB, + RECEIVER_CONNECTION = 0x02 + RECEIVER_PAIRING = 0xB2 + DEVICES_ACTIVITY = 0x2B3 + RECEIVER_INFO = 0x2B5 + BOLT_DEVICE_DISCOVERY = 0xC0 + BOLT_PAIRING = 0x2C1 + BOLT_UNIQUE_ID = 0x02FB + # only apply to devices - mouse_button_flags=0x01, - keyboard_hand_detection=0x01, - devices_configuration=0x03, - battery_status=0x07, - keyboard_fn_swap=0x09, - battery_charge=0x0D, - keyboard_illumination=0x17, - three_leds=0x51, - mouse_dpi=0x63, - # apply to both - notifications=0x00, - firmware=0xF1, + MOUSE_BUTTON_FLAGS = 0x01 + KEYBOARD_HAND_DETECTION = 0x01 + DEVICES_CONFIGURATION = 0x03 + BATTERY_STATUS = 0x07 + KEYBOARD_FN_SWAP = 0x09 + BATTERY_CHARGE = 0x0D + KEYBOARD_ILLUMINATION = 0x17 + THREE_LEDS = 0x51 + MOUSE_DPI = 0x63 + # notifications - passkey_request_notification=0x4D, - passkey_pressed_notification=0x4E, - device_discovery_notification=0x4F, - discovery_status_notification=0x53, - pairing_status_notification=0x54, -) + PASSKEY_REQUEST_NOTIFICATION = 0x4D + PASSKEY_PRESSED_NOTIFICATION = 0x4E + DEVICE_DISCOVERY_NOTIFICATION = 0x4F + DISCOVERY_STATUS_NOTIFICATION = 0x53 + PAIRING_STATUS_NOTIFICATION = 0x54 + + # Subregisters for receiver_info register INFO_SUBREGISTERS = NamedInts( serial_number=0x01, # not found on many receivers diff --git a/lib/logitech_receiver/notifications.py b/lib/logitech_receiver/notifications.py index 0f8c68bad..7413413d9 100644 --- a/lib/logitech_receiver/notifications.py +++ b/lib/logitech_receiver/notifications.py @@ -33,12 +33,12 @@ from . import settings_templates from .common import Alert from .common import BatteryStatus +from .hidpp10_constants import Registers logger = logging.getLogger(__name__) _hidpp10 = hidpp10.Hidpp10() _hidpp20 = hidpp20.Hidpp20() -_R = hidpp10_constants.REGISTERS _F = hidpp20_constants.FEATURE @@ -74,7 +74,7 @@ def _process_receiver_notification(receiver, n): receiver.changed(reason=reason) return True - elif n.sub_id == _R.discovery_status_notification: # Bolt pairing + elif n.sub_id == Registers.DISCOVERY_STATUS_NOTIFICATION: # Bolt pairing with notification_lock: receiver.pairing.discovering = n.address == 0x00 reason = _("discovery lock is open") if receiver.pairing.discovering else _("discovery lock is closed") @@ -92,7 +92,7 @@ def _process_receiver_notification(receiver, n): receiver.changed(reason=reason) return True - elif n.sub_id == _R.device_discovery_notification: # Bolt pairing + elif n.sub_id == Registers.DISCOVERY_STATUS_NOTIFICATION: # Bolt pairing with notification_lock: counter = n.address + n.data[0] * 256 # notification counter if receiver.pairing.counter is None: @@ -108,7 +108,7 @@ def _process_receiver_notification(receiver, n): receiver.pairing.device_name = n.data[3 : 3 + n.data[2]].decode("utf-8") return True - elif n.sub_id == _R.pairing_status_notification: # Bolt pairing + elif n.sub_id == Registers.PAIRING_STATUS_NOTIFICATION: # Bolt pairing with notification_lock: receiver.pairing.device_passkey = None receiver.pairing.lock_open = n.address == 0x00 @@ -132,12 +132,12 @@ def _process_receiver_notification(receiver, n): receiver.changed(reason=reason) return True - elif n.sub_id == _R.passkey_request_notification: # Bolt pairing + elif n.sub_id == Registers.PASSKEY_REQUEST_NOTIFICATION: # Bolt pairing with notification_lock: receiver.pairing.device_passkey = n.data[0:6].decode("utf-8") return True - elif n.sub_id == _R.passkey_pressed_notification: # Bolt pairing + elif n.sub_id == Registers.PASSKEY_PRESSED_NOTIFICATION: # Bolt pairing return True logger.warning("%s: unhandled notification %s", receiver, n) @@ -214,7 +214,7 @@ def _process_hidpp10_custom_notification(device, n): if logger.isEnabledFor(logging.DEBUG): logger.debug("%s (%s) custom notification %s", device, device.protocol, n) - if n.sub_id in (_R.battery_status, _R.battery_charge): + if n.sub_id in (Registers.BATTERY_STATUS, Registers.BATTERY_CHARGE): assert n.data[-1:] == b"\x00" data = chr(n.address).encode() + n.data device.set_battery_info(hidpp10.parse_battery_status(n.sub_id, data)) diff --git a/lib/logitech_receiver/receiver.py b/lib/logitech_receiver/receiver.py index b5b91ad60..4b8a23373 100644 --- a/lib/logitech_receiver/receiver.py +++ b/lib/logitech_receiver/receiver.py @@ -36,11 +36,11 @@ from . import hidpp10_constants from .common import Alert from .device import Device +from .hidpp10_constants import Registers logger = logging.getLogger(__name__) _hidpp10 = hidpp10.Hidpp10() -_R = hidpp10_constants.REGISTERS _IR = hidpp10_constants.INFO_SUBREGISTERS @@ -111,7 +111,7 @@ def __init__(self, receiver_kind, product_info, handle, path, product_id, settin def initialize(self, product_info: dict): # read the receiver information subregister, so we can find out max_devices - serial_reply = self.read_register(_R.receiver_info, _IR.receiver_information) + serial_reply = self.read_register(Registers.RECEIVER_INFO, _IR.receiver_information) if serial_reply: self.serial = serial_reply[1:5].hex().upper() self.max_devices = serial_reply[6] @@ -146,7 +146,7 @@ def firmware(self): # how many pairings remain (None for unknown, -1 for unlimited) def remaining_pairings(self, cache=True): if self._remaining_pairings is None or not cache: - ps = self.read_register(_R.receiver_connection) + ps = self.read_register(Registers.RECEIVER_CONNECTION) if ps is not None: ps = ord(ps[2:3]) self._remaining_pairings = ps - 5 if ps >= 5 else -1 @@ -174,7 +174,7 @@ def enable_connection_notifications(self, enable=True): return flag_bits def device_codename(self, n): - codename = self.read_register(_R.receiver_info, _IR.device_name + n - 1) + codename = self.read_register(Registers.RECEIVER_INFO, _IR.device_name + n - 1) if codename: codename = codename[2 : 2 + ord(codename[1:2])] return codename.decode("ascii") @@ -182,7 +182,7 @@ def device_codename(self, n): def notify_devices(self): """Scan all devices.""" if self.handle: - if not self.write_register(_R.receiver_connection, 0x02): + if not self.write_register(Registers.RECEIVER_CONNECTION, 0x02): logger.warning("%s: failed to trigger device link notifications", self) def notification_information(self, number, notification): @@ -199,13 +199,13 @@ def device_pairing_information(self, n: int) -> dict: polling_rate = "" serial = None power_switch = "(unknown)" - pair_info = self.read_register(_R.receiver_info, _IR.pairing_information + n - 1) + pair_info = self.read_register(Registers.RECEIVER_INFO, _IR.pairing_information + n - 1) if pair_info: # a receiver that uses Unifying-style pairing registers wpid = pair_info[3:5].hex().upper() kind = hidpp10_constants.DEVICE_KIND[pair_info[7] & 0x0F] polling_rate = str(pair_info[2]) + "ms" elif not self.receiver_kind == "unifying": # may be an old Nano receiver - device_info = self.read_register(_R.receiver_info, 0x04) # undocumented + device_info = self.read_register(Registers.RECEIVER_INFO, 0x04) # undocumented if device_info: logger.warning("using undocumented register for device wpid") wpid = device_info[3:5].hex().upper() @@ -214,7 +214,7 @@ def device_pairing_information(self, n: int) -> dict: raise exceptions.NoSuchDevice(number=n, receiver=self, error="read pairing information - non-unifying") else: raise exceptions.NoSuchDevice(number=n, receiver=self, error="read pairing information") - pair_info = self.read_register(_R.receiver_info, _IR.extended_pairing_information + n - 1) + pair_info = self.read_register(Registers.RECEIVER_INFO, _IR.extended_pairing_information + n - 1) if pair_info: power_switch = hidpp10_constants.POWER_SWITCH_LOCATION[pair_info[9] & 0x0F] serial = pair_info[1:5].hex().upper() @@ -261,13 +261,13 @@ def register_new_device(self, number, notification=None): def set_lock(self, lock_closed=True, device=0, timeout=0): if self.handle: action = 0x02 if lock_closed else 0x01 - reply = self.write_register(_R.receiver_pairing, action, device, timeout) + reply = self.write_register(Registers.RECEIVER_PAIRING, action, device, timeout) if reply: return True logger.warning("%s: failed to %s the receiver lock", self, "close" if lock_closed else "open") def count(self): - count = self.read_register(_R.receiver_connection) + count = self.read_register(Registers.RECEIVER_CONNECTION) return 0 if count is None else ord(count[1:2]) def request(self, request_id, *params): @@ -344,7 +344,7 @@ def _unpair_device(self, key, force=False): def _unpair_device_per_receiver(self, key): """Receiver specific unpairing.""" - return self.write_register(_R.receiver_pairing, 0x03, key) + return self.write_register(Registers.RECEIVER_PAIRING, 0x03, key) def __len__(self): return len([d for d in self._devices.values() if d is not None]) @@ -392,18 +392,18 @@ def __init__(self, receiver_kind, product_info, handle, path, product_id, settin super().__init__(receiver_kind, product_info, handle, path, product_id, setting_callback) def initialize(self, product_info: dict): - serial_reply = self.read_register(_R.bolt_uniqueId) + serial_reply = self.read_register(Registers.BOLT_UNIQUE_ID) self.serial = serial_reply.hex().upper() self.max_devices = product_info.get("max_devices", 1) def device_codename(self, n): - codename = self.read_register(_R.receiver_info, _IR.bolt_device_name + n, 0x01) + codename = self.read_register(Registers.RECEIVER_INFO, _IR.bolt_device_name + n, 0x01) if codename: codename = codename[3 : 3 + min(14, ord(codename[2:3]))] return codename.decode("ascii") def device_pairing_information(self, n: int) -> dict: - pair_info = self.read_register(_R.receiver_info, _IR.bolt_pairing_information + n) + pair_info = self.read_register(Registers.RECEIVER_INFO, _IR.bolt_pairing_information + n) if pair_info: wpid = (pair_info[3:4] + pair_info[2:3]).hex().upper() kind = hidpp10_constants.DEVICE_KIND[pair_info[1] & 0x0F] @@ -416,7 +416,7 @@ def discover(self, cancel=False, timeout=30): """Discover Logitech Bolt devices.""" if self.handle: action = 0x02 if cancel else 0x01 - reply = self.write_register(_R.bolt_device_discovery, timeout, action) + reply = self.write_register(Registers.BOLT_DEVICE_DISCOVERY, timeout, action) if reply: return True logger.warning("%s: failed to %s device discovery", self, "cancel" if cancel else "start") @@ -425,14 +425,14 @@ def pair_device(self, pair=True, slot=0, address=b"\0\0\0\0\0\0", authentication """Pair a Bolt device.""" if self.handle: action = 0x01 if pair is True else 0x03 if pair is False else 0x02 - reply = self.write_register(_R.bolt_pairing, action, slot, address, authentication, entropy) + reply = self.write_register(Registers.BOLT_PAIRING, action, slot, address, authentication, entropy) if reply: return True logger.warning("%s: failed to %s device %s", self, "pair" if pair else "unpair", address) def _unpair_device_per_receiver(self, key): """Receiver specific unpairing.""" - return self.write_register(_R.bolt_pairing, 0x03, key) + return self.write_register(Registers.BOLT_PAIRING, 0x03, key) class UnifyingReceiver(Receiver): diff --git a/lib/logitech_receiver/settings.py b/lib/logitech_receiver/settings.py index 4feafbbaa..c9b816d87 100644 --- a/lib/logitech_receiver/settings.py +++ b/lib/logitech_receiver/settings.py @@ -610,7 +610,7 @@ class RegisterRW: kind = NamedInt(0x01, _("register")) - def __init__(self, register): + def __init__(self, register: int): assert isinstance(register, int) self.register = register diff --git a/lib/logitech_receiver/settings_templates.py b/lib/logitech_receiver/settings_templates.py index 7730387ca..f07dcaf81 100644 --- a/lib/logitech_receiver/settings_templates.py +++ b/lib/logitech_receiver/settings_templates.py @@ -33,12 +33,12 @@ from . import notify from . import settings from . import special_keys +from .hidpp10_constants import Registers logger = logging.getLogger(__name__) _hidpp20 = hidpp20.Hidpp20() _DK = hidpp10_constants.DEVICE_KIND -_R = hidpp10_constants.REGISTERS _F = hidpp20_constants.FEATURE _GG = hidpp20_constants.GESTURE @@ -137,7 +137,7 @@ class RegisterHandDetection(settings.Setting): name = "hand-detection" label = _("Hand Detection") description = _("Turn on illumination when the hands hover over the keyboard.") - register = _R.keyboard_hand_detection + register = Registers.KEYBOARD_HAND_DETECTION validator_options = {"true_value": b"\x00\x00\x00", "false_value": b"\x00\x00\x30", "mask": b"\x00\x00\xff"} @@ -145,7 +145,7 @@ class RegisterSmoothScroll(settings.Setting): name = "smooth-scroll" label = _("Scroll Wheel Smooth Scrolling") description = _("High-sensitivity mode for vertical scroll with the wheel.") - register = _R.mouse_button_flags + register = Registers.MOUSE_BUTTON_FLAGS validator_options = {"true_value": 0x40, "mask": 0x40} @@ -156,7 +156,7 @@ class RegisterSideScroll(settings.Setting): "When disabled, pushing the wheel sideways sends custom button events\n" "instead of the standard side-scrolling events." ) - register = _R.mouse_button_flags + register = Registers.MOUSE_BUTTON_FLAGS validator_options = {"true_value": 0x02, "mask": 0x02} @@ -165,14 +165,14 @@ class RegisterDpi(settings.Setting): name = "dpi-old" label = _("Sensitivity (DPI - older mice)") description = _("Mouse movement sensitivity") - register = _R.mouse_dpi + register = Registers.MOUSE_DPI choices_universe = common.NamedInts.range(0x81, 0x8F, lambda x: str((x - 0x80) * 100)) validator_class = settings.ChoicesValidator validator_options = {"choices": choices_universe} class RegisterFnSwap(FnSwapVirtual): - register = _R.keyboard_fn_swap + register = Registers.KEYBOARD_FN_SWAP validator_options = {"true_value": b"\x00\x01", "mask": b"\x00\x01"} diff --git a/lib/solaar/cli/pair.py b/lib/solaar/cli/pair.py index c39cdaf55..6a08a7e37 100644 --- a/lib/solaar/cli/pair.py +++ b/lib/solaar/cli/pair.py @@ -22,7 +22,6 @@ from logitech_receiver import notifications as _notifications _hidpp10 = hidpp10.Hidpp10() -_R = _hidpp10_constants.REGISTERS def run(receivers, args, find_receiver, _ignore): diff --git a/lib/solaar/cli/probe.py b/lib/solaar/cli/probe.py index 31e3d055c..86a8645b7 100644 --- a/lib/solaar/cli/probe.py +++ b/lib/solaar/cli/probe.py @@ -17,12 +17,11 @@ from logitech_receiver import base as _base from logitech_receiver import hidpp10_constants as _hidpp10_constants from logitech_receiver.common import strhex as _strhex +from logitech_receiver.hidpp10_constants import Registers as Reg from solaar.cli.show import _print_device from solaar.cli.show import _print_receiver -_R = _hidpp10_constants.REGISTERS - def run(receivers, args, find_receiver, _ignore): assert receivers @@ -45,37 +44,37 @@ def run(receivers, args, find_receiver, _ignore): print("") print(" Register Dump") - rgst = receiver.read_register(_R.notifications) - print(" Notifications %#04x: %s" % (_R.notifications % 0x100, "0x" + _strhex(rgst) if rgst else "None")) - rgst = receiver.read_register(_R.receiver_connection) - print(" Connection State %#04x: %s" % (_R.receiver_connection % 0x100, "0x" + _strhex(rgst) if rgst else "None")) - rgst = receiver.read_register(_R.devices_activity) - print(" Device Activity %#04x: %s" % (_R.devices_activity % 0x100, "0x" + _strhex(rgst) if rgst else "None")) + rgst = receiver.read_register(Reg.NOTIFICATIONS) + print(" Notifications %#04x: %s" % (Reg.NOTIFICATIONS % 0x100, "0x" + _strhex(rgst) if rgst else "None")) + rgst = receiver.read_register(Reg.RECEIVER_CONNECTION) + print(" Connection State %#04x: %s" % (Reg.RECEIVER_CONNECTION % 0x100, "0x" + _strhex(rgst) if rgst else "None")) + rgst = receiver.read_register(Reg.DEVICES_ACTIVITY) + print(" Device Activity %#04x: %s" % (Reg.DEVICES_ACTIVITY % 0x100, "0x" + _strhex(rgst) if rgst else "None")) for sub_reg in range(0, 16): - rgst = receiver.read_register(_R.receiver_info, sub_reg) + rgst = receiver.read_register(Reg.RECEIVER_INFO, sub_reg) print( " Pairing Register %#04x %#04x: %s" - % (_R.receiver_info % 0x100, sub_reg, "0x" + _strhex(rgst) if rgst else "None") + % (Reg.RECEIVER_INFO % 0x100, sub_reg, "0x" + _strhex(rgst) if rgst else "None") ) for device in range(0, 7): for sub_reg in [0x10, 0x20, 0x30, 0x50]: - rgst = receiver.read_register(_R.receiver_info, sub_reg + device) + rgst = receiver.read_register(Reg.RECEIVER_INFO, sub_reg + device) print( " Pairing Register %#04x %#04x: %s" - % (_R.receiver_info % 0x100, sub_reg + device, "0x" + _strhex(rgst) if rgst else "None") + % (Reg.RECEIVER_INFO % 0x100, sub_reg + device, "0x" + _strhex(rgst) if rgst else "None") ) - rgst = receiver.read_register(_R.receiver_info, 0x40 + device) + rgst = receiver.read_register(Reg.RECEIVER_INFO, 0x40 + device) print( " Pairing Name %#04x %#02x: %s" - % (_R.receiver_info % 0x100, 0x40 + device, rgst[2 : 2 + ord(rgst[1:2])] if rgst else "None") + % (Reg.RECEIVER_INFO % 0x100, 0x40 + device, rgst[2 : 2 + ord(rgst[1:2])] if rgst else "None") ) for part in range(1, 4): - rgst = receiver.read_register(_R.receiver_info, 0x60 + device, part) + rgst = receiver.read_register(Reg.RECEIVER_INFO, 0x60 + device, part) print( " Pairing Name %#04x %#02x %#02x: %2d %s" % ( - _R.receiver_info % 0x100, + Reg.RECEIVER_INFO % 0x100, 0x60 + device, part, ord(rgst[2:3]) if rgst else 0, @@ -83,10 +82,10 @@ def run(receivers, args, find_receiver, _ignore): ) ) for sub_reg in range(0, 5): - rgst = receiver.read_register(_R.firmware, sub_reg) + rgst = receiver.read_register(Reg.FIRMWARE, sub_reg) print( " Firmware %#04x %#04x: %s" - % (_R.firmware % 0x100, sub_reg, "0x" + _strhex(rgst) if rgst is not None else "None") + % (Reg.FIRMWARE % 0x100, sub_reg, "0x" + _strhex(rgst) if rgst is not None else "None") ) print("") diff --git a/lib/solaar/listener.py b/lib/solaar/listener.py index a8dfd7131..06d3f613b 100644 --- a/lib/solaar/listener.py +++ b/lib/solaar/listener.py @@ -32,6 +32,7 @@ from logitech_receiver import hidpp10_constants as _hidpp10_constants from logitech_receiver import listener as _listener from logitech_receiver import notifications as _notifications +from logitech_receiver.hidpp10_constants import Registers from . import configuration from . import dbus @@ -42,7 +43,6 @@ logger = logging.getLogger(__name__) -_R = _hidpp10_constants.REGISTERS _IR = _hidpp10_constants.INFO_SUBREGISTERS @@ -183,7 +183,7 @@ def _notifications_handler(self, n): if not already_known: if n.address == 0x0A and not self.receiver.receiver_kind == "bolt": # some Nanos send a notification even if no new pairing - check that there really is a device there - if self.receiver.read_register(_R.receiver_info, _IR.pairing_information + n.devnumber - 1) is None: + if self.receiver.read_register(Registers.RECEIVER_INFO, _IR.pairing_information + n.devnumber - 1) is None: return dev = self.receiver.register_new_device(n.devnumber, n) elif self.receiver.pairing.lock_open and self.receiver.re_pairs and not ord(n.data[0:1]) & 0x40: diff --git a/tests/logitech_receiver/test_hidpp10.py b/tests/logitech_receiver/test_hidpp10.py index 76dd303d9..536551bcd 100644 --- a/tests/logitech_receiver/test_hidpp10.py +++ b/tests/logitech_receiver/test_hidpp10.py @@ -10,6 +10,7 @@ from logitech_receiver import hidpp10 from logitech_receiver import hidpp10_constants from logitech_receiver import hidpp20_constants +from logitech_receiver.hidpp10_constants import Registers _hidpp10 = hidpp10.Hidpp10() @@ -28,7 +29,7 @@ class Device: kind: str = "fake" protocol: float = 1.0 isDevice: bool = False # incorrect, but useful here - registers: List[common.NamedInt] = field(default_factory=list) + registers: List[Registers] = field(default_factory=list) responses: List[Response] = field(default_factory=list) def request(self, id, params=None, no_reply=False): @@ -42,27 +43,25 @@ def request(self, id, params=None, no_reply=False): device_offline = Device("OFFLINE", False) -device_leds = Device( - "LEDS", True, registers=[hidpp10_constants.REGISTERS.three_leds, hidpp10_constants.REGISTERS.battery_status] -) +device_leds = Device("LEDS", True, registers=[Registers.THREE_LEDS, Registers.BATTERY_STATUS]) device_features = Device("FEATURES", True, protocol=4.5) -registers_standard = [hidpp10_constants.REGISTERS.battery_status, hidpp10_constants.REGISTERS.firmware] +registers_standard = [Registers.BATTERY_STATUS, Registers.FIRMWARE] responses_standard = [ - Response("555555", 0x8100 | hidpp10_constants.REGISTERS.battery_status, 0x00), - Response("666666", 0x8100 | hidpp10_constants.REGISTERS.battery_status, 0x10), - Response("777777", 0x8000 | hidpp10_constants.REGISTERS.battery_status, 0x00), - Response("888888", 0x8000 | hidpp10_constants.REGISTERS.battery_status, 0x10), - Response("052100", 0x8100 | hidpp10_constants.REGISTERS.battery_status, []), - Response("ABCDEF", 0x8100 | hidpp10_constants.REGISTERS.firmware, 0x01), - Response("ABCDEF", 0x8100 | hidpp10_constants.REGISTERS.firmware, 0x02), - Response("ABCDEF", 0x8100 | hidpp10_constants.REGISTERS.firmware, 0x03), - Response("ABCDEF", 0x8100 | hidpp10_constants.REGISTERS.firmware, 0x04), - Response("000900", 0x8100 | hidpp10_constants.REGISTERS.notifications, []), - Response("101010", 0x8100 | hidpp10_constants.REGISTERS.mouse_button_flags, []), - Response("010101", 0x8100 | hidpp10_constants.REGISTERS.keyboard_fn_swap, []), - Response("020202", 0x8100 | hidpp10_constants.REGISTERS.devices_configuration, []), - Response("030303", 0x8000 | hidpp10_constants.REGISTERS.devices_configuration, 0x00), + Response("555555", 0x8100 | Registers.BATTERY_STATUS, 0x00), + Response("666666", 0x8100 | Registers.BATTERY_STATUS, 0x10), + Response("777777", 0x8000 | Registers.BATTERY_STATUS, 0x00), + Response("888888", 0x8000 | Registers.BATTERY_STATUS, 0x10), + Response("052100", 0x8100 | Registers.BATTERY_STATUS, []), + Response("ABCDEF", 0x8100 | Registers.FIRMWARE, 0x01), + Response("ABCDEF", 0x8100 | Registers.FIRMWARE, 0x02), + Response("ABCDEF", 0x8100 | Registers.FIRMWARE, 0x03), + Response("ABCDEF", 0x8100 | Registers.FIRMWARE, 0x04), + Response("000900", 0x8100 | Registers.NOTIFICATIONS, []), + Response("101010", 0x8100 | Registers.MOUSE_BUTTON_FLAGS, []), + Response("010101", 0x8100 | Registers.KEYBOARD_FN_SWAP, []), + Response("020202", 0x8100 | Registers.DEVICES_CONFIGURATION, []), + Response("030303", 0x8000 | Registers.DEVICES_CONFIGURATION, 0x00), ] device_standard = Device("STANDARD", True, registers=registers_standard, responses=responses_standard) @@ -70,10 +69,10 @@ def request(self, id, params=None, no_reply=False): @pytest.mark.parametrize( "device, register, param, expected_result", [ - (device_offline, hidpp10_constants.REGISTERS.three_leds, 0x00, None), - (device_standard, hidpp10_constants.REGISTERS.three_leds, 0x00, None), - (device_standard, hidpp10_constants.REGISTERS.battery_status, 0x00, "555555"), - (device_standard, hidpp10_constants.REGISTERS.battery_status, 0x10, "666666"), + (device_offline, Registers.THREE_LEDS, 0x00, None), + (device_standard, Registers.THREE_LEDS, 0x00, None), + (device_standard, Registers.BATTERY_STATUS, 0x00, "555555"), + (device_standard, Registers.BATTERY_STATUS, 0x10, "666666"), ], ) def test_read_register(device, register, param, expected_result, mocker): @@ -88,10 +87,10 @@ def test_read_register(device, register, param, expected_result, mocker): @pytest.mark.parametrize( "device, register, param, expected_result", [ - (device_offline, hidpp10_constants.REGISTERS.three_leds, 0x00, None), - (device_standard, hidpp10_constants.REGISTERS.three_leds, 0x00, None), - (device_standard, hidpp10_constants.REGISTERS.battery_status, 0x00, "777777"), - (device_standard, hidpp10_constants.REGISTERS.battery_status, 0x10, "888888"), + (device_offline, Registers.THREE_LEDS, 0x00, None), + (device_standard, Registers.THREE_LEDS, 0x00, None), + (device_standard, Registers.BATTERY_STATUS, 0x00, "777777"), + (device_standard, Registers.BATTERY_STATUS, 0x10, "888888"), ], ) def test_write_register(device, register, param, expected_result, mocker): @@ -104,7 +103,7 @@ def test_write_register(device, register, param, expected_result, mocker): def device_charge(name, response): - responses = [Response(response, 0x8100 | hidpp10_constants.REGISTERS.battery_charge, [])] + responses = [Response(response, 0x8100 | Registers.BATTERY_CHARGE, [])] return Device(name, registers=[], responses=responses) @@ -115,7 +114,7 @@ def device_charge(name, response): def device_status(name, response): - responses = [Response(response, 0x8100 | hidpp10_constants.REGISTERS.battery_status, [])] + responses = [Response(response, 0x8100 | Registers.BATTERY_STATUS, [])] return Device(name, registers=[], responses=responses) @@ -136,53 +135,49 @@ def device_status(name, response): ( device_standard, common.Battery(common.BatteryLevelApproximation.GOOD, None, common.BatteryStatus.RECHARGING, None), - hidpp10_constants.REGISTERS.battery_status, - ), - ( - device_charge1, - common.Battery(0x55, None, common.BatteryStatus.DISCHARGING, None), - hidpp10_constants.REGISTERS.battery_charge, + Registers.BATTERY_STATUS, ), + (device_charge1, common.Battery(0x55, None, common.BatteryStatus.DISCHARGING, None), Registers.BATTERY_CHARGE), ( device_charge2, common.Battery(0x44, None, common.BatteryStatus.RECHARGING, None), - hidpp10_constants.REGISTERS.battery_charge, + Registers.BATTERY_CHARGE, ), ( device_charge3, common.Battery(0x60, None, common.BatteryStatus.FULL, None), - hidpp10_constants.REGISTERS.battery_charge, + Registers.BATTERY_CHARGE, ), - (device_charge4, common.Battery(0x22, None, None, None), hidpp10_constants.REGISTERS.battery_charge), + (device_charge4, common.Battery(0x22, None, None, None), Registers.BATTERY_CHARGE), ( device_status1, common.Battery(common.BatteryLevelApproximation.FULL, None, common.BatteryStatus.FULL, None), - hidpp10_constants.REGISTERS.battery_status, + Registers.BATTERY_STATUS, ), ( device_status2, common.Battery(common.BatteryLevelApproximation.GOOD, None, common.BatteryStatus.RECHARGING, None), - hidpp10_constants.REGISTERS.battery_status, + Registers.BATTERY_STATUS, ), ( device_status3, common.Battery(common.BatteryLevelApproximation.LOW, None, common.BatteryStatus.FULL, None), - hidpp10_constants.REGISTERS.battery_status, + Registers.BATTERY_STATUS, ), ( device_status4, common.Battery(common.BatteryLevelApproximation.CRITICAL, None, None, None), - hidpp10_constants.REGISTERS.battery_status, + Registers.BATTERY_STATUS, ), ( device_status5, common.Battery(common.BatteryLevelApproximation.EMPTY, None, common.BatteryStatus.DISCHARGING, None), - hidpp10_constants.REGISTERS.battery_status, + Registers.BATTERY_STATUS, ), ( device_status6, common.Battery(None, None, common.BatteryStatus.FULL, None), - hidpp10_constants.REGISTERS.battery_status, + Registers.BATTERY_STATUS, ), ], ) @@ -227,7 +222,7 @@ def test_set_3leds(device, level, charging, warning, p1, p2, mocker): _hidpp10.set_3leds(device, level, charging, warning) - spy_request.assert_called_once_with(0x8000 | hidpp10_constants.REGISTERS.three_leds, p1, p2) + spy_request.assert_called_once_with(0x8000 | Registers.THREE_LEDS, p1, p2) @pytest.mark.parametrize("device", [(device_offline), (device_features)]) @@ -254,7 +249,7 @@ def test_set_notification_flags(mocker): device, hidpp10_constants.NOTIFICATION_FLAG.battery_status, hidpp10_constants.NOTIFICATION_FLAG.wireless ) - spy_request.assert_called_once_with(0x8000 | hidpp10_constants.REGISTERS.notifications, b"\x10\x01\x00") + spy_request.assert_called_once_with(0x8000 | Registers.NOTIFICATIONS, b"\x10\x01\x00") assert result is not None @@ -279,10 +274,10 @@ def test_get_device_features(): @pytest.mark.parametrize( "device, register, expected_result", [ - (device_standard, hidpp10_constants.REGISTERS.battery_status, "052100"), - (device_standard, hidpp10_constants.REGISTERS.mouse_button_flags, "101010"), - (device_standard, hidpp10_constants.REGISTERS.keyboard_illumination, None), - (device_features, hidpp10_constants.REGISTERS.keyboard_illumination, None), + (device_standard, Registers.BATTERY_STATUS, "052100"), + (device_standard, Registers.MOUSE_BUTTON_FLAGS, "101010"), + (device_standard, Registers.KEYBOARD_ILLUMINATION, None), + (device_features, Registers.KEYBOARD_ILLUMINATION, None), ], ) def test_get_register(device, register, expected_result):