logitech_receiver: Move hidpp10 constants into new module

Related #1097
This commit is contained in:
Matthias Hagmann 2024-02-14 20:36:42 +01:00 committed by Peter F. Patel-Schneider
parent e8fdbeee8e
commit 2fcab65486
10 changed files with 201 additions and 192 deletions

View File

@ -24,8 +24,8 @@
# - the device uses a USB interface other than 2 # - the device uses a USB interface other than 2
# - the name or codename should be different from what the device reports # - the name or codename should be different from what the device reports
from .hidpp10 import DEVICE_KIND as _DK from .hidpp10_constants import DEVICE_KIND as _DK
from .hidpp10 import REGISTERS as _R from .hidpp10_constants import REGISTERS as _R
# #
# #

View File

@ -30,16 +30,18 @@ from . import base as _base
from . import descriptors as _descriptors from . import descriptors as _descriptors
from . import exceptions from . import exceptions
from . import hidpp10 as _hidpp10 from . import hidpp10 as _hidpp10
from . import hidpp10_constants as _hidpp10_constants
from . import hidpp20 as _hidpp20 from . import hidpp20 as _hidpp20
from . import hidpp20_constants as _hidpp20_constants
from .common import strhex as _strhex from .common import strhex as _strhex
from .settings_templates import check_feature_settings as _check_feature_settings from .settings_templates import check_feature_settings as _check_feature_settings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_R = _hidpp10.REGISTERS _R = _hidpp10_constants.REGISTERS
_IR = _hidpp10.INFO_SUBREGISTERS _IR = _hidpp10_constants.INFO_SUBREGISTERS
KIND_MAP = {kind: _hidpp10.DEVICE_KIND[str(kind)] for kind in _hidpp20.DEVICE_KIND} KIND_MAP = {kind: _hidpp10_constants.DEVICE_KIND[str(kind)] for kind in _hidpp20_constants.DEVICE_KIND}
# #
# #
@ -124,14 +126,14 @@ class Device:
if receiver.receiver_kind == '27Mhz': # 27 Mhz receiver if receiver.receiver_kind == '27Mhz': # 27 Mhz receiver
self.wpid = '00' + _strhex(link_notification.data[2:3]) self.wpid = '00' + _strhex(link_notification.data[2:3])
kind = receiver.get_kind_from_index(number) kind = receiver.get_kind_from_index(number)
self._kind = _hidpp10.DEVICE_KIND[kind] self._kind = _hidpp10_constants.DEVICE_KIND[kind]
elif receiver.receiver_kind == '27Mhz': # 27 Mhz receiver doesn't have pairing registers elif receiver.receiver_kind == '27Mhz': # 27 Mhz receiver doesn't have pairing registers
self.wpid = _hid.find_paired_node_wpid(receiver.path, number) self.wpid = _hid.find_paired_node_wpid(receiver.path, number)
if not self.wpid: if not self.wpid:
logger.error('Unable to get wpid from udev for device %d of %s', number, receiver) logger.error('Unable to get wpid from udev for device %d of %s', number, receiver)
raise exceptions.NoSuchDevice(number=number, receiver=receiver, error='Not present 27Mhz device') raise exceptions.NoSuchDevice(number=number, receiver=receiver, error='Not present 27Mhz device')
kind = receiver.get_kind_from_index(number) kind = receiver.get_kind_from_index(number)
self._kind = _hidpp10.DEVICE_KIND[kind] self._kind = _hidpp10_constants.DEVICE_KIND[kind]
else: # get information from pairing registers else: # get information from pairing registers
self.online = True self.online = True
self.update_pairing_information() self.update_pairing_information()
@ -415,10 +417,10 @@ class Device:
if enable: if enable:
set_flag_bits = ( set_flag_bits = (
_hidpp10.NOTIFICATION_FLAG.battery_status _hidpp10_constants.NOTIFICATION_FLAG.battery_status
| _hidpp10.NOTIFICATION_FLAG.keyboard_illumination | _hidpp10_constants.NOTIFICATION_FLAG.keyboard_illumination
| _hidpp10.NOTIFICATION_FLAG.wireless | _hidpp10_constants.NOTIFICATION_FLAG.wireless
| _hidpp10.NOTIFICATION_FLAG.software_present | _hidpp10_constants.NOTIFICATION_FLAG.software_present
) )
else: else:
set_flag_bits = 0 set_flag_bits = 0
@ -427,7 +429,7 @@ class Device:
logger.warning('%s: failed to %s device notifications', self, 'enable' if enable else 'disable') logger.warning('%s: failed to %s device notifications', self, 'enable' if enable else 'disable')
flag_bits = _hidpp10.get_notification_flags(self) flag_bits = _hidpp10.get_notification_flags(self)
flag_names = None if flag_bits is None else tuple(_hidpp10.NOTIFICATION_FLAG.flag_names(flag_bits)) flag_names = None if flag_bits is None else tuple(_hidpp10_constants.NOTIFICATION_FLAG.flag_names(flag_bits))
if logger.isEnabledFor(logging.INFO): if logger.isEnabledFor(logging.INFO):
logger.info('%s: device notifications %s %s', self, 'enabled' if enable else 'disabled', flag_names) logger.info('%s: device notifications %s %s', self, 'enabled' if enable else 'disabled', flag_names)
return flag_bits if ok else None return flag_bits if ok else None

