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
This commit is contained in:
MattHag 2024-06-02 16:34:00 +02:00 committed by GitHub
parent c23ebcd267
commit be83dac209
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 213 additions and 209 deletions

View File

@ -26,10 +26,13 @@
from solaar.i18n import _ 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 # max_devices is only used for receivers that do not support reading from Registers.RECEIVER_INFO offset 0x03, default
# may_unpair is only used for receivers that do not support reading from _R.receiver_info offset 0x03, default to False # to 1.
# unpair is for receivers that do support reading from _R.receiver_info offset 0x03, no default # may_unpair is only used for receivers that do not support reading from Registers.RECEIVER_INFO offset 0x03,
## should this last be changed so that may_unpair is used for all receivers? writing to _R.receiver_pairing doesn't seem right # 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 # 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? ## currently only one receiver is so marked - should there be more?

View File

@ -25,7 +25,7 @@ An entry should only be added to fix problems, such as
""" """
from .hidpp10_constants import DEVICE_KIND from .hidpp10_constants import DEVICE_KIND
from .hidpp10_constants import REGISTERS as REG from .hidpp10_constants import Registers as Reg
class _DeviceDescriptor: class _DeviceDescriptor:
@ -193,24 +193,24 @@ def get_btid(btid):
# Keyboards # Keyboards
_D("Wireless Keyboard EX110", codename="EX110", protocol=1.0, wpid="0055", 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 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 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 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("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("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 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 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 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 MK320", codename="MK320", protocol=1.0, wpid="200F", registers=(Reg.BATTERY_STATUS,))
_D( _D(
"Wireless Illuminated Keyboard K800", "Wireless Illuminated Keyboard K800",
codename="K800", codename="K800",
protocol=1.0, protocol=1.0,
wpid="2010", 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 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 K270 (unifying)", codename="K270", protocol=2.0, wpid="4003")
_D("Wireless Keyboard K360", codename="K360", protocol=2.0, wpid="4004") _D("Wireless Keyboard K360", codename="K360", protocol=2.0, wpid="4004")
@ -235,57 +235,57 @@ _D("K845 Mechanical Keyboard", codename="K845", usbid=0xC341, interface=3)
# Mice # Mice
_D("LX5 Cordless Mouse", codename="LX5", protocol=1.0, wpid="0036", 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("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 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 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("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("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("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("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("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("V450 Laser Cordless Mouse", codename="V450", protocol=1.0, wpid="1005", registers=(Reg.BATTERY_STATUS,))
_D( _D(
"VX Revolution", "VX Revolution",
codename="VX Revolution", codename="VX Revolution",
kind=DEVICE_KIND.mouse, kind=DEVICE_KIND.mouse,
protocol=1.0, protocol=1.0,
wpid=("1006", "100D", "0612"), 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( _D(
"MX Revolution", "MX Revolution",
codename="MX Revolution", codename="MX Revolution",
protocol=1.0, protocol=1.0,
kind=DEVICE_KIND.mouse, kind=DEVICE_KIND.mouse,
wpid=("1008", "100C"), 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("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("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("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("V550 Nano Cordless Laser Mouse", codename="V550 Nano", protocol=1.0, wpid="1013", registers=(Reg.BATTERY_CHARGE,))
_D( _D(
"MX 1100 Cordless Laser Mouse", "MX 1100 Cordless Laser Mouse",
codename="MX 1100", codename="MX 1100",
protocol=1.0, protocol=1.0,
kind=DEVICE_KIND.mouse, kind=DEVICE_KIND.mouse,
wpid="1014", 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( _D(
"Performance Mouse MX", "Performance Mouse MX",
codename="Performance MX", codename="Performance MX",
protocol=1.0, protocol=1.0,
wpid="101A", 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("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 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 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 M305", codename="M305", protocol=1.0, wpid="101F", registers=(Reg.BATTERY_STATUS,))
_D("Wireless Mouse M215", codename="M215", protocol=1.0, wpid="1020") _D("Wireless Mouse M215", codename="M215", protocol=1.0, wpid="1020")
_D( _D(
"G700 Gaming Mouse", "G700 Gaming Mouse",
@ -295,12 +295,12 @@ _D(
usbid=0xC06B, usbid=0xC06B,
interface=1, interface=1,
registers=( registers=(
REG.battery_status, Reg.BATTERY_STATUS,
REG.three_leds, Reg.THREE_LEDS,
), ),
) )
_D("Wireless Mouse M310", codename="M310", protocol=1.0, wpid="1024", 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("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("Fujitsu Sonic Mouse", codename="Sonic", protocol=1.0, wpid="1029")
_D( _D(
"G700s Gaming Mouse", "G700s Gaming Mouse",
@ -310,8 +310,8 @@ _D(
usbid=0xC07C, usbid=0xC07C,
interface=1, interface=1,
registers=( registers=(
REG.battery_status, Reg.BATTERY_STATUS,
REG.three_leds, Reg.THREE_LEDS,
), ),
) )
_D("Couch Mouse M515", codename="M515", protocol=2.0, wpid="4007") _D("Couch Mouse M515", codename="M515", protocol=2.0, wpid="4007")

View File

@ -44,8 +44,6 @@ logger = logging.getLogger(__name__)
_hidpp10 = hidpp10.Hidpp10() _hidpp10 = hidpp10.Hidpp10()
_hidpp20 = hidpp20.Hidpp20() _hidpp20 = hidpp20.Hidpp20()
_R = hidpp10_constants.REGISTERS
_IR = hidpp10_constants.INFO_SUBREGISTERS
class LowLevelInterface(Protocol): class LowLevelInterface(Protocol):

View File

@ -25,7 +25,7 @@ from . import common
from .common import Battery from .common import Battery
from .common import BatteryLevelApproximation from .common import BatteryLevelApproximation
from .common import BatteryStatus from .common import BatteryStatus
from .hidpp10_constants import REGISTERS from .hidpp10_constants import Registers
from .hidpp20_constants import FIRMWARE_KIND from .hidpp20_constants import FIRMWARE_KIND
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -52,30 +52,30 @@ class Device(Protocol):
... ...
def read_register(device: Device, register_number, *params): def read_register(device: Device, register: Registers | int, *params) -> Any:
assert device is not None, f"tried to read register {register_number:02X} from invalid device {device}" 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 # 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) return device.request(request_id, *params)
def write_register(device: Device, register_number, *value): def write_register(device: Device, register: Registers | int, *value) -> Any:
assert device is not None, f"tried to write register {register_number:02X} to invalid device {device}" 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 # 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) return device.request(request_id, *value)
def get_configuration_pending_flags(receiver): def get_configuration_pending_flags(receiver):
assert not receiver.isDevice assert not receiver.isDevice
result = read_register(receiver, REGISTERS.devices_configuration) result = read_register(receiver, Registers.DEVICES_CONFIGURATION)
if result is not None: if result is not None:
return ord(result[:1]) return ord(result[:1])
def set_configuration_pending_flags(receiver, devices): def set_configuration_pending_flags(receiver, devices):
assert not receiver.isDevice 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 return result is not None
@ -90,7 +90,7 @@ class Hidpp10:
# let's just assume HID++ 2.0 devices do not provide the battery info in a register # let's just assume HID++ 2.0 devices do not provide the battery info in a register
return return
for r in (REGISTERS.battery_status, REGISTERS.battery_charge): for r in (Registers.BATTERY_STATUS, Registers.BATTERY_CHARGE):
if r in device.registers: if r in device.registers:
reply = read_register(device, r) reply = read_register(device, r)
if reply: if reply:
@ -98,44 +98,44 @@ class Hidpp10:
return return
# the descriptor does not tell us which register this device has, try them both # 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: if reply:
# remember this for the next time # remember this for the next time
device.registers.append(REGISTERS.battery_charge) device.registers.append(Registers.BATTERY_CHARGE)
return parse_battery_status(REGISTERS.battery_charge, reply) return parse_battery_status(Registers.BATTERY_CHARGE, reply)
reply = read_register(device, REGISTERS.battery_status) reply = read_register(device, Registers.BATTERY_STATUS)
if reply: if reply:
# remember this for the next time # remember this for the next time
device.registers.append(REGISTERS.battery_status) device.registers.append(Registers.BATTERY_STATUS)
return parse_battery_status(REGISTERS.battery_status, reply) return parse_battery_status(Registers.BATTERY_STATUS, reply)
def get_firmware(self, device: Device): def get_firmware(self, device: Device):
assert device is not None assert device is not None
firmware = [None, None, None] firmware = [None, None, None]
reply = read_register(device, REGISTERS.firmware, 0x01) reply = read_register(device, Registers.FIRMWARE, 0x01)
if not reply: if not reply:
# won't be able to read any of it now... # won't be able to read any of it now...
return return
fw_version = common.strhex(reply[1:3]) fw_version = common.strhex(reply[1:3])
fw_version = f"{fw_version[0:2]}.{fw_version[2:4]}" 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: if reply:
fw_version += ".B" + common.strhex(reply[1:3]) fw_version += ".B" + common.strhex(reply[1:3])
fw = common.FirmwareInfo(FIRMWARE_KIND.Firmware, "", fw_version, None) fw = common.FirmwareInfo(FIRMWARE_KIND.Firmware, "", fw_version, None)
firmware[0] = fw firmware[0] = fw
reply = read_register(device, REGISTERS.firmware, 0x04) reply = read_register(device, Registers.FIRMWARE, 0x04)
if reply: if reply:
bl_version = common.strhex(reply[1:3]) bl_version = common.strhex(reply[1:3])
bl_version = f"{bl_version[0:2]}.{bl_version[2:4]}" bl_version = f"{bl_version[0:2]}.{bl_version[2:4]}"
bl = common.FirmwareInfo(FIRMWARE_KIND.Bootloader, "", bl_version, None) bl = common.FirmwareInfo(FIRMWARE_KIND.Bootloader, "", bl_version, None)
firmware[1] = bl firmware[1] = bl
reply = read_register(device, REGISTERS.firmware, 0x03) reply = read_register(device, Registers.FIRMWARE, 0x03)
if reply: if reply:
o_version = common.strhex(reply[1:3]) o_version = common.strhex(reply[1:3])
o_version = f"{o_version[0:2]}.{o_version[2:4]}" o_version = f"{o_version[0:2]}.{o_version[2:4]}"
@ -151,7 +151,7 @@ class Hidpp10:
if not device.online: if not device.online:
return return
if REGISTERS.three_leds not in device.registers: if Registers.THREE_LEDS not in device.registers:
return return
if battery_level is not None: if battery_level is not None:
@ -185,10 +185,10 @@ class Hidpp10:
# turn off all leds # turn off all leds
v1, v2 = 0x11, 0x11 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): 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): def set_notification_flags(self, device: Device, *flag_bits):
assert device is not None assert device is not None
@ -202,13 +202,13 @@ class Hidpp10:
flag_bits = sum(int(b) for b in flag_bits) flag_bits = sum(int(b) for b in flag_bits)
assert flag_bits & 0x00FFFFFF == 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 return result is not None
def get_device_features(self, device: Device): 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 assert device is not None
# Avoid a call if the device is not online, # Avoid a call if the device is not online,
@ -224,7 +224,7 @@ class Hidpp10:
return common.bytes2int(flags) 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: def status_byte_to_charge(status_byte_: int) -> BatteryLevelApproximation:
if status_byte_ == 7: if status_byte_ == 7:
charge_ = BatteryLevelApproximation.FULL charge_ = BatteryLevelApproximation.FULL
@ -262,14 +262,14 @@ def parse_battery_status(register, reply) -> Battery | None:
status_text_ = None status_text_ = None
return status_text_ return status_text_
if register == REGISTERS.battery_charge: if register == Registers.BATTERY_CHARGE:
charge = ord(reply[:1]) charge = ord(reply[:1])
status_byte = ord(reply[2:3]) & 0xF0 status_byte = ord(reply[2:3]) & 0xF0
battery_status = status_byte_to_battery_status(status_byte) battery_status = status_byte_to_battery_status(status_byte)
return Battery(charge, None, battery_status, None) return Battery(charge, None, battery_status, None)
if register == REGISTERS.battery_status: if register == Registers.BATTERY_STATUS:
status_byte = ord(reply[:1]) status_byte = ord(reply[:1])
charging_byte = ord(reply[1:2]) charging_byte = ord(reply[1:2])

View File

@ -14,6 +14,7 @@
## You should have received a copy of the GNU General Public License along ## 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., ## with this program; if not, write to the Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from enum import IntEnum
from .common import NamedInts from .common import NamedInts
@ -103,38 +104,47 @@ ERROR = NamedInts(
PAIRING_ERRORS = NamedInts(device_timeout=0x01, device_not_supported=0x02, too_many_devices=0x03, sequence_timeout=0x06) 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) 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.""" class Registers(IntEnum):
REGISTERS = NamedInts( """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 # only apply to receivers
receiver_connection=0x02, RECEIVER_CONNECTION = 0x02
receiver_pairing=0xB2, RECEIVER_PAIRING = 0xB2
devices_activity=0x2B3, DEVICES_ACTIVITY = 0x2B3
receiver_info=0x2B5, RECEIVER_INFO = 0x2B5
bolt_device_discovery=0xC0, BOLT_DEVICE_DISCOVERY = 0xC0
bolt_pairing=0x2C1, BOLT_PAIRING = 0x2C1
bolt_uniqueId=0x02FB, BOLT_UNIQUE_ID = 0x02FB
# only apply to devices # only apply to devices
mouse_button_flags=0x01, MOUSE_BUTTON_FLAGS = 0x01
keyboard_hand_detection=0x01, KEYBOARD_HAND_DETECTION = 0x01
devices_configuration=0x03, DEVICES_CONFIGURATION = 0x03
battery_status=0x07, BATTERY_STATUS = 0x07
keyboard_fn_swap=0x09, KEYBOARD_FN_SWAP = 0x09
battery_charge=0x0D, BATTERY_CHARGE = 0x0D
keyboard_illumination=0x17, KEYBOARD_ILLUMINATION = 0x17
three_leds=0x51, THREE_LEDS = 0x51
mouse_dpi=0x63, MOUSE_DPI = 0x63
# apply to both
notifications=0x00,
firmware=0xF1,
# notifications # notifications
passkey_request_notification=0x4D, PASSKEY_REQUEST_NOTIFICATION = 0x4D
passkey_pressed_notification=0x4E, PASSKEY_PRESSED_NOTIFICATION = 0x4E
device_discovery_notification=0x4F, DEVICE_DISCOVERY_NOTIFICATION = 0x4F
discovery_status_notification=0x53, DISCOVERY_STATUS_NOTIFICATION = 0x53
pairing_status_notification=0x54, PAIRING_STATUS_NOTIFICATION = 0x54
)
# Subregisters for receiver_info register # Subregisters for receiver_info register
INFO_SUBREGISTERS = NamedInts( INFO_SUBREGISTERS = NamedInts(
serial_number=0x01, # not found on many receivers serial_number=0x01, # not found on many receivers

View File

@ -33,12 +33,12 @@ from . import hidpp20_constants
from . import settings_templates from . import settings_templates
from .common import Alert from .common import Alert
from .common import BatteryStatus from .common import BatteryStatus
from .hidpp10_constants import Registers
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_hidpp10 = hidpp10.Hidpp10() _hidpp10 = hidpp10.Hidpp10()
_hidpp20 = hidpp20.Hidpp20() _hidpp20 = hidpp20.Hidpp20()
_R = hidpp10_constants.REGISTERS
_F = hidpp20_constants.FEATURE _F = hidpp20_constants.FEATURE
@ -74,7 +74,7 @@ def _process_receiver_notification(receiver, n):
receiver.changed(reason=reason) receiver.changed(reason=reason)
return True 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: with notification_lock:
receiver.pairing.discovering = n.address == 0x00 receiver.pairing.discovering = n.address == 0x00
reason = _("discovery lock is open") if receiver.pairing.discovering else _("discovery lock is closed") 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) receiver.changed(reason=reason)
return True 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: with notification_lock:
counter = n.address + n.data[0] * 256 # notification counter counter = n.address + n.data[0] * 256 # notification counter
if receiver.pairing.counter is None: 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") receiver.pairing.device_name = n.data[3 : 3 + n.data[2]].decode("utf-8")
return True 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: with notification_lock:
receiver.pairing.device_passkey = None receiver.pairing.device_passkey = None
receiver.pairing.lock_open = n.address == 0x00 receiver.pairing.lock_open = n.address == 0x00
@ -132,12 +132,12 @@ def _process_receiver_notification(receiver, n):
receiver.changed(reason=reason) receiver.changed(reason=reason)
return True 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: with notification_lock:
receiver.pairing.device_passkey = n.data[0:6].decode("utf-8") receiver.pairing.device_passkey = n.data[0:6].decode("utf-8")
return True return True
elif n.sub_id == _R.passkey_pressed_notification: # Bolt pairing elif n.sub_id == Registers.PASSKEY_PRESSED_NOTIFICATION: # Bolt pairing
return True return True
logger.warning("%s: unhandled notification %s", receiver, n) logger.warning("%s: unhandled notification %s", receiver, n)
@ -214,7 +214,7 @@ def _process_hidpp10_custom_notification(device, n):
if logger.isEnabledFor(logging.DEBUG): if logger.isEnabledFor(logging.DEBUG):
logger.debug("%s (%s) custom notification %s", device, device.protocol, n) 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" assert n.data[-1:] == b"\x00"
data = chr(n.address).encode() + n.data data = chr(n.address).encode() + n.data
device.set_battery_info(hidpp10.parse_battery_status(n.sub_id, data)) device.set_battery_info(hidpp10.parse_battery_status(n.sub_id, data))

View File

@ -36,11 +36,11 @@ from . import hidpp10
from . import hidpp10_constants from . import hidpp10_constants
from .common import Alert from .common import Alert
from .device import Device from .device import Device
from .hidpp10_constants import Registers
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_hidpp10 = hidpp10.Hidpp10() _hidpp10 = hidpp10.Hidpp10()
_R = hidpp10_constants.REGISTERS
_IR = hidpp10_constants.INFO_SUBREGISTERS _IR = hidpp10_constants.INFO_SUBREGISTERS
@ -111,7 +111,7 @@ class Receiver:
def initialize(self, product_info: dict): def initialize(self, product_info: dict):
# read the receiver information subregister, so we can find out max_devices # 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: if serial_reply:
self.serial = serial_reply[1:5].hex().upper() self.serial = serial_reply[1:5].hex().upper()
self.max_devices = serial_reply[6] self.max_devices = serial_reply[6]
@ -146,7 +146,7 @@ class Receiver:
# how many pairings remain (None for unknown, -1 for unlimited) # how many pairings remain (None for unknown, -1 for unlimited)
def remaining_pairings(self, cache=True): def remaining_pairings(self, cache=True):
if self._remaining_pairings is None or not cache: 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: if ps is not None:
ps = ord(ps[2:3]) ps = ord(ps[2:3])
self._remaining_pairings = ps - 5 if ps >= 5 else -1 self._remaining_pairings = ps - 5 if ps >= 5 else -1
@ -174,7 +174,7 @@ class Receiver:
return flag_bits return flag_bits
def device_codename(self, n): 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: if codename:
codename = codename[2 : 2 + ord(codename[1:2])] codename = codename[2 : 2 + ord(codename[1:2])]
return codename.decode("ascii") return codename.decode("ascii")
@ -182,7 +182,7 @@ class Receiver:
def notify_devices(self): def notify_devices(self):
"""Scan all devices.""" """Scan all devices."""
if self.handle: 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) logger.warning("%s: failed to trigger device link notifications", self)
def notification_information(self, number, notification): def notification_information(self, number, notification):
@ -199,13 +199,13 @@ class Receiver:
polling_rate = "" polling_rate = ""
serial = None serial = None
power_switch = "(unknown)" 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 if pair_info: # a receiver that uses Unifying-style pairing registers
wpid = pair_info[3:5].hex().upper() wpid = pair_info[3:5].hex().upper()
kind = hidpp10_constants.DEVICE_KIND[pair_info[7] & 0x0F] kind = hidpp10_constants.DEVICE_KIND[pair_info[7] & 0x0F]
polling_rate = str(pair_info[2]) + "ms" polling_rate = str(pair_info[2]) + "ms"
elif not self.receiver_kind == "unifying": # may be an old Nano receiver 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: if device_info:
logger.warning("using undocumented register for device wpid") logger.warning("using undocumented register for device wpid")
wpid = device_info[3:5].hex().upper() wpid = device_info[3:5].hex().upper()
@ -214,7 +214,7 @@ class Receiver:
raise exceptions.NoSuchDevice(number=n, receiver=self, error="read pairing information - non-unifying") raise exceptions.NoSuchDevice(number=n, receiver=self, error="read pairing information - non-unifying")
else: else:
raise exceptions.NoSuchDevice(number=n, receiver=self, error="read pairing information") 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: if pair_info:
power_switch = hidpp10_constants.POWER_SWITCH_LOCATION[pair_info[9] & 0x0F] power_switch = hidpp10_constants.POWER_SWITCH_LOCATION[pair_info[9] & 0x0F]
serial = pair_info[1:5].hex().upper() serial = pair_info[1:5].hex().upper()
@ -261,13 +261,13 @@ class Receiver:
def set_lock(self, lock_closed=True, device=0, timeout=0): def set_lock(self, lock_closed=True, device=0, timeout=0):
if self.handle: if self.handle:
action = 0x02 if lock_closed else 0x01 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: if reply:
return True return True
logger.warning("%s: failed to %s the receiver lock", self, "close" if lock_closed else "open") logger.warning("%s: failed to %s the receiver lock", self, "close" if lock_closed else "open")
def count(self): 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]) return 0 if count is None else ord(count[1:2])
def request(self, request_id, *params): def request(self, request_id, *params):
@ -344,7 +344,7 @@ class Receiver:
def _unpair_device_per_receiver(self, key): def _unpair_device_per_receiver(self, key):
"""Receiver specific unpairing.""" """Receiver specific unpairing."""
return self.write_register(_R.receiver_pairing, 0x03, key) return self.write_register(Registers.RECEIVER_PAIRING, 0x03, key)
def __len__(self): def __len__(self):
return len([d for d in self._devices.values() if d is not None]) return len([d for d in self._devices.values() if d is not None])
@ -392,18 +392,18 @@ class BoltReceiver(Receiver):
super().__init__(receiver_kind, product_info, handle, path, product_id, setting_callback) super().__init__(receiver_kind, product_info, handle, path, product_id, setting_callback)
def initialize(self, product_info: dict): 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.serial = serial_reply.hex().upper()
self.max_devices = product_info.get("max_devices", 1) self.max_devices = product_info.get("max_devices", 1)
def device_codename(self, n): 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: if codename:
codename = codename[3 : 3 + min(14, ord(codename[2:3]))] codename = codename[3 : 3 + min(14, ord(codename[2:3]))]
return codename.decode("ascii") return codename.decode("ascii")
def device_pairing_information(self, n: int) -> dict: 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: if pair_info:
wpid = (pair_info[3:4] + pair_info[2:3]).hex().upper() wpid = (pair_info[3:4] + pair_info[2:3]).hex().upper()
kind = hidpp10_constants.DEVICE_KIND[pair_info[1] & 0x0F] kind = hidpp10_constants.DEVICE_KIND[pair_info[1] & 0x0F]
@ -416,7 +416,7 @@ class BoltReceiver(Receiver):
"""Discover Logitech Bolt devices.""" """Discover Logitech Bolt devices."""
if self.handle: if self.handle:
action = 0x02 if cancel else 0x01 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: if reply:
return True return True
logger.warning("%s: failed to %s device discovery", self, "cancel" if cancel else "start") logger.warning("%s: failed to %s device discovery", self, "cancel" if cancel else "start")
@ -425,14 +425,14 @@ class BoltReceiver(Receiver):
"""Pair a Bolt device.""" """Pair a Bolt device."""
if self.handle: if self.handle:
action = 0x01 if pair is True else 0x03 if pair is False else 0x02 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: if reply:
return True return True
logger.warning("%s: failed to %s device %s", self, "pair" if pair else "unpair", address) logger.warning("%s: failed to %s device %s", self, "pair" if pair else "unpair", address)
def _unpair_device_per_receiver(self, key): def _unpair_device_per_receiver(self, key):
"""Receiver specific unpairing.""" """Receiver specific unpairing."""
return self.write_register(_R.bolt_pairing, 0x03, key) return self.write_register(Registers.BOLT_PAIRING, 0x03, key)
class UnifyingReceiver(Receiver): class UnifyingReceiver(Receiver):

View File

@ -610,7 +610,7 @@ class RegisterRW:
kind = NamedInt(0x01, _("register")) kind = NamedInt(0x01, _("register"))
def __init__(self, register): def __init__(self, register: int):
assert isinstance(register, int) assert isinstance(register, int)
self.register = register self.register = register

View File

@ -33,12 +33,12 @@ from . import hidpp20_constants
from . import notify from . import notify
from . import settings from . import settings
from . import special_keys from . import special_keys
from .hidpp10_constants import Registers
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_hidpp20 = hidpp20.Hidpp20() _hidpp20 = hidpp20.Hidpp20()
_DK = hidpp10_constants.DEVICE_KIND _DK = hidpp10_constants.DEVICE_KIND
_R = hidpp10_constants.REGISTERS
_F = hidpp20_constants.FEATURE _F = hidpp20_constants.FEATURE
_GG = hidpp20_constants.GESTURE _GG = hidpp20_constants.GESTURE
@ -137,7 +137,7 @@ class RegisterHandDetection(settings.Setting):
name = "hand-detection" name = "hand-detection"
label = _("Hand Detection") label = _("Hand Detection")
description = _("Turn on illumination when the hands hover over the keyboard.") 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"} 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" name = "smooth-scroll"
label = _("Scroll Wheel Smooth Scrolling") label = _("Scroll Wheel Smooth Scrolling")
description = _("High-sensitivity mode for vertical scroll with the wheel.") 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} 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" "When disabled, pushing the wheel sideways sends custom button events\n"
"instead of the standard side-scrolling events." "instead of the standard side-scrolling events."
) )
register = _R.mouse_button_flags register = Registers.MOUSE_BUTTON_FLAGS
validator_options = {"true_value": 0x02, "mask": 0x02} validator_options = {"true_value": 0x02, "mask": 0x02}
@ -165,14 +165,14 @@ class RegisterDpi(settings.Setting):
name = "dpi-old" name = "dpi-old"
label = _("Sensitivity (DPI - older mice)") label = _("Sensitivity (DPI - older mice)")
description = _("Mouse movement sensitivity") 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)) choices_universe = common.NamedInts.range(0x81, 0x8F, lambda x: str((x - 0x80) * 100))
validator_class = settings.ChoicesValidator validator_class = settings.ChoicesValidator
validator_options = {"choices": choices_universe} validator_options = {"choices": choices_universe}
class RegisterFnSwap(FnSwapVirtual): class RegisterFnSwap(FnSwapVirtual):
register = _R.keyboard_fn_swap register = Registers.KEYBOARD_FN_SWAP
validator_options = {"true_value": b"\x00\x01", "mask": b"\x00\x01"} validator_options = {"true_value": b"\x00\x01", "mask": b"\x00\x01"}

View File

@ -22,7 +22,6 @@ from logitech_receiver import hidpp10_constants as _hidpp10_constants
from logitech_receiver import notifications as _notifications from logitech_receiver import notifications as _notifications
_hidpp10 = hidpp10.Hidpp10() _hidpp10 = hidpp10.Hidpp10()
_R = _hidpp10_constants.REGISTERS
def run(receivers, args, find_receiver, _ignore): def run(receivers, args, find_receiver, _ignore):

View File

@ -17,12 +17,11 @@
from logitech_receiver import base as _base from logitech_receiver import base as _base
from logitech_receiver import hidpp10_constants as _hidpp10_constants from logitech_receiver import hidpp10_constants as _hidpp10_constants
from logitech_receiver.common import strhex as _strhex 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_device
from solaar.cli.show import _print_receiver from solaar.cli.show import _print_receiver
_R = _hidpp10_constants.REGISTERS
def run(receivers, args, find_receiver, _ignore): def run(receivers, args, find_receiver, _ignore):
assert receivers assert receivers
@ -45,37 +44,37 @@ def run(receivers, args, find_receiver, _ignore):
print("") print("")
print(" Register Dump") print(" Register Dump")
rgst = receiver.read_register(_R.notifications) rgst = receiver.read_register(Reg.NOTIFICATIONS)
print(" Notifications %#04x: %s" % (_R.notifications % 0x100, "0x" + _strhex(rgst) if rgst else "None")) print(" Notifications %#04x: %s" % (Reg.NOTIFICATIONS % 0x100, "0x" + _strhex(rgst) if rgst else "None"))
rgst = receiver.read_register(_R.receiver_connection) rgst = receiver.read_register(Reg.RECEIVER_CONNECTION)
print(" Connection State %#04x: %s" % (_R.receiver_connection % 0x100, "0x" + _strhex(rgst) if rgst else "None")) print(" Connection State %#04x: %s" % (Reg.RECEIVER_CONNECTION % 0x100, "0x" + _strhex(rgst) if rgst else "None"))
rgst = receiver.read_register(_R.devices_activity) rgst = receiver.read_register(Reg.DEVICES_ACTIVITY)
print(" Device Activity %#04x: %s" % (_R.devices_activity % 0x100, "0x" + _strhex(rgst) if rgst else "None")) print(" Device Activity %#04x: %s" % (Reg.DEVICES_ACTIVITY % 0x100, "0x" + _strhex(rgst) if rgst else "None"))
for sub_reg in range(0, 16): 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( print(
" Pairing Register %#04x %#04x: %s" " 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 device in range(0, 7):
for sub_reg in [0x10, 0x20, 0x30, 0x50]: 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( print(
" Pairing Register %#04x %#04x: %s" " 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( print(
" Pairing Name %#04x %#02x: %s" " 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): 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( print(
" Pairing Name %#04x %#02x %#02x: %2d %s" " Pairing Name %#04x %#02x %#02x: %2d %s"
% ( % (
_R.receiver_info % 0x100, Reg.RECEIVER_INFO % 0x100,
0x60 + device, 0x60 + device,
part, part,
ord(rgst[2:3]) if rgst else 0, 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): for sub_reg in range(0, 5):
rgst = receiver.read_register(_R.firmware, sub_reg) rgst = receiver.read_register(Reg.FIRMWARE, sub_reg)
print( print(
" Firmware %#04x %#04x: %s" " 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("") print("")

View File

@ -32,6 +32,7 @@ from logitech_receiver import exceptions
from logitech_receiver import hidpp10_constants as _hidpp10_constants from logitech_receiver import hidpp10_constants as _hidpp10_constants
from logitech_receiver import listener as _listener from logitech_receiver import listener as _listener
from logitech_receiver import notifications as _notifications from logitech_receiver import notifications as _notifications
from logitech_receiver.hidpp10_constants import Registers
from . import configuration from . import configuration
from . import dbus from . import dbus
@ -42,7 +43,6 @@ from gi.repository import GLib # NOQA: E402 # isort:skip
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_R = _hidpp10_constants.REGISTERS
_IR = _hidpp10_constants.INFO_SUBREGISTERS _IR = _hidpp10_constants.INFO_SUBREGISTERS
@ -183,7 +183,7 @@ class SolaarListener(_listener.EventsListener):
if not already_known: if not already_known:
if n.address == 0x0A and not self.receiver.receiver_kind == "bolt": 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 # 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 return
dev = self.receiver.register_new_device(n.devnumber, n) 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: elif self.receiver.pairing.lock_open and self.receiver.re_pairs and not ord(n.data[0:1]) & 0x40:

View File

@ -10,6 +10,7 @@ from logitech_receiver import common
from logitech_receiver import hidpp10 from logitech_receiver import hidpp10
from logitech_receiver import hidpp10_constants from logitech_receiver import hidpp10_constants
from logitech_receiver import hidpp20_constants from logitech_receiver import hidpp20_constants
from logitech_receiver.hidpp10_constants import Registers
_hidpp10 = hidpp10.Hidpp10() _hidpp10 = hidpp10.Hidpp10()
@ -28,7 +29,7 @@ class Device:
kind: str = "fake" kind: str = "fake"
protocol: float = 1.0 protocol: float = 1.0
isDevice: bool = False # incorrect, but useful here 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) responses: List[Response] = field(default_factory=list)
def request(self, id, params=None, no_reply=False): def request(self, id, params=None, no_reply=False):
@ -42,27 +43,25 @@ class Device:
device_offline = Device("OFFLINE", False) device_offline = Device("OFFLINE", False)
device_leds = Device( device_leds = Device("LEDS", True, registers=[Registers.THREE_LEDS, Registers.BATTERY_STATUS])
"LEDS", True, registers=[hidpp10_constants.REGISTERS.three_leds, hidpp10_constants.REGISTERS.battery_status]
)
device_features = Device("FEATURES", True, protocol=4.5) 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 = [ responses_standard = [
Response("555555", 0x8100 | hidpp10_constants.REGISTERS.battery_status, 0x00), Response("555555", 0x8100 | Registers.BATTERY_STATUS, 0x00),
Response("666666", 0x8100 | hidpp10_constants.REGISTERS.battery_status, 0x10), Response("666666", 0x8100 | Registers.BATTERY_STATUS, 0x10),
Response("777777", 0x8000 | hidpp10_constants.REGISTERS.battery_status, 0x00), Response("777777", 0x8000 | Registers.BATTERY_STATUS, 0x00),
Response("888888", 0x8000 | hidpp10_constants.REGISTERS.battery_status, 0x10), Response("888888", 0x8000 | Registers.BATTERY_STATUS, 0x10),
Response("052100", 0x8100 | hidpp10_constants.REGISTERS.battery_status, []), Response("052100", 0x8100 | Registers.BATTERY_STATUS, []),
Response("ABCDEF", 0x8100 | hidpp10_constants.REGISTERS.firmware, 0x01), Response("ABCDEF", 0x8100 | Registers.FIRMWARE, 0x01),
Response("ABCDEF", 0x8100 | hidpp10_constants.REGISTERS.firmware, 0x02), Response("ABCDEF", 0x8100 | Registers.FIRMWARE, 0x02),
Response("ABCDEF", 0x8100 | hidpp10_constants.REGISTERS.firmware, 0x03), Response("ABCDEF", 0x8100 | Registers.FIRMWARE, 0x03),
Response("ABCDEF", 0x8100 | hidpp10_constants.REGISTERS.firmware, 0x04), Response("ABCDEF", 0x8100 | Registers.FIRMWARE, 0x04),
Response("000900", 0x8100 | hidpp10_constants.REGISTERS.notifications, []), Response("000900", 0x8100 | Registers.NOTIFICATIONS, []),
Response("101010", 0x8100 | hidpp10_constants.REGISTERS.mouse_button_flags, []), Response("101010", 0x8100 | Registers.MOUSE_BUTTON_FLAGS, []),
Response("010101", 0x8100 | hidpp10_constants.REGISTERS.keyboard_fn_swap, []), Response("010101", 0x8100 | Registers.KEYBOARD_FN_SWAP, []),
Response("020202", 0x8100 | hidpp10_constants.REGISTERS.devices_configuration, []), Response("020202", 0x8100 | Registers.DEVICES_CONFIGURATION, []),
Response("030303", 0x8000 | hidpp10_constants.REGISTERS.devices_configuration, 0x00), Response("030303", 0x8000 | Registers.DEVICES_CONFIGURATION, 0x00),
] ]
device_standard = Device("STANDARD", True, registers=registers_standard, responses=responses_standard) device_standard = Device("STANDARD", True, registers=registers_standard, responses=responses_standard)
@ -70,10 +69,10 @@ device_standard = Device("STANDARD", True, registers=registers_standard, respons
@pytest.mark.parametrize( @pytest.mark.parametrize(
"device, register, param, expected_result", "device, register, param, expected_result",
[ [
(device_offline, hidpp10_constants.REGISTERS.three_leds, 0x00, None), (device_offline, Registers.THREE_LEDS, 0x00, None),
(device_standard, hidpp10_constants.REGISTERS.three_leds, 0x00, None), (device_standard, Registers.THREE_LEDS, 0x00, None),
(device_standard, hidpp10_constants.REGISTERS.battery_status, 0x00, "555555"), (device_standard, Registers.BATTERY_STATUS, 0x00, "555555"),
(device_standard, hidpp10_constants.REGISTERS.battery_status, 0x10, "666666"), (device_standard, Registers.BATTERY_STATUS, 0x10, "666666"),
], ],
) )
def test_read_register(device, register, param, expected_result, mocker): 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( @pytest.mark.parametrize(
"device, register, param, expected_result", "device, register, param, expected_result",
[ [
(device_offline, hidpp10_constants.REGISTERS.three_leds, 0x00, None), (device_offline, Registers.THREE_LEDS, 0x00, None),
(device_standard, hidpp10_constants.REGISTERS.three_leds, 0x00, None), (device_standard, Registers.THREE_LEDS, 0x00, None),
(device_standard, hidpp10_constants.REGISTERS.battery_status, 0x00, "777777"), (device_standard, Registers.BATTERY_STATUS, 0x00, "777777"),
(device_standard, hidpp10_constants.REGISTERS.battery_status, 0x10, "888888"), (device_standard, Registers.BATTERY_STATUS, 0x10, "888888"),
], ],
) )
def test_write_register(device, register, param, expected_result, mocker): 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): 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) return Device(name, registers=[], responses=responses)
@ -115,7 +114,7 @@ device_charge4 = device_charge("OTHER", "220000")
def device_status(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) return Device(name, registers=[], responses=responses)
@ -136,53 +135,49 @@ device_status6 = device_status("NOSTATUS", "002200")
( (
device_standard, device_standard,
common.Battery(common.BatteryLevelApproximation.GOOD, None, common.BatteryStatus.RECHARGING, None), common.Battery(common.BatteryLevelApproximation.GOOD, None, common.BatteryStatus.RECHARGING, None),
hidpp10_constants.REGISTERS.battery_status, Registers.BATTERY_STATUS,
),
(
device_charge1,
common.Battery(0x55, None, common.BatteryStatus.DISCHARGING, None),
hidpp10_constants.REGISTERS.battery_charge,
), ),
(device_charge1, common.Battery(0x55, None, common.BatteryStatus.DISCHARGING, None), Registers.BATTERY_CHARGE),
( (
device_charge2, device_charge2,
common.Battery(0x44, None, common.BatteryStatus.RECHARGING, None), common.Battery(0x44, None, common.BatteryStatus.RECHARGING, None),
hidpp10_constants.REGISTERS.battery_charge, Registers.BATTERY_CHARGE,
), ),
( (
device_charge3, device_charge3,
common.Battery(0x60, None, common.BatteryStatus.FULL, None), 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, device_status1,
common.Battery(common.BatteryLevelApproximation.FULL, None, common.BatteryStatus.FULL, None), common.Battery(common.BatteryLevelApproximation.FULL, None, common.BatteryStatus.FULL, None),
hidpp10_constants.REGISTERS.battery_status, Registers.BATTERY_STATUS,
), ),
( (
device_status2, device_status2,
common.Battery(common.BatteryLevelApproximation.GOOD, None, common.BatteryStatus.RECHARGING, None), common.Battery(common.BatteryLevelApproximation.GOOD, None, common.BatteryStatus.RECHARGING, None),
hidpp10_constants.REGISTERS.battery_status, Registers.BATTERY_STATUS,
), ),
( (
device_status3, device_status3,
common.Battery(common.BatteryLevelApproximation.LOW, None, common.BatteryStatus.FULL, None), common.Battery(common.BatteryLevelApproximation.LOW, None, common.BatteryStatus.FULL, None),
hidpp10_constants.REGISTERS.battery_status, Registers.BATTERY_STATUS,
), ),
( (
device_status4, device_status4,
common.Battery(common.BatteryLevelApproximation.CRITICAL, None, None, None), common.Battery(common.BatteryLevelApproximation.CRITICAL, None, None, None),
hidpp10_constants.REGISTERS.battery_status, Registers.BATTERY_STATUS,
), ),
( (
device_status5, device_status5,
common.Battery(common.BatteryLevelApproximation.EMPTY, None, common.BatteryStatus.DISCHARGING, None), common.Battery(common.BatteryLevelApproximation.EMPTY, None, common.BatteryStatus.DISCHARGING, None),
hidpp10_constants.REGISTERS.battery_status, Registers.BATTERY_STATUS,
), ),
( (
device_status6, device_status6,
common.Battery(None, None, common.BatteryStatus.FULL, None), 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) _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)]) @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 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 assert result is not None
@ -279,10 +274,10 @@ def test_get_device_features():
@pytest.mark.parametrize( @pytest.mark.parametrize(
"device, register, expected_result", "device, register, expected_result",
[ [
(device_standard, hidpp10_constants.REGISTERS.battery_status, "052100"), (device_standard, Registers.BATTERY_STATUS, "052100"),
(device_standard, hidpp10_constants.REGISTERS.mouse_button_flags, "101010"), (device_standard, Registers.MOUSE_BUTTON_FLAGS, "101010"),
(device_standard, hidpp10_constants.REGISTERS.keyboard_illumination, None), (device_standard, Registers.KEYBOARD_ILLUMINATION, None),
(device_features, hidpp10_constants.REGISTERS.keyboard_illumination, None), (device_features, Registers.KEYBOARD_ILLUMINATION, None),
], ],
) )
def test_get_register(device, register, expected_result): def test_get_register(device, register, expected_result):