View File

@ -20,164 +20,14 @@ import logging
from .common import BATTERY_APPROX as _BATTERY_APPROX from .common import BATTERY_APPROX as _BATTERY_APPROX
from .common import FirmwareInfo as _FirmwareInfo from .common import FirmwareInfo as _FirmwareInfo
from .common import NamedInts as _NamedInts
from .common import bytes2int as _bytes2int from .common import bytes2int as _bytes2int
from .common import int2bytes as _int2bytes from .common import int2bytes as _int2bytes
from .common import strhex as _strhex from .common import strhex as _strhex
from .hidpp20 import BATTERY_STATUS, FIRMWARE_KIND from .hidpp20 import BATTERY_STATUS, FIRMWARE_KIND
from .hidpp10_constants import REGISTERS
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
#
# Constants - most of them as defined by the official Logitech HID++ 1.0
# documentation, some of them guessed.
#
DEVICE_KIND = _NamedInts(
unknown=0x00,
keyboard=0x01,
mouse=0x02,
numpad=0x03,
presenter=0x04,
remote=0x07,
trackball=0x08,
touchpad=0x09,
headset=0x0D, # not from Logitech documentation
remote_control=0x0E, # for compatibility with HID++ 2.0
receiver=0x0F # for compatibility with HID++ 2.0
)
POWER_SWITCH_LOCATION = _NamedInts(
base=0x01,
top_case=0x02,
edge_of_top_right_corner=0x03,
top_left_corner=0x05,
bottom_left_corner=0x06,
top_right_corner=0x07,
bottom_right_corner=0x08,
top_edge=0x09,
right_edge=0x0A,
left_edge=0x0B,
bottom_edge=0x0C
)
# Some flags are used both by devices and receivers. The Logitech documentation
# mentions that the first and last (third) byte are used for devices while the
# second is used for the receiver. In practise, the second byte is also used for
# some device-specific notifications (keyboard illumination level). Do not
# simply set all notification bits if the software does not support it. For
# example, enabling keyboard_sleep_raw makes the Sleep key a no-operation unless
# the software is updated to handle that event.
# Observations:
# - wireless and software present were seen on receivers, reserved_r1b4 as well
# - the rest work only on devices as far as we can tell right now
# In the future would be useful to have separate enums for receiver and device notification flags,
# but right now we don't know enough.
# additional flags taken from https://drive.google.com/file/d/0BxbRzx7vEV7eNDBheWY0UHM5dEU/view?usp=sharing
NOTIFICATION_FLAG = _NamedInts(
numpad_numerical_keys=0x800000,
f_lock_status=0x400000,
roller_H=0x200000,
battery_status=0x100000, # send battery charge notifications (0x07 or 0x0D)
mouse_extra_buttons=0x080000,
roller_V=0x040000,
keyboard_sleep_raw=0x020000, # system control keys such as Sleep
keyboard_multimedia_raw=0x010000, # consumer controls such as Mute and Calculator
# reserved_r1b4= 0x001000, # unknown, seen on a unifying receiver
reserved5=0x008000,
reserved4=0x004000,
reserved3=0x002000,
reserved2=0x001000,
software_present=0x000800, # .. no idea
reserved1=0x000400,
keyboard_illumination=0x000200, # illumination brightness level changes (by pressing keys)
wireless=0x000100, # notify when the device wireless goes on/off-line
mx_air_3d_gesture=0x000001,
)
ERROR = _NamedInts(
invalid_SubID__command=0x01,
invalid_address=0x02,
invalid_value=0x03,
connection_request_failed=0x04,
too_many_devices=0x05,
already_exists=0x06,
busy=0x07,
unknown_device=0x08,
resource_error=0x09,
request_unavailable=0x0A,
unsupported_parameter_value=0x0B,
wrong_pin_code=0x0C
)
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(
# 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,
# only apply to devices
mouse_button_flags=0x01,
keyboard_hand_detection=0x01,
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,
# notifications
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
fw_version=0x02,
receiver_information=0x03,
pairing_information=0x20, # 0x2N, by connected device
extended_pairing_information=0x30, # 0x3N, by connected device
device_name=0x40, # 0x4N, by connected device
bolt_pairing_information=0x50, # 0x5N, by connected device
bolt_device_name=0x60, # 0x6N01, by connected device,
)
# Flags taken from https://drive.google.com/file/d/0BxbRzx7vEV7eNDBheWY0UHM5dEU/view?usp=sharing
DEVICE_FEATURES = _NamedInts(
reserved1=0x010000,
special_buttons=0x020000,
enhanced_key_usage=0x040000,
fast_fw_rev=0x080000,
reserved2=0x100000,
reserved3=0x200000,
scroll_accel=0x400000,
buttons_control_resolution=0x800000,
inhibit_lock_key_sound=0x000001,
reserved4=0x000002,
mx_air_3d_engine=0x000004,
host_control_leds=0x000008,
reserved5=0x000010,
reserved6=0x000020,
reserved7=0x000040,
reserved8=0x000080,
)
# #
# functions # functions
# #

View File

@ -0,0 +1,151 @@
from .common import NamedInts
#
# Constants - most of them as defined by the official Logitech HID++ 1.0
# documentation, some of them guessed.
#
DEVICE_KIND = NamedInts(
unknown=0x00,
keyboard=0x01,
mouse=0x02,
numpad=0x03,
presenter=0x04,
remote=0x07,
trackball=0x08,
touchpad=0x09,
headset=0x0D, # not from Logitech documentation
remote_control=0x0E, # for compatibility with HID++ 2.0
receiver=0x0F # for compatibility with HID++ 2.0
)
POWER_SWITCH_LOCATION = NamedInts(
base=0x01,
top_case=0x02,
edge_of_top_right_corner=0x03,
top_left_corner=0x05,
bottom_left_corner=0x06,
top_right_corner=0x07,
bottom_right_corner=0x08,
top_edge=0x09,
right_edge=0x0A,
left_edge=0x0B,
bottom_edge=0x0C
)
# Some flags are used both by devices and receivers. The Logitech documentation
# mentions that the first and last (third) byte are used for devices while the
# second is used for the receiver. In practise, the second byte is also used for
# some device-specific notifications (keyboard illumination level). Do not
# simply set all notification bits if the software does not support it. For
# example, enabling keyboard_sleep_raw makes the Sleep key a no-operation unless
# the software is updated to handle that event.
# Observations:
# - wireless and software present were seen on receivers, reserved_r1b4 as well
# - the rest work only on devices as far as we can tell right now
# In the future would be useful to have separate enums for receiver and device notification flags,
# but right now we don't know enough.
# additional flags taken from https://drive.google.com/file/d/0BxbRzx7vEV7eNDBheWY0UHM5dEU/view?usp=sharing
NOTIFICATION_FLAG = NamedInts(
numpad_numerical_keys=0x800000,
f_lock_status=0x400000,
roller_H=0x200000,
battery_status=0x100000, # send battery charge notifications (0x07 or 0x0D)
mouse_extra_buttons=0x080000,
roller_V=0x040000,
keyboard_sleep_raw=0x020000, # system control keys such as Sleep
keyboard_multimedia_raw=0x010000, # consumer controls such as Mute and Calculator
# reserved_r1b4= 0x001000, # unknown, seen on a unifying receiver
reserved5=0x008000,
reserved4=0x004000,
reserved3=0x002000,
reserved2=0x001000,
software_present=0x000800, # .. no idea
reserved1=0x000400,
keyboard_illumination=0x000200, # illumination brightness level changes (by pressing keys)
wireless=0x000100, # notify when the device wireless goes on/off-line
mx_air_3d_gesture=0x000001,
)
ERROR = NamedInts(
invalid_SubID__command=0x01,
invalid_address=0x02,
invalid_value=0x03,
connection_request_failed=0x04,
too_many_devices=0x05,
already_exists=0x06,
busy=0x07,
unknown_device=0x08,
resource_error=0x09,
request_unavailable=0x0A,
unsupported_parameter_value=0x0B,
wrong_pin_code=0x0C
)
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(
# 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,
# only apply to devices
mouse_button_flags=0x01,
keyboard_hand_detection=0x01,
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,
# notifications
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
fw_version=0x02,
receiver_information=0x03,
pairing_information=0x20, # 0x2N, by connected device
extended_pairing_information=0x30, # 0x3N, by connected device
device_name=0x40, # 0x4N, by connected device
bolt_pairing_information=0x50, # 0x5N, by connected device
bolt_device_name=0x60, # 0x6N01, by connected device,
)
# Flags taken from https://drive.google.com/file/d/0BxbRzx7vEV7eNDBheWY0UHM5dEU/view?usp=sharing
DEVICE_FEATURES = NamedInts(
reserved1=0x010000,
special_buttons=0x020000,
enhanced_key_usage=0x040000,
fast_fw_rev=0x080000,
reserved2=0x100000,
reserved3=0x200000,
scroll_accel=0x400000,
buttons_control_resolution=0x800000,
inhibit_lock_key_sound=0x000001,
reserved4=0x000002,
mx_air_3d_engine=0x000004,
host_control_leds=0x000008,
reserved5=0x000010,
reserved6=0x000020,
reserved7=0x000040,
reserved8=0x000080,
)

View File

@ -173,7 +173,8 @@ class Receiver:
if not wpid: if not wpid:
logger.error('Unable to get wpid from udev for device %d of %s', n, self) logger.error('Unable to get wpid from udev for device %d of %s', n, self)
raise exceptions.NoSuchDevice(number=n, receiver=self, error='Not present 27Mhz device') raise exceptions.NoSuchDevice(number=n, receiver=self, error='Not present 27Mhz device')
kind = _hidpp10.DEVICE_KIND[self.get_kind_from_index(n)] kind = _hidpp10_constants.DEVICE_KIND[self.get_kind_from_index(n)]
elif not self.receiver_kind == 'unifying': # unifying protocol not supported, may be an old Nano receiver elif not self.receiver_kind == 'unifying': # unifying protocol not supported, may be an old Nano receiver
device_info = self.read_register(_R.receiver_info, 0x04) device_info = self.read_register(_R.receiver_info, 0x04)
if device_info: if device_info:
@ -197,7 +198,7 @@ class Receiver:
return '?', power_switch return '?', power_switch
pair_info = self.read_register(_R.receiver_info, _IR.extended_pairing_information + n - 1) pair_info = self.read_register(_R.receiver_info, _IR.extended_pairing_information + n - 1)
if pair_info: if pair_info:
power_switch = _hidpp10.POWER_SWITCH_LOCATION[ord(pair_info[9:10]) & 0x0F] power_switch = _hidpp10_constants.POWER_SWITCH_LOCATION[ord(pair_info[9:10]) & 0x0F]
else: # some Nano receivers? else: # some Nano receivers?
pair_info = self.read_register(0x2D5) pair_info = self.read_register(0x2D5)
if pair_info: if pair_info:

View File

@ -26,7 +26,7 @@ from time import time as _time
from traceback import format_exc as _format_exc from traceback import format_exc as _format_exc
from . import descriptors as _descriptors from . import descriptors as _descriptors
from . import hidpp10 as _hidpp10 from . import hidpp10_constants as _hidpp10_constants
from . import hidpp20 as _hidpp20 from . import hidpp20 as _hidpp20
from . import special_keys as _special_keys from . import special_keys as _special_keys
from .base import _HIDPP_Notification as _HIDPP_Notification from .base import _HIDPP_Notification as _HIDPP_Notification
@ -58,8 +58,8 @@ from .special_keys import DISABLE as _DKEY
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_DK = _hidpp10.DEVICE_KIND _DK = _hidpp10_constants.DEVICE_KIND
_R = _hidpp10.REGISTERS _R = _hidpp10_constants.REGISTERS
_F = _hidpp20.FEATURE _F = _hidpp20.FEATURE
_GG = _hidpp20.GESTURE _GG = _hidpp20.GESTURE

View File

@ -19,7 +19,8 @@
import logging import logging
from . import hidpp10 as _hidpp10 from . import hidpp10 as _hidpp10
from . import hidpp20 as _hidpp20 from . import hidpp10_constants as _hidpp10_constants
from . import hidpp20_constants as _hidpp20_constants
from . import settings as _settings from . import settings as _settings
from .common import BATTERY_APPROX as _BATTERY_APPROX from .common import BATTERY_APPROX as _BATTERY_APPROX
from .common import NamedInt as _NamedInt from .common import NamedInt as _NamedInt
@ -28,7 +29,7 @@ from .i18n import _, ngettext
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_R = _hidpp10.REGISTERS _R = _hidpp10_constants.REGISTERS
# #
# #
@ -154,11 +155,11 @@ class DeviceStatus(dict):
# Some notifications may come with no battery level info, just # Some notifications may come with no battery level info, just
# charging state info, so do our best to infer a level (even if it is just the last level) # charging state info, so do our best to infer a level (even if it is just the last level)
# It is not always possible to do this well # It is not always possible to do this well
if status == _hidpp20.BATTERY_STATUS.full: if status == _hidpp20_constants.BATTERY_STATUS.full:
level = _BATTERY_APPROX.full level = _BATTERY_APPROX.full
elif status in (_hidpp20.BATTERY_STATUS.almost_full, _hidpp20.BATTERY_STATUS.recharging): elif status in (_hidpp20_constants.BATTERY_STATUS.almost_full, _hidpp20_constants.BATTERY_STATUS.recharging):
level = _BATTERY_APPROX.good level = _BATTERY_APPROX.good
elif status == _hidpp20.BATTERY_STATUS.slow_recharge: elif status == _hidpp20_constants.BATTERY_STATUS.slow_recharge:
level = _BATTERY_APPROX.low level = _BATTERY_APPROX.low
else: else:
level = self.get(KEYS.BATTERY_LEVEL) level = self.get(KEYS.BATTERY_LEVEL)
@ -172,15 +173,15 @@ class DeviceStatus(dict):
old_voltage, self[KEYS.BATTERY_VOLTAGE] = self.get(KEYS.BATTERY_VOLTAGE), voltage old_voltage, self[KEYS.BATTERY_VOLTAGE] = self.get(KEYS.BATTERY_VOLTAGE), voltage
charging = status in ( charging = status in (
_hidpp20.BATTERY_STATUS.recharging, _hidpp20.BATTERY_STATUS.almost_full, _hidpp20.BATTERY_STATUS.full, _hidpp20_constants.BATTERY_STATUS.recharging, _hidpp20_constants.BATTERY_STATUS.almost_full, _hidpp20_constants.BATTERY_STATUS.full,
_hidpp20.BATTERY_STATUS.slow_recharge _hidpp20_constants.BATTERY_STATUS.slow_recharge
) )
old_charging, self[KEYS.BATTERY_CHARGING] = self.get(KEYS.BATTERY_CHARGING), charging old_charging, self[KEYS.BATTERY_CHARGING] = self.get(KEYS.BATTERY_CHARGING), charging
changed = old_level != level or old_status != status or old_charging != charging or old_voltage != voltage changed = old_level != level or old_status != status or old_charging != charging or old_voltage != voltage
alert, reason = ALERT.NONE, None alert, reason = ALERT.NONE, None
if _hidpp20.BATTERY_OK(status) and (level is None or level > _BATTERY_ATTENTION_LEVEL): if _hidpp20_constants.BATTERY_OK(status) and (level is None or level > _BATTERY_ATTENTION_LEVEL):
self[KEYS.ERROR] = None self[KEYS.ERROR] = None
else: else:
logger.warning('%s: battery %d%%, ALERT %s', self._device, level, status) logger.warning('%s: battery %d%%, ALERT %s', self._device, level, status)
@ -238,7 +239,7 @@ class DeviceStatus(dict):
# when devices request software reconfiguration # when devices request software reconfiguration
# and when devices become active if they don't have wireless device status feature, # and when devices become active if they don't have wireless device status feature,
if was_active is None or push or not was_active and ( if was_active is None or push or not was_active and (
not d.features or _hidpp20.FEATURE.WIRELESS_DEVICE_STATUS not in d.features not d.features or _hidpp20_constants.FEATURE.WIRELESS_DEVICE_STATUS not in d.features
): ):
if logger.isEnabledFor(logging.INFO): if logger.isEnabledFor(logging.INFO):
logger.info('%s pushing device settings %s', d, d.settings) logger.info('%s pushing device settings %s', d, d.settings)

View File

@ -20,10 +20,11 @@ from time import time as _timestamp
from logitech_receiver import base as _base from logitech_receiver import base as _base
from logitech_receiver import hidpp10 as _hidpp10 from logitech_receiver import hidpp10 as _hidpp10
from logitech_receiver import hidpp10_constants as _hidpp10_constants
from logitech_receiver import notifications as _notifications from logitech_receiver import notifications as _notifications
from logitech_receiver import status as _status from logitech_receiver import status as _status
_R = _hidpp10.REGISTERS _R = _hidpp10_constants.REGISTERS
def run(receivers, args, find_receiver, _ignore): def run(receivers, args, find_receiver, _ignore):
@ -42,8 +43,8 @@ def run(receivers, args, find_receiver, _ignore):
# check if it's necessary to set the notification flags # check if it's necessary to set the notification flags
old_notification_flags = _hidpp10.get_notification_flags(receiver) or 0 old_notification_flags = _hidpp10.get_notification_flags(receiver) or 0
if not (old_notification_flags & _hidpp10.NOTIFICATION_FLAG.wireless): if not (old_notification_flags & _hidpp10_constants.NOTIFICATION_FLAG.wireless):
_hidpp10.set_notification_flags(receiver, old_notification_flags | _hidpp10.NOTIFICATION_FLAG.wireless) _hidpp10.set_notification_flags(receiver, old_notification_flags | _hidpp10_constants.NOTIFICATION_FLAG.wireless)
# get all current devices # get all current devices
known_devices = [dev.number for dev in receiver] known_devices = [dev.number for dev in receiver]
@ -85,7 +86,9 @@ def run(receivers, args, find_receiver, _ignore):
kind = receiver.status.device_kind kind = receiver.status.device_kind
print(f'Bolt Pairing: discovered {name}') print(f'Bolt Pairing: discovered {name}')
receiver.pair_device( receiver.pair_device(
address=address, authentication=authentication, entropy=20 if kind == _hidpp10.DEVICE_KIND.keyboard else 10 address=address,
authentication=authentication,
entropy=20 if kind == _hidpp10_constants.DEVICE_KIND.keyboard else 10
) )
pairing_start = _timestamp() pairing_start = _timestamp()
patience = 5 # the discovering notification may come slightly later, so be patient patience = 5 # the discovering notification may come slightly later, so be patient
@ -121,7 +124,7 @@ def run(receivers, args, find_receiver, _ignore):
if n: if n:
receiver.handle.notifications_hook(n) receiver.handle.notifications_hook(n)
if not (old_notification_flags & _hidpp10.NOTIFICATION_FLAG.wireless): if not (old_notification_flags & _hidpp10_constants.NOTIFICATION_FLAG.wireless):
# only clear the flags if they weren't set before, otherwise a # only clear the flags if they weren't set before, otherwise a
# concurrently running Solaar app might stop working properly # concurrently running Solaar app might stop working properly
_hidpp10.set_notification_flags(receiver, old_notification_flags) _hidpp10.set_notification_flags(receiver, old_notification_flags)

View File

@ -17,11 +17,11 @@
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from logitech_receiver import base as _base from logitech_receiver import base as _base
from logitech_receiver import hidpp10 as _hidpp10 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 solaar.cli.show import _print_device, _print_receiver from solaar.cli.show import _print_device, _print_receiver
_R = _hidpp10.REGISTERS _R = _hidpp10_constants.REGISTERS
def run(receivers, args, find_receiver, _ignore): def run(receivers, args, find_receiver, _ignore):
@ -90,9 +90,9 @@ def run(receivers, args, find_receiver, _ignore):
last = None last = None
for sub in range(0, 0xFF): for sub in range(0, 0xFF):
rgst = _base.request(receiver.handle, 0xFF, 0x8100 | reg, sub, return_error=True) rgst = _base.request(receiver.handle, 0xFF, 0x8100 | reg, sub, return_error=True)
if isinstance(rgst, int) and rgst == _hidpp10.ERROR.invalid_address: if isinstance(rgst, int) and rgst == _hidpp10_constants.ERROR.invalid_address:
break break
elif isinstance(rgst, int) and rgst == _hidpp10.ERROR.invalid_value: elif isinstance(rgst, int) and rgst == _hidpp10_constants.ERROR.invalid_value:
continue continue
else: else:
if not isinstance(last, bytes) or not isinstance(rgst, bytes) or last != rgst: if not isinstance(last, bytes) or not isinstance(rgst, bytes) or last != rgst:
@ -104,9 +104,9 @@ def run(receivers, args, find_receiver, _ignore):
last = None last = None
for sub in range(0, 0xFF): for sub in range(0, 0xFF):
rgst = _base.request(receiver.handle, 0xFF, 0x8100 | (0x200 + reg), sub, return_error=True) rgst = _base.request(receiver.handle, 0xFF, 0x8100 | (0x200 + reg), sub, return_error=True)
if isinstance(rgst, int) and rgst == _hidpp10.ERROR.invalid_address: if isinstance(rgst, int) and rgst == _hidpp10_constants.ERROR.invalid_address:
break break
elif isinstance(rgst, int) and rgst == _hidpp10.ERROR.invalid_value: elif isinstance(rgst, int) and rgst == _hidpp10_constants.ERROR.invalid_value:
continue continue
else: else:
if not isinstance(last, bytes) or not isinstance(rgst, bytes) or last != rgst: if not isinstance(last, bytes) or not isinstance(rgst, bytes) or last != rgst:

View File

@ -18,6 +18,7 @@
from logitech_receiver import exceptions from logitech_receiver import exceptions
from logitech_receiver import hidpp10 as _hidpp10 from logitech_receiver import hidpp10 as _hidpp10
from logitech_receiver import hidpp10_constants as _hidpp10_constants
from logitech_receiver import hidpp20 as _hidpp20 from logitech_receiver import hidpp20 as _hidpp20
from logitech_receiver import receiver as _receiver from logitech_receiver import receiver as _receiver
from logitech_receiver import settings_templates as _settings_templates from logitech_receiver import settings_templates as _settings_templates
@ -46,12 +47,12 @@ def _print_receiver(receiver):
notification_flags = _hidpp10.get_notification_flags(receiver) notification_flags = _hidpp10.get_notification_flags(receiver)
if notification_flags is not None: if notification_flags is not None:
if notification_flags: if notification_flags:
notification_names = _hidpp10.NOTIFICATION_FLAG.flag_names(notification_flags) notification_names = _hidpp10_constants.NOTIFICATION_FLAG.flag_names(notification_flags)
print(' Notifications: %s (0x%06X)' % (', '.join(notification_names), notification_flags)) print(' Notifications: %s (0x%06X)' % (', '.join(notification_names), notification_flags))
else: else:
print(' Notifications: (none)') print(' Notifications: (none)')
activity = receiver.read_register(_hidpp10.REGISTERS.devices_activity) activity = receiver.read_register(_hidpp10_constants.REGISTERS.devices_activity)
if activity: if activity:
activity = [(d, ord(activity[d - 1:d])) for d in range(1, receiver.max_devices)] activity = [(d, ord(activity[d - 1:d])) for d in range(1, receiver.max_devices)]
activity_text = ', '.join(('%d=%d' % (d, a)) for d, a in activity if a > 0) activity_text = ', '.join(('%d=%d' % (d, a)) for d, a in activity if a > 0)
@ -122,14 +123,14 @@ def _print_device(dev, num=None):
notification_flags = _hidpp10.get_notification_flags(dev) notification_flags = _hidpp10.get_notification_flags(dev)
if notification_flags is not None: if notification_flags is not None:
if notification_flags: if notification_flags:
notification_names = _hidpp10.NOTIFICATION_FLAG.flag_names(notification_flags) notification_names = _hidpp10_constants.NOTIFICATION_FLAG.flag_names(notification_flags)
print(' Notifications: %s (0x%06X).' % (', '.join(notification_names), notification_flags)) print(' Notifications: %s (0x%06X).' % (', '.join(notification_names), notification_flags))
else: else:
print(' Notifications: (none).') print(' Notifications: (none).')
device_features = _hidpp10.get_device_features(dev) device_features = _hidpp10.get_device_features(dev)
if device_features is not None: if device_features is not None:
if device_features: if device_features:
device_features_names = _hidpp10.DEVICE_FEATURES.flag_names(device_features) device_features_names = _hidpp10_constants.DEVICE_FEATURES.flag_names(device_features)
print(' Features: %s (0x%06X)' % (', '.join(device_features_names), device_features)) print(' Features: %s (0x%06X)' % (', '.join(device_features_names), device_features))
else: else:
print(' Features: (none)') print(' Features: (none)')