diff --git a/lib/logitech_receiver/device.py b/lib/logitech_receiver/device.py index fdd9622f9..62b759f99 100644 --- a/lib/logitech_receiver/device.py +++ b/lib/logitech_receiver/device.py @@ -31,11 +31,11 @@ from . import hidpp10 from . import hidpp10_constants from . import hidpp20 -from . import hidpp20_constants from . import settings from . import settings_templates from .common import Alert from .common import Battery +from .hidpp20_constants import SupportedFeature logger = logging.getLogger(__name__) @@ -295,9 +295,9 @@ def polling_rate(self): @property def led_effects(self): if not self._led_effects and self.online and self.protocol >= 2.0: - if hidpp20_constants.FEATURE.COLOR_LED_EFFECTS in self.features: + if SupportedFeature.COLOR_LED_EFFECTS in self.features: self._led_effects = hidpp20.LEDEffectsInfo(self) - elif hidpp20_constants.FEATURE.RGB_EFFECTS in self.features: + elif SupportedFeature.RGB_EFFECTS in self.features: self._led_effects = hidpp20.RGBEffectsInfo(self) return self._led_effects @@ -435,7 +435,7 @@ def changed(self, active=None, alert=Alert.NONE, reason=None, push=False): was_active is None or not was_active or push - and (not self.features or hidpp20_constants.FEATURE.WIRELESS_DEVICE_STATUS not in self.features) + and (not self.features or SupportedFeature.WIRELESS_DEVICE_STATUS not in self.features) ): if logger.isEnabledFor(logging.INFO): logger.info("%s pushing device settings %s", self, self.settings) diff --git a/lib/logitech_receiver/diversion.py b/lib/logitech_receiver/diversion.py index b9c368ab6..9ad6ccd8d 100644 --- a/lib/logitech_receiver/diversion.py +++ b/lib/logitech_receiver/diversion.py @@ -47,7 +47,7 @@ import evdev from .common import NamedInt -from .hidpp20 import FEATURE +from .hidpp20 import SupportedFeature from .special_keys import CONTROL gi.require_version("Gdk", "3.0") # isort:skip @@ -434,7 +434,7 @@ def simulate_scroll(dx, dy): def thumb_wheel_up(f, r, d, a): global thumb_wheel_displacement - if f != FEATURE.THUMB_WHEEL or r != 0: + if f != SupportedFeature.THUMB_WHEEL or r != 0: return False if a is None: return signed(d[0:2]) < 0 and signed(d[0:2]) @@ -447,7 +447,7 @@ def thumb_wheel_up(f, r, d, a): def thumb_wheel_down(f, r, d, a): global thumb_wheel_displacement - if f != FEATURE.THUMB_WHEEL or r != 0: + if f != SupportedFeature.THUMB_WHEEL or r != 0: return False if a is None: return signed(d[0:2]) > 0 and signed(d[0:2]) @@ -460,9 +460,9 @@ def thumb_wheel_down(f, r, d, a): def charging(f, r, d, _a): if ( - (f == FEATURE.BATTERY_STATUS and r == 0 and 1 <= d[2] <= 4) - or (f == FEATURE.BATTERY_VOLTAGE and r == 0 and d[2] & (1 << 7)) - or (f == FEATURE.UNIFIED_BATTERY and r == 0 and 1 <= d[2] <= 3) + (f == SupportedFeature.BATTERY_STATUS and r == 0 and 1 <= d[2] <= 4) + or (f == SupportedFeature.BATTERY_VOLTAGE and r == 0 and d[2] & (1 << 7)) + or (f == SupportedFeature.UNIFIED_BATTERY and r == 0 and 1 <= d[2] <= 3) ): return 1 else: @@ -470,30 +470,30 @@ def charging(f, r, d, _a): TESTS = { - "crown_right": [lambda f, r, d, a: f == FEATURE.CROWN and r == 0 and d[1] < 128 and d[1], False], - "crown_left": [lambda f, r, d, a: f == FEATURE.CROWN and r == 0 and d[1] >= 128 and 256 - d[1], False], - "crown_right_ratchet": [lambda f, r, d, a: f == FEATURE.CROWN and r == 0 and d[2] < 128 and d[2], False], - "crown_left_ratchet": [lambda f, r, d, a: f == FEATURE.CROWN and r == 0 and d[2] >= 128 and 256 - d[2], False], - "crown_tap": [lambda f, r, d, a: f == FEATURE.CROWN and r == 0 and d[5] == 0x01 and d[5], False], - "crown_start_press": [lambda f, r, d, a: f == FEATURE.CROWN and r == 0 and d[6] == 0x01 and d[6], False], - "crown_end_press": [lambda f, r, d, a: f == FEATURE.CROWN and r == 0 and d[6] == 0x05 and d[6], False], - "crown_pressed": [lambda f, r, d, a: f == FEATURE.CROWN and r == 0 and 0x01 <= d[6] <= 0x04 and d[6], False], + "crown_right": [lambda f, r, d, a: f == SupportedFeature.CROWN and r == 0 and d[1] < 128 and d[1], False], + "crown_left": [lambda f, r, d, a: f == SupportedFeature.CROWN and r == 0 and d[1] >= 128 and 256 - d[1], False], + "crown_right_ratchet": [lambda f, r, d, a: f == SupportedFeature.CROWN and r == 0 and d[2] < 128 and d[2], False], + "crown_left_ratchet": [lambda f, r, d, a: f == SupportedFeature.CROWN and r == 0 and d[2] >= 128 and 256 - d[2], False], + "crown_tap": [lambda f, r, d, a: f == SupportedFeature.CROWN and r == 0 and d[5] == 0x01 and d[5], False], + "crown_start_press": [lambda f, r, d, a: f == SupportedFeature.CROWN and r == 0 and d[6] == 0x01 and d[6], False], + "crown_end_press": [lambda f, r, d, a: f == SupportedFeature.CROWN and r == 0 and d[6] == 0x05 and d[6], False], + "crown_pressed": [lambda f, r, d, a: f == SupportedFeature.CROWN and r == 0 and 0x01 <= d[6] <= 0x04 and d[6], False], "thumb_wheel_up": [thumb_wheel_up, True], "thumb_wheel_down": [thumb_wheel_down, True], "lowres_wheel_up": [ - lambda f, r, d, a: f == FEATURE.LOWRES_WHEEL and r == 0 and signed(d[0:1]) > 0 and signed(d[0:1]), + lambda f, r, d, a: f == SupportedFeature.LOWRES_WHEEL and r == 0 and signed(d[0:1]) > 0 and signed(d[0:1]), False, ], "lowres_wheel_down": [ - lambda f, r, d, a: f == FEATURE.LOWRES_WHEEL and r == 0 and signed(d[0:1]) < 0 and signed(d[0:1]), + lambda f, r, d, a: f == SupportedFeature.LOWRES_WHEEL and r == 0 and signed(d[0:1]) < 0 and signed(d[0:1]), False, ], "hires_wheel_up": [ - lambda f, r, d, a: f == FEATURE.HIRES_WHEEL and r == 0 and signed(d[1:3]) > 0 and signed(d[1:3]), + lambda f, r, d, a: f == SupportedFeature.HIRES_WHEEL and r == 0 and signed(d[1:3]) > 0 and signed(d[1:3]), False, ], "hires_wheel_down": [ - lambda f, r, d, a: f == FEATURE.HIRES_WHEEL and r == 0 and signed(d[1:3]) < 0 and signed(d[1:3]), + lambda f, r, d, a: f == SupportedFeature.HIRES_WHEEL and r == 0 and signed(d[1:3]) < 0 and signed(d[1:3]), False, ], "charging": [charging, False], @@ -738,12 +738,13 @@ def data(self): class Feature(Condition): - def __init__(self, feature, warn=True): - if not (isinstance(feature, str) and feature in FEATURE): + def __init__(self, feature: str, warn: bool = True): + try: + self.feature = SupportedFeature[feature] + except KeyError: + self.feature = None if warn: logger.warning("rule Feature argument not name of a feature: %s", feature) - self.feature = None - self.feature = FEATURE[feature] def __str__(self): return "Feature: " + str(self.feature) @@ -1052,7 +1053,7 @@ def __str__(self): def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) - if feature == FEATURE.MOUSE_GESTURE: + if feature == SupportedFeature.MOUSE_GESTURE: d = notification.data data = struct.unpack("!" + (int(len(d) / 2) * "h"), d) data_offset = 1 @@ -1501,7 +1502,7 @@ def process_notification(device, notification: HIDPPNotification, feature) -> No key_down, key_up = None, None # need to keep track of keys that are down to find a new key down if notification.address == 0x00: - if feature == FEATURE.REPROG_CONTROLS_V4: + if feature == SupportedFeature.REPROG_CONTROLS_V4: new_keys_down = struct.unpack("!4H", notification.data[:8]) for key in new_keys_down: if key and key not in keys_down: @@ -1511,7 +1512,7 @@ def process_notification(device, notification: HIDPPNotification, feature) -> No key_up = key keys_down = new_keys_down # and also G keys down - elif feature == FEATURE.GKEY: + elif feature == SupportedFeature.GKEY: new_g_keys_down = struct.unpack(" No key_up = CONTROL["G" + str(i + 1)] g_keys_down = new_g_keys_down # and also M keys down - elif feature == FEATURE.MKEYS: + elif feature == SupportedFeature.MKEYS: new_m_keys_down = struct.unpack("!1B", notification.data[:1])[0] for i in range(1, 9): if new_m_keys_down & (0x01 << (i - 1)) and not m_keys_down & (0x01 << (i - 1)): @@ -1529,7 +1530,7 @@ def process_notification(device, notification: HIDPPNotification, feature) -> No key_up = CONTROL["M" + str(i)] m_keys_down = new_m_keys_down # and also MR key - elif feature == FEATURE.MR: + elif feature == SupportedFeature.MR: new_mr_key_down = struct.unpack("!1B", notification.data[:1])[0] if not mr_key_down and new_mr_key_down: key_down = CONTROL["MR"] @@ -1537,7 +1538,7 @@ def process_notification(device, notification: HIDPPNotification, feature) -> No key_up = CONTROL["MR"] mr_key_down = new_mr_key_down # keep track of thumb wheel movement - elif feature == FEATURE.THUMB_WHEEL: + elif feature == SupportedFeature.THUMB_WHEEL: if notification.data[4] <= 0x01: # when wheel starts, zero out last movement thumb_wheel_displacement = 0 thumb_wheel_displacement += signed(notification.data[0:2]) diff --git a/lib/logitech_receiver/hidpp20.py b/lib/logitech_receiver/hidpp20.py index 2d1a3d9f9..8861ec6e2 100644 --- a/lib/logitech_receiver/hidpp20.py +++ b/lib/logitech_receiver/hidpp20.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 __future__ import annotations import logging import socket @@ -44,9 +45,9 @@ from .hidpp20_constants import CHARGE_TYPE from .hidpp20_constants import DEVICE_KIND from .hidpp20_constants import ERROR -from .hidpp20_constants import FEATURE from .hidpp20_constants import FIRMWARE_KIND from .hidpp20_constants import GESTURE +from .hidpp20_constants import SupportedFeature logger = logging.getLogger(__name__) @@ -95,7 +96,7 @@ def _check(self) -> bool: return False if self.count > 0: return True - reply = self.device.request(0x0000, struct.pack("!H", FEATURE.FEATURE_SET)) + reply = self.device.request(0x0000, struct.pack("!H", SupportedFeature.FEATURE_SET)) if reply is not None: fs_index = reply[0] if fs_index: @@ -105,14 +106,14 @@ def _check(self) -> bool: return False else: self.count = count[0] + 1 # ROOT feature not included in count - self[FEATURE.ROOT] = 0 - self[FEATURE.FEATURE_SET] = fs_index + self[SupportedFeature.ROOT] = 0 + self[SupportedFeature.FEATURE_SET] = fs_index return True else: self.supported = False return False - def get_feature(self, index: int) -> Optional[NamedInt]: + def get_feature(self, index: int) -> SupportedFeature | None: feature = self.inverse.get(index) if feature is not None: return feature @@ -120,9 +121,13 @@ def get_feature(self, index: int) -> Optional[NamedInt]: feature = self.inverse.get(index) if feature is not None: return feature - response = self.device.feature_request(FEATURE.FEATURE_SET, 0x10, index) + response = self.device.feature_request(SupportedFeature.FEATURE_SET, 0x10, index) if response: - feature = FEATURE[struct.unpack("!H", response[:2])[0]] + data = struct.unpack("!H", response[:2])[0] + try: + feature = SupportedFeature(data) + except ValueError: + feature = f"unknown:{data:04X}" self[feature] = index self.version[feature] = response[3] return feature @@ -290,7 +295,7 @@ def remap(self, to: NamedInt): def _getCidReporting(self): try: mapped_data = self._device.feature_request( - FEATURE.REPROG_CONTROLS_V4, + SupportedFeature.REPROG_CONTROLS_V4, 0x20, *tuple(struct.pack("!H", self._cid)), ) @@ -373,7 +378,7 @@ def _setCidReporting(self, flags: Dict[NamedInt, bool] = None, remap: int = 0): pkt = tuple(struct.pack("!HBH", self._cid, bfield & 0xFF, remap)) # TODO: to fully support version 4 of REPROG_CONTROLS_V4, append `(bfield >> 8) & 0xff` here. # But older devices might behave oddly given that byte, so we don't send it. - ret = self._device.feature_request(FEATURE.REPROG_CONTROLS_V4, 0x30, *pkt) + ret = self._device.feature_request(SupportedFeature.REPROG_CONTROLS_V4, 0x30, *pkt) if ret is None or struct.unpack("!BBBBB", ret[:5]) != pkt and logger.isEnabledFor(logging.DEBUG): logger.debug(f"REPROG_CONTROLS_v4 setCidReporting on device {self._device} didn't echo request packet.") @@ -434,13 +439,13 @@ def data_bytes(self): def remap(self, data_bytes): cid = common.int2bytes(self._cid, 2) if common.bytes2int(data_bytes) == special_keys.KEYS_Default: # map back to default - self._device.feature_request(FEATURE.PERSISTENT_REMAPPABLE_ACTION, 0x50, cid, 0xFF) + self._device.feature_request(SupportedFeature.PERSISTENT_REMAPPABLE_ACTION, 0x50, cid, 0xFF) self._device.remap_keys._query_key(self.index) return self._device.remap_keys.keys[self.index].data_bytes else: self.actionId, self.remapped, self._modifierMask = struct.unpack("!BHB", data_bytes) self.cidStatus = 0x01 - self._device.feature_request(FEATURE.PERSISTENT_REMAPPABLE_ACTION, 0x40, cid, 0xFF, data_bytes) + self._device.feature_request(SupportedFeature.PERSISTENT_REMAPPABLE_ACTION, 0x40, cid, 0xFF, data_bytes) return True @@ -451,10 +456,10 @@ def __init__(self, device, count, version): assert device is not None self.device = device self.lock = threading.Lock() - if FEATURE.REPROG_CONTROLS_V4 in self.device.features: - self.keyversion = FEATURE.REPROG_CONTROLS_V4 - elif FEATURE.REPROG_CONTROLS_V2 in self.device.features: - self.keyversion = FEATURE.REPROG_CONTROLS_V2 + if SupportedFeature.REPROG_CONTROLS_V4 in self.device.features: + self.keyversion = SupportedFeature.REPROG_CONTROLS_V4 + elif SupportedFeature.REPROG_CONTROLS_V2 in self.device.features: + self.keyversion = SupportedFeature.REPROG_CONTROLS_V2 else: if logger.isEnabledFor(logging.ERROR): logger.error(f"Trying to read keys on device {device} which has no REPROG_CONTROLS(_VX) support.") @@ -515,7 +520,7 @@ def __init__(self, device: Device, count, version=1): def _query_key(self, index: int): if index < 0 or index >= len(self.keys): raise IndexError(index) - keydata = self.device.feature_request(FEATURE.REPROG_CONTROLS, 0x10, index) + keydata = self.device.feature_request(SupportedFeature.REPROG_CONTROLS, 0x10, index) if keydata: cid, tid, flags = struct.unpack("!HHB", keydata[:5]) self.keys[index] = ReprogrammableKey(self.device, index, cid, tid, flags) @@ -531,7 +536,7 @@ def __init__(self, device, count): def _query_key(self, index: int): if index < 0 or index >= len(self.keys): raise IndexError(index) - keydata = self.device.feature_request(FEATURE.REPROG_CONTROLS_V4, 0x10, index) + keydata = self.device.feature_request(SupportedFeature.REPROG_CONTROLS_V4, 0x10, index) if keydata: cid, tid, flags1, pos, group, gmask, flags2 = struct.unpack("!HHBBBBB", keydata[:9]) flags = flags1 | (flags2 << 8) @@ -552,7 +557,7 @@ def __init__(self, device, count): @property def capabilities(self): if self._capabilities is None and self.device.online: - capabilities = self.device.feature_request(FEATURE.PERSISTENT_REMAPPABLE_ACTION, 0x00) + capabilities = self.device.feature_request(SupportedFeature.PERSISTENT_REMAPPABLE_ACTION, 0x00) assert capabilities, "Oops, persistent remappable key capabilities cannot be retrieved!" self._capabilities = struct.unpack("!H", capabilities[:2])[0] # flags saying what the mappings are possible return self._capabilities @@ -560,11 +565,11 @@ def capabilities(self): def _query_key(self, index: int): if index < 0 or index >= len(self.keys): raise IndexError(index) - keydata = self.device.feature_request(FEATURE.PERSISTENT_REMAPPABLE_ACTION, 0x20, index, 0xFF) + keydata = self.device.feature_request(SupportedFeature.PERSISTENT_REMAPPABLE_ACTION, 0x20, index, 0xFF) if keydata: key = struct.unpack("!H", keydata[:2])[0] mapped_data = self.device.feature_request( - FEATURE.PERSISTENT_REMAPPABLE_ACTION, + SupportedFeature.PERSISTENT_REMAPPABLE_ACTION, 0x30, key >> 8, key & 0xFF, @@ -708,7 +713,7 @@ def diversion_offset_mask(self): def enabled(self): # is the gesture enabled? if self._enabled is None and self.index is not None: offset, mask = self.enable_offset_mask() - result = self._device.feature_request(FEATURE.GESTURE_2, 0x10, offset, 0x01, mask) + result = self._device.feature_request(SupportedFeature.GESTURE_2, 0x10, offset, 0x01, mask) self._enabled = bool(result[0] & mask) if result else None return self._enabled @@ -717,13 +722,15 @@ def set(self, enable): # enable or disable the gesture return None if self.index is not None: offset, mask = self.enable_offset_mask() - reply = self._device.feature_request(FEATURE.GESTURE_2, 0x20, offset, 0x01, mask, mask if enable else 0x00) + reply = self._device.feature_request( + SupportedFeature.GESTURE_2, 0x20, offset, 0x01, mask, mask if enable else 0x00 + ) return reply def diverted(self): # is the gesture diverted? if self._diverted is None and self.diversion_index is not None: offset, mask = self.diversion_offset_mask() - result = self._device.feature_request(FEATURE.GESTURE_2, 0x30, offset, 0x01, mask) + result = self._device.feature_request(SupportedFeature.GESTURE_2, 0x30, offset, 0x01, mask) self._diverted = bool(result[0] & mask) if result else None return self._diverted @@ -733,7 +740,7 @@ def divert(self, diverted): # divert or undivert the gesture if self.diversion_index is not None: offset, mask = self.diversion_offset_mask() reply = self._device.feature_request( - FEATURE.GESTURE_2, + SupportedFeature.GESTURE_2, 0x40, offset, 0x01, @@ -776,7 +783,7 @@ def value(self): return self._value if self._value is not None else self.read() def read(self): # returns the bytes for the parameter - result = self._device.feature_request(FEATURE.GESTURE_2, 0x70, self.index, 0xFF) + result = self._device.feature_request(SupportedFeature.GESTURE_2, 0x70, self.index, 0xFF) if result: self._value = common.bytes2int(result[: self.size]) return self._value @@ -788,14 +795,14 @@ def default_value(self): return self._default_value def _read_default(self): - result = self._device.feature_request(FEATURE.GESTURE_2, 0x60, self.index, 0xFF) + result = self._device.feature_request(SupportedFeature.GESTURE_2, 0x60, self.index, 0xFF) if result: self._default_value = common.bytes2int(result[: self.size]) return self._default_value def write(self, bytes): self._value = bytes - return self._device.feature_request(FEATURE.GESTURE_2, 0x80, self.index, bytes, 0xFF) + return self._device.feature_request(SupportedFeature.GESTURE_2, 0x80, self.index, bytes, 0xFF) def __str__(self): return str(self.param) @@ -820,7 +827,7 @@ def value(self): def read(self): try: - value = self._device.feature_request(FEATURE.GESTURE_2, 0x50, self.id, 0xFF) + value = self._device.feature_request(SupportedFeature.GESTURE_2, 0x50, self.id, 0xFF) except exceptions.FeatureCallError: # some calls produce an error (notably spec 5 multiplier on K400Plus) if logger.isEnabledFor(logging.WARNING): logger.warning( @@ -849,7 +856,7 @@ def __init__(self, device): field_high = 0x00 while field_high != 0x01: # end of fields # retrieve the next eight fields - fields = device.feature_request(FEATURE.GESTURE_2, 0x00, index >> 8, index & 0xFF) + fields = device.feature_request(SupportedFeature.GESTURE_2, 0x00, index >> 8, index & 0xFF) if not fields: break for offset in range(8): @@ -907,7 +914,7 @@ class Backlight: """Information about the current settings of x1982 Backlight2 v3, but also works for previous versions""" def __init__(self, device): - response = device.feature_request(FEATURE.BACKLIGHT2, 0x00) + response = device.feature_request(SupportedFeature.BACKLIGHT2, 0x00) if not response: raise exceptions.FeatureCallError(msg="No reply from device.") self.device = device @@ -923,7 +930,7 @@ def write(self): self.options = (self.options & 0x07) | (self.mode << 3) level = self.level if self.mode == 0x3 else 0 data_bytes = struct.pack(" 0x04: return @@ -1366,11 +1373,11 @@ def read_sector(cls, dev, sector, s): # doesn't check for valid sector or size bytes = b"" o = 0 while o < s - 15: - chunk = dev.feature_request(FEATURE.ONBOARD_PROFILES, 0x50, sector >> 8, sector & 0xFF, o >> 8, o & 0xFF) + chunk = dev.feature_request(SupportedFeature.ONBOARD_PROFILES, 0x50, sector >> 8, sector & 0xFF, o >> 8, o & 0xFF) bytes += chunk o += 16 chunk = dev.feature_request( - FEATURE.ONBOARD_PROFILES, + SupportedFeature.ONBOARD_PROFILES, 0x50, sector >> 8, sector & 0xFF, @@ -1385,12 +1392,12 @@ def write_sector(cls, device, s, bs): # doesn't check for valid sector or size rbs = OnboardProfiles.read_sector(device, s, len(bs)) if rbs[:-2] == bs[:-2]: return False - device.feature_request(FEATURE.ONBOARD_PROFILES, 0x60, s >> 8, s & 0xFF, 0, 0, len(bs) >> 8, len(bs) & 0xFF) + device.feature_request(SupportedFeature.ONBOARD_PROFILES, 0x60, s >> 8, s & 0xFF, 0, 0, len(bs) >> 8, len(bs) & 0xFF) o = 0 while o < len(bs) - 1: - device.feature_request(FEATURE.ONBOARD_PROFILES, 0x70, bs[o : o + 16]) + device.feature_request(SupportedFeature.ONBOARD_PROFILES, 0x70, bs[o : o + 16]) o += 16 - device.feature_request(FEATURE.ONBOARD_PROFILES, 0x80) + device.feature_request(SupportedFeature.ONBOARD_PROFILES, 0x80) return True def write(self, device): @@ -1449,13 +1456,13 @@ def get_firmware(self, device): :returns: a list of FirmwareInfo tuples, ordered by firmware layer. """ - count = device.feature_request(FEATURE.DEVICE_FW_VERSION) + count = device.feature_request(SupportedFeature.DEVICE_FW_VERSION) if count: count = ord(count[:1]) fw = [] for index in range(0, count): - fw_info = device.feature_request(FEATURE.DEVICE_FW_VERSION, 0x10, index) + fw_info = device.feature_request(SupportedFeature.DEVICE_FW_VERSION, 0x10, index) if fw_info: level = ord(fw_info[:1]) & 0x0F if level == 0 or level == 1: @@ -1475,7 +1482,7 @@ def get_firmware(self, device): def get_ids(self, device): """Reads a device's ids (unit and model numbers)""" - ids = device.feature_request(FEATURE.DEVICE_FW_VERSION) + ids = device.feature_request(SupportedFeature.DEVICE_FW_VERSION) if ids: unitId = ids[1:5] modelId = ids[7:13] @@ -1495,7 +1502,7 @@ def get_kind(self, device: Device): :returns: a string describing the device type, or ``None`` if the device is not available or does not support the ``DEVICE_NAME`` feature. """ - kind = device.feature_request(FEATURE.DEVICE_NAME, 0x20) + kind = device.feature_request(SupportedFeature.DEVICE_NAME, 0x20) if kind: kind = ord(kind[:1]) try: @@ -1509,13 +1516,13 @@ def get_name(self, device: Device): :returns: a string with the device name, or ``None`` if the device is not available or does not support the ``DEVICE_NAME`` feature. """ - name_length = device.feature_request(FEATURE.DEVICE_NAME) + name_length = device.feature_request(SupportedFeature.DEVICE_NAME) if name_length: name_length = ord(name_length[:1]) name = b"" while len(name) < name_length: - fragment = device.feature_request(FEATURE.DEVICE_NAME, 0x10, len(name)) + fragment = device.feature_request(SupportedFeature.DEVICE_NAME, 0x10, len(name)) if fragment: name += fragment[: name_length - len(name)] else: @@ -1530,13 +1537,13 @@ def get_friendly_name(self, device: Device): :returns: a string with the device name, or ``None`` if the device is not available or does not support the ``DEVICE_NAME`` feature. """ - name_length = device.feature_request(FEATURE.DEVICE_FRIENDLY_NAME) + name_length = device.feature_request(SupportedFeature.DEVICE_FRIENDLY_NAME) if name_length: name_length = ord(name_length[:1]) name = b"" while len(name) < name_length: - fragment = device.feature_request(FEATURE.DEVICE_FRIENDLY_NAME, 0x10, len(name)) + fragment = device.feature_request(SupportedFeature.DEVICE_FRIENDLY_NAME, 0x10, len(name)) if fragment: name += fragment[1 : name_length - len(name) + 1] else: @@ -1546,27 +1553,27 @@ def get_friendly_name(self, device: Device): return name.decode("utf-8") def get_battery_status(self, device: Device): - report = device.feature_request(FEATURE.BATTERY_STATUS) + report = device.feature_request(SupportedFeature.BATTERY_STATUS) if report: return decipher_battery_status(report) def get_battery_unified(self, device: Device): - report = device.feature_request(FEATURE.UNIFIED_BATTERY, 0x10) + report = device.feature_request(SupportedFeature.UNIFIED_BATTERY, 0x10) if report is not None: return decipher_battery_unified(report) def get_battery_voltage(self, device: Device): - report = device.feature_request(FEATURE.BATTERY_VOLTAGE) + report = device.feature_request(SupportedFeature.BATTERY_VOLTAGE) if report is not None: return decipher_battery_voltage(report) def get_adc_measurement(self, device: Device): try: # this feature call produces an error for headsets that are connected but inactive - report = device.feature_request(FEATURE.ADC_MEASUREMENT) + report = device.feature_request(SupportedFeature.ADC_MEASUREMENT) if report is not None: return decipher_adc_measurement(report) except exceptions.FeatureCallError: - return FEATURE.ADC_MEASUREMENT if FEATURE.ADC_MEASUREMENT in device.features else None + return SupportedFeature.ADC_MEASUREMENT if SupportedFeature.ADC_MEASUREMENT in device.features else None def get_battery(self, device, feature): """Return battery information - feature, approximate level, next, charging, voltage @@ -1588,39 +1595,39 @@ def get_battery(self, device, feature): def get_keys(self, device: Device): # TODO: add here additional variants for other REPROG_CONTROLS count = None - if FEATURE.REPROG_CONTROLS_V2 in device.features: - count = device.feature_request(FEATURE.REPROG_CONTROLS_V2) + if SupportedFeature.REPROG_CONTROLS_V2 in device.features: + count = device.feature_request(SupportedFeature.REPROG_CONTROLS_V2) return KeysArrayV2(device, ord(count[:1])) - elif FEATURE.REPROG_CONTROLS_V4 in device.features: - count = device.feature_request(FEATURE.REPROG_CONTROLS_V4) + elif SupportedFeature.REPROG_CONTROLS_V4 in device.features: + count = device.feature_request(SupportedFeature.REPROG_CONTROLS_V4) return KeysArrayV4(device, ord(count[:1])) return None def get_remap_keys(self, device: Device): - count = device.feature_request(FEATURE.PERSISTENT_REMAPPABLE_ACTION, 0x10) + count = device.feature_request(SupportedFeature.PERSISTENT_REMAPPABLE_ACTION, 0x10) if count: return KeysArrayPersistent(device, ord(count[:1])) def get_gestures(self, device: Device): if getattr(device, "_gestures", None) is not None: return device._gestures - if FEATURE.GESTURE_2 in device.features: + if SupportedFeature.GESTURE_2 in device.features: return Gestures(device) def get_backlight(self, device: Device): if getattr(device, "_backlight", None) is not None: return device._backlight - if FEATURE.BACKLIGHT2 in device.features: + if SupportedFeature.BACKLIGHT2 in device.features: return Backlight(device) def get_profiles(self, device: Device): if getattr(device, "_profiles", None) is not None: return device._profiles - if FEATURE.ONBOARD_PROFILES in device.features: + if SupportedFeature.ONBOARD_PROFILES in device.features: return OnboardProfiles.from_device(device) def get_mouse_pointer_info(self, device: Device): - pointer_info = device.feature_request(FEATURE.MOUSE_POINTER) + pointer_info = device.feature_request(SupportedFeature.MOUSE_POINTER) if pointer_info: dpi, flags = struct.unpack("!HB", pointer_info[:3]) acceleration = ("none", "low", "med", "high")[flags & 0x3] @@ -1634,7 +1641,7 @@ def get_mouse_pointer_info(self, device: Device): } def get_vertical_scrolling_info(self, device: Device): - vertical_scrolling_info = device.feature_request(FEATURE.VERTICAL_SCROLLING) + vertical_scrolling_info = device.feature_request(SupportedFeature.VERTICAL_SCROLLING) if vertical_scrolling_info: roller, ratchet, lines = struct.unpack("!BBB", vertical_scrolling_info[:3]) roller_type = ( @@ -1650,13 +1657,13 @@ def get_vertical_scrolling_info(self, device: Device): return {"roller": roller_type, "ratchet": ratchet, "lines": lines} def get_hi_res_scrolling_info(self, device: Device): - hi_res_scrolling_info = device.feature_request(FEATURE.HI_RES_SCROLLING) + hi_res_scrolling_info = device.feature_request(SupportedFeature.HI_RES_SCROLLING) if hi_res_scrolling_info: mode, resolution = struct.unpack("!BB", hi_res_scrolling_info[:2]) return mode, resolution def get_pointer_speed_info(self, device: Device): - pointer_speed_info = device.feature_request(FEATURE.POINTER_SPEED) + pointer_speed_info = device.feature_request(SupportedFeature.POINTER_SPEED) if pointer_speed_info: pointer_speed_hi, pointer_speed_lo = struct.unpack("!BB", pointer_speed_info[:2]) # if pointer_speed_lo > 0: @@ -1664,16 +1671,16 @@ def get_pointer_speed_info(self, device: Device): return pointer_speed_hi + pointer_speed_lo / 256 def get_lowres_wheel_status(self, device: Device): - lowres_wheel_status = device.feature_request(FEATURE.LOWRES_WHEEL) + lowres_wheel_status = device.feature_request(SupportedFeature.LOWRES_WHEEL) if lowres_wheel_status: wheel_flag = struct.unpack("!B", lowres_wheel_status[:1])[0] wheel_reporting = ("HID", "HID++")[wheel_flag & 0x01] return wheel_reporting def get_hires_wheel(self, device: Device): - caps = device.feature_request(FEATURE.HIRES_WHEEL, 0x00) - mode = device.feature_request(FEATURE.HIRES_WHEEL, 0x10) - ratchet = device.feature_request(FEATURE.HIRES_WHEEL, 0x030) + caps = device.feature_request(SupportedFeature.HIRES_WHEEL, 0x00) + mode = device.feature_request(SupportedFeature.HIRES_WHEEL, 0x10) + ratchet = device.feature_request(SupportedFeature.HIRES_WHEEL, 0x030) if caps and mode and ratchet: # Parse caps @@ -1697,7 +1704,7 @@ def get_hires_wheel(self, device: Device): return multi, has_invert, has_ratchet, inv, res, target, ratchet def get_new_fn_inversion(self, device: Device): - state = device.feature_request(FEATURE.NEW_FN_INVERSION, 0x00) + state = device.feature_request(SupportedFeature.NEW_FN_INVERSION, 0x00) if state: inverted, default_inverted = struct.unpack("!BB", state[:2]) inverted = (inverted & 0x01) != 0 @@ -1705,18 +1712,18 @@ def get_new_fn_inversion(self, device: Device): return inverted, default_inverted def get_host_names(self, device: Device): - state = device.feature_request(FEATURE.HOSTS_INFO, 0x00) + state = device.feature_request(SupportedFeature.HOSTS_INFO, 0x00) host_names = {} if state: capability_flags, _ignore, numHosts, currentHost = struct.unpack("!BBBB", state[:4]) if capability_flags & 0x01: # device can get host names for host in range(0, numHosts): - hostinfo = device.feature_request(FEATURE.HOSTS_INFO, 0x10, host) + hostinfo = device.feature_request(SupportedFeature.HOSTS_INFO, 0x10, host) _ignore, status, _ignore, _ignore, nameLen, _ignore = struct.unpack("!BBBBBB", hostinfo[:6]) name = "" remaining = nameLen while remaining > 0: - name_piece = device.feature_request(FEATURE.HOSTS_INFO, 0x30, host, nameLen - remaining) + name_piece = device.feature_request(SupportedFeature.HOSTS_INFO, 0x30, host, nameLen - remaining) if name_piece: name += name_piece[2 : 2 + min(remaining, 14)].decode() remaining = max(0, remaining - 14) @@ -1735,62 +1742,64 @@ def set_host_name(self, device: Device, name, currentName=""): currentName = bytearray(currentName, "utf-8") if logger.isEnabledFor(logging.INFO): logger.info("Setting host name to %s", name) - state = device.feature_request(FEATURE.HOSTS_INFO, 0x00) + state = device.feature_request(SupportedFeature.HOSTS_INFO, 0x00) if state: flags, _ignore, _ignore, currentHost = struct.unpack("!BBBB", state[:4]) if flags & 0x02: - hostinfo = device.feature_request(FEATURE.HOSTS_INFO, 0x10, currentHost) + hostinfo = device.feature_request(SupportedFeature.HOSTS_INFO, 0x10, currentHost) _ignore, _ignore, _ignore, _ignore, _ignore, maxNameLen = struct.unpack("!BBBBBB", hostinfo[:6]) if name[:maxNameLen] == currentName[:maxNameLen] and False: return True length = min(maxNameLen, len(name)) chunk = 0 while chunk < length: - response = device.feature_request(FEATURE.HOSTS_INFO, 0x40, currentHost, chunk, name[chunk : chunk + 14]) + response = device.feature_request( + SupportedFeature.HOSTS_INFO, 0x40, currentHost, chunk, name[chunk : chunk + 14] + ) if not response: return False chunk += 14 return True def get_onboard_mode(self, device: Device): - state = device.feature_request(FEATURE.ONBOARD_PROFILES, 0x20) + state = device.feature_request(SupportedFeature.ONBOARD_PROFILES, 0x20) if state: mode = struct.unpack("!B", state[:1])[0] return mode def set_onboard_mode(self, device: Device, mode): - state = device.feature_request(FEATURE.ONBOARD_PROFILES, 0x10, mode) + state = device.feature_request(SupportedFeature.ONBOARD_PROFILES, 0x10, mode) return state def get_polling_rate(self, device: Device): - state = device.feature_request(FEATURE.REPORT_RATE, 0x10) + state = device.feature_request(SupportedFeature.REPORT_RATE, 0x10) if state: rate = struct.unpack("!B", state[:1])[0] return str(rate) + "ms" else: rates = ["8ms", "4ms", "2ms", "1ms", "500us", "250us", "125us"] - state = device.feature_request(FEATURE.EXTENDED_ADJUSTABLE_REPORT_RATE, 0x20) + state = device.feature_request(SupportedFeature.EXTENDED_ADJUSTABLE_REPORT_RATE, 0x20) if state: rate = struct.unpack("!B", state[:1])[0] return rates[rate] def get_remaining_pairing(self, device: Device): - result = device.feature_request(FEATURE.REMAINING_PAIRING, 0x0) + result = device.feature_request(SupportedFeature.REMAINING_PAIRING, 0x0) if result: result = struct.unpack("!B", result[:1])[0] - FEATURE._fallback = lambda x: f"unknown:{x:04X}" + SupportedFeature._fallback = lambda x: f"unknown:{x:04X}" return result def config_change(self, device: Device, configuration, no_reply=False): - return device.feature_request(FEATURE.CONFIG_CHANGE, 0x10, configuration, no_reply=no_reply) + return device.feature_request(SupportedFeature.CONFIG_CHANGE, 0x10, configuration, no_reply=no_reply) battery_functions = { - FEATURE.BATTERY_STATUS: Hidpp20.get_battery_status, - FEATURE.BATTERY_VOLTAGE: Hidpp20.get_battery_voltage, - FEATURE.UNIFIED_BATTERY: Hidpp20.get_battery_unified, - FEATURE.ADC_MEASUREMENT: Hidpp20.get_adc_measurement, + SupportedFeature.BATTERY_STATUS: Hidpp20.get_battery_status, + SupportedFeature.BATTERY_VOLTAGE: Hidpp20.get_battery_voltage, + SupportedFeature.UNIFIED_BATTERY: Hidpp20.get_battery_unified, + SupportedFeature.ADC_MEASUREMENT: Hidpp20.get_adc_measurement, } @@ -1807,7 +1816,7 @@ def decipher_battery_status(report: FixedBytes5) -> Tuple[Any, Battery]: logger.debug( "battery status %s%% charged, next %s%%, status %s", battery_discharge_level, battery_discharge_next_level, status ) - return FEATURE.BATTERY_STATUS, Battery(battery_discharge_level, battery_discharge_next_level, status, None) + return SupportedFeature.BATTERY_STATUS, Battery(battery_discharge_level, battery_discharge_next_level, status, None) def decipher_battery_voltage(report): @@ -1845,7 +1854,7 @@ def decipher_battery_voltage(report): charge_lvl, charge_type, ) - return FEATURE.BATTERY_VOLTAGE, Battery(charge_lvl, None, status, voltage) + return SupportedFeature.BATTERY_VOLTAGE, Battery(charge_lvl, None, status, voltage) def decipher_battery_unified(report): @@ -1869,7 +1878,7 @@ def decipher_battery_unified(report): else: level = BatteryLevelApproximation.EMPTY - return FEATURE.UNIFIED_BATTERY, Battery(discharge if discharge else level, None, status, None) + return SupportedFeature.UNIFIED_BATTERY, Battery(discharge if discharge else level, None, status, None) def decipher_adc_measurement(report): @@ -1882,4 +1891,4 @@ def decipher_adc_measurement(report): break if flags & 0x01: status = BatteryStatus.RECHARGING if flags & 0x02 else BatteryStatus.DISCHARGING - return FEATURE.ADC_MEASUREMENT, Battery(charge_level, None, status, adc) + return SupportedFeature.ADC_MEASUREMENT, Battery(charge_level, None, status, adc) diff --git a/lib/logitech_receiver/hidpp20_constants.py b/lib/logitech_receiver/hidpp20_constants.py index 6499956d1..6fa2b1794 100644 --- a/lib/logitech_receiver/hidpp20_constants.py +++ b/lib/logitech_receiver/hidpp20_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 @@ -25,127 +26,131 @@ A particular device might not support all these features, and may support other unknown features as well. """ -FEATURE = NamedInts( - ROOT=0x0000, - FEATURE_SET=0x0001, - FEATURE_INFO=0x0002, + + +class SupportedFeature(IntEnum): + ROOT = 0x0000 + FEATURE_SET = 0x0001 + FEATURE_INFO = 0x0002 # Common - DEVICE_FW_VERSION=0x0003, - DEVICE_UNIT_ID=0x0004, - DEVICE_NAME=0x0005, - DEVICE_GROUPS=0x0006, - DEVICE_FRIENDLY_NAME=0x0007, - KEEP_ALIVE=0x0008, - CONFIG_CHANGE=0x0020, - CRYPTO_ID=0x0021, - TARGET_SOFTWARE=0x0030, - WIRELESS_SIGNAL_STRENGTH=0x0080, - DFUCONTROL_LEGACY=0x00C0, - DFUCONTROL_UNSIGNED=0x00C1, - DFUCONTROL_SIGNED=0x00C2, - DFUCONTROL=0x00C3, - DFU=0x00D0, - BATTERY_STATUS=0x1000, - BATTERY_VOLTAGE=0x1001, - UNIFIED_BATTERY=0x1004, - CHARGING_CONTROL=0x1010, - LED_CONTROL=0x1300, - FORCE_PAIRING=0x1500, - GENERIC_TEST=0x1800, - DEVICE_RESET=0x1802, - OOBSTATE=0x1805, - CONFIG_DEVICE_PROPS=0x1806, - CHANGE_HOST=0x1814, - HOSTS_INFO=0x1815, - BACKLIGHT=0x1981, - BACKLIGHT2=0x1982, - BACKLIGHT3=0x1983, - ILLUMINATION=0x1990, - PRESENTER_CONTROL=0x1A00, - SENSOR_3D=0x1A01, - REPROG_CONTROLS=0x1B00, - REPROG_CONTROLS_V2=0x1B01, - REPROG_CONTROLS_V2_2=0x1B02, # LogiOptions 2.10.73 features.xml - REPROG_CONTROLS_V3=0x1B03, - REPROG_CONTROLS_V4=0x1B04, - REPORT_HID_USAGE=0x1BC0, - PERSISTENT_REMAPPABLE_ACTION=0x1C00, - WIRELESS_DEVICE_STATUS=0x1D4B, - REMAINING_PAIRING=0x1DF0, - FIRMWARE_PROPERTIES=0x1F1F, - ADC_MEASUREMENT=0x1F20, + DEVICE_FW_VERSION = 0x0003 + DEVICE_UNIT_ID = 0x0004 + DEVICE_NAME = 0x0005 + DEVICE_GROUPS = 0x0006 + DEVICE_FRIENDLY_NAME = 0x0007 + KEEP_ALIVE = 0x0008 + CONFIG_CHANGE = 0x0020 + CRYPTO_ID = 0x0021 + TARGET_SOFTWARE = 0x0030 + WIRELESS_SIGNAL_STRENGTH = 0x0080 + DFUCONTROL_LEGACY = 0x00C0 + DFUCONTROL_UNSIGNED = 0x00C1 + DFUCONTROL_SIGNED = 0x00C2 + DFUCONTROL = 0x00C3 + DFU = 0x00D0 + BATTERY_STATUS = 0x1000 + BATTERY_VOLTAGE = 0x1001 + UNIFIED_BATTERY = 0x1004 + CHARGING_CONTROL = 0x1010 + LED_CONTROL = 0x1300 + FORCE_PAIRING = 0x1500 + GENERIC_TEST = 0x1800 + DEVICE_RESET = 0x1802 + OOBSTATE = 0x1805 + CONFIG_DEVICE_PROPS = 0x1806 + CHANGE_HOST = 0x1814 + HOSTS_INFO = 0x1815 + BACKLIGHT = 0x1981 + BACKLIGHT2 = 0x1982 + BACKLIGHT3 = 0x1983 + ILLUMINATION = 0x1990 + PRESENTER_CONTROL = 0x1A00 + SENSOR_3D = 0x1A01 + REPROG_CONTROLS = 0x1B00 + REPROG_CONTROLS_V2 = 0x1B01 + REPROG_CONTROLS_V2_2 = 0x1B02 # LogiOptions 2.10.73 features.xml + REPROG_CONTROLS_V3 = 0x1B03 + REPROG_CONTROLS_V4 = 0x1B04 + REPORT_HID_USAGE = 0x1BC0 + PERSISTENT_REMAPPABLE_ACTION = 0x1C00 + WIRELESS_DEVICE_STATUS = 0x1D4B + REMAINING_PAIRING = 0x1DF0 + FIRMWARE_PROPERTIES = 0x1F1F + ADC_MEASUREMENT = 0x1F20 # Mouse - LEFT_RIGHT_SWAP=0x2001, - SWAP_BUTTON_CANCEL=0x2005, - POINTER_AXIS_ORIENTATION=0x2006, - VERTICAL_SCROLLING=0x2100, - SMART_SHIFT=0x2110, - SMART_SHIFT_ENHANCED=0x2111, - HI_RES_SCROLLING=0x2120, - HIRES_WHEEL=0x2121, - LOWRES_WHEEL=0x2130, - THUMB_WHEEL=0x2150, - MOUSE_POINTER=0x2200, - ADJUSTABLE_DPI=0x2201, - EXTENDED_ADJUSTABLE_DPI=0x2202, - POINTER_SPEED=0x2205, - ANGLE_SNAPPING=0x2230, - SURFACE_TUNING=0x2240, - XY_STATS=0x2250, - WHEEL_STATS=0x2251, - HYBRID_TRACKING=0x2400, + LEFT_RIGHT_SWAP = 0x2001 + SWAP_BUTTON_CANCEL = 0x2005 + POINTER_AXIS_ORIENTATION = 0x2006 + VERTICAL_SCROLLING = 0x2100 + SMART_SHIFT = 0x2110 + SMART_SHIFT_ENHANCED = 0x2111 + HI_RES_SCROLLING = 0x2120 + HIRES_WHEEL = 0x2121 + LOWRES_WHEEL = 0x2130 + THUMB_WHEEL = 0x2150 + MOUSE_POINTER = 0x2200 + ADJUSTABLE_DPI = 0x2201 + EXTENDED_ADJUSTABLE_DPI = 0x2202 + POINTER_SPEED = 0x2205 + ANGLE_SNAPPING = 0x2230 + SURFACE_TUNING = 0x2240 + XY_STATS = 0x2250 + WHEEL_STATS = 0x2251 + HYBRID_TRACKING = 0x2400 # Keyboard - FN_INVERSION=0x40A0, - NEW_FN_INVERSION=0x40A2, - K375S_FN_INVERSION=0x40A3, - ENCRYPTION=0x4100, - LOCK_KEY_STATE=0x4220, - SOLAR_DASHBOARD=0x4301, - KEYBOARD_LAYOUT=0x4520, - KEYBOARD_DISABLE_KEYS=0x4521, - KEYBOARD_DISABLE_BY_USAGE=0x4522, - DUALPLATFORM=0x4530, - MULTIPLATFORM=0x4531, - KEYBOARD_LAYOUT_2=0x4540, - CROWN=0x4600, + FN_INVERSION = 0x40A0 + NEW_FN_INVERSION = 0x40A2 + K375S_FN_INVERSION = 0x40A3 + ENCRYPTION = 0x4100 + LOCK_KEY_STATE = 0x4220 + SOLAR_DASHBOARD = 0x4301 + KEYBOARD_LAYOUT = 0x4520 + KEYBOARD_DISABLE_KEYS = 0x4521 + KEYBOARD_DISABLE_BY_USAGE = 0x4522 + DUALPLATFORM = 0x4530 + MULTIPLATFORM = 0x4531 + KEYBOARD_LAYOUT_2 = 0x4540 + CROWN = 0x4600 # Touchpad - TOUCHPAD_FW_ITEMS=0x6010, - TOUCHPAD_SW_ITEMS=0x6011, - TOUCHPAD_WIN8_FW_ITEMS=0x6012, - TAP_ENABLE=0x6020, - TAP_ENABLE_EXTENDED=0x6021, - CURSOR_BALLISTIC=0x6030, - TOUCHPAD_RESOLUTION=0x6040, - TOUCHPAD_RAW_XY=0x6100, - TOUCHMOUSE_RAW_POINTS=0x6110, - TOUCHMOUSE_6120=0x6120, - GESTURE=0x6500, - GESTURE_2=0x6501, + TOUCHPAD_FW_ITEMS = 0x6010 + TOUCHPAD_SW_ITEMS = 0x6011 + TOUCHPAD_WIN8_FW_ITEMS = 0x6012 + TAP_ENABLE = 0x6020 + TAP_ENABLE_EXTENDED = 0x6021 + CURSOR_BALLISTIC = 0x6030 + TOUCHPAD_RESOLUTION = 0x6040 + TOUCHPAD_RAW_XY = 0x6100 + TOUCHMOUSE_RAW_POINTS = 0x6110 + TOUCHMOUSE_6120 = 0x6120 + GESTURE = 0x6500 + GESTURE_2 = 0x6501 # Gaming Devices - GKEY=0x8010, - MKEYS=0x8020, - MR=0x8030, - BRIGHTNESS_CONTROL=0x8040, - REPORT_RATE=0x8060, - EXTENDED_ADJUSTABLE_REPORT_RATE=0x8061, - COLOR_LED_EFFECTS=0x8070, - RGB_EFFECTS=0x8071, - PER_KEY_LIGHTING=0x8080, - PER_KEY_LIGHTING_V2=0x8081, - MODE_STATUS=0x8090, - ONBOARD_PROFILES=0x8100, - MOUSE_BUTTON_SPY=0x8110, - LATENCY_MONITORING=0x8111, - GAMING_ATTACHMENTS=0x8120, - FORCE_FEEDBACK=0x8123, + GKEY = 0x8010 + MKEYS = 0x8020 + MR = 0x8030 + BRIGHTNESS_CONTROL = 0x8040 + REPORT_RATE = 0x8060 + EXTENDED_ADJUSTABLE_REPORT_RATE = 0x8061 + COLOR_LED_EFFECTS = 0x8070 + RGB_EFFECTS = 0x8071 + PER_KEY_LIGHTING = 0x8080 + PER_KEY_LIGHTING_V2 = 0x8081 + MODE_STATUS = 0x8090 + ONBOARD_PROFILES = 0x8100 + MOUSE_BUTTON_SPY = 0x8110 + LATENCY_MONITORING = 0x8111 + GAMING_ATTACHMENTS = 0x8120 + FORCE_FEEDBACK = 0x8123 # Headsets - SIDETONE=0x8300, - EQUALIZER=0x8310, - HEADSET_OUT=0x8320, + SIDETONE = 0x8300 + EQUALIZER = 0x8310 + HEADSET_OUT = 0x8320 # Fake features for Solaar internal use - MOUSE_GESTURE=0xFE00, -) -FEATURE._fallback = lambda x: f"unknown:{x:04X}" + MOUSE_GESTURE = 0xFE00 + + def __str__(self): + return self.name.replace("_", " ") + FEATURE_FLAG = NamedInts(internal=0x20, hidden=0x40, obsolete=0x80) diff --git a/lib/logitech_receiver/notifications.py b/lib/logitech_receiver/notifications.py index 101baa68b..21492b127 100644 --- a/lib/logitech_receiver/notifications.py +++ b/lib/logitech_receiver/notifications.py @@ -40,7 +40,7 @@ _hidpp10 = hidpp10.Hidpp10() _hidpp20 = hidpp20.Hidpp20() -_F = hidpp20_constants.FEATURE +_F = hidpp20_constants.SupportedFeature notification_lock = threading.Lock() diff --git a/lib/logitech_receiver/settings.py b/lib/logitech_receiver/settings.py index 9f0eea251..f6674eb8c 100644 --- a/lib/logitech_receiver/settings.py +++ b/lib/logitech_receiver/settings.py @@ -629,8 +629,17 @@ class FeatureRW: default_read_fnid = 0x00 default_write_fnid = 0x10 - def __init__(self, feature, read_fnid=0x00, write_fnid=0x10, prefix=b"", suffix=b"", read_prefix=b"", no_reply=False): - assert isinstance(feature, NamedInt) + def __init__( + self, + feature: hidpp20_constants.SupportedFeature, + read_fnid=0x00, + write_fnid=0x10, + prefix=b"", + suffix=b"", + read_prefix=b"", + no_reply=False, + ): + assert isinstance(feature, hidpp20_constants.SupportedFeature) self.feature = feature self.read_fnid = read_fnid self.write_fnid = write_fnid @@ -658,13 +667,13 @@ class FeatureRWMap(FeatureRW): def __init__( self, - feature, + feature: hidpp20_constants.SupportedFeature, read_fnid=default_read_fnid, write_fnid=default_write_fnid, key_byte_count=default_key_byte_count, no_reply=False, ): - assert isinstance(feature, NamedInt) + assert isinstance(feature, hidpp20_constants.SupportedFeature) self.feature = feature self.read_fnid = read_fnid self.write_fnid = write_fnid @@ -1422,7 +1431,10 @@ def read(self, device): # need to return bytes, as if read from device def write(self, device, data_bytes): def handler(device, n): # Called on notification events from the device - if n.sub_id < 0x40 and device.features.get_feature(n.sub_id) == hidpp20_constants.FEATURE.REPROG_CONTROLS_V4: + if ( + n.sub_id < 0x40 + and device.features.get_feature(n.sub_id) == hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4 + ): if n.address == 0x00: cids = struct.unpack("!HHHH", n.data[:8]) if not self.pressed and int(self.key.key) in cids: # trigger key pressed @@ -1484,11 +1496,11 @@ def __init__(self, device, name=""): self.keys = [] # the keys that can initiate processing self.initiating_key = None # the key that did initiate processing self.active = False - self.feature_offset = device.features[hidpp20_constants.FEATURE.REPROG_CONTROLS_V4] + self.feature_offset = device.features[hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4] assert self.feature_offset is not False def handler(self, device, n): # Called on notification events from the device - if n.sub_id < 0x40 and device.features.get_feature(n.sub_id) == hidpp20_constants.FEATURE.REPROG_CONTROLS_V4: + if n.sub_id < 0x40 and device.features.get_feature(n.sub_id) == hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4: if n.address == 0x00: cids = struct.unpack("!HHHH", n.data[:8]) ## generalize to list of keys @@ -1556,7 +1568,7 @@ def key_action(self, key): # acction to take when some other diverted key is pr def apply_all_settings(device): - if device.features and hidpp20_constants.FEATURE.HIRES_WHEEL in device.features: + if device.features and hidpp20_constants.SupportedFeature.HIRES_WHEEL in device.features: time.sleep(0.2) # delay to try to get out of race condition with Linux HID++ driver persister = getattr(device, "persister", None) sensitives = persister.get("_sensitive", {}) if persister else {} diff --git a/lib/logitech_receiver/settings_templates.py b/lib/logitech_receiver/settings_templates.py index afa45433d..7828bba26 100644 --- a/lib/logitech_receiver/settings_templates.py +++ b/lib/logitech_receiver/settings_templates.py @@ -13,6 +13,8 @@ ## 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 __future__ import annotations + import enum import logging import socket @@ -20,6 +22,7 @@ import traceback from time import time +from typing import Any from typing import Callable from solaar.i18n import _ @@ -40,7 +43,7 @@ _hidpp20 = hidpp20.Hidpp20() _DK = hidpp10_constants.DEVICE_KIND -_F = hidpp20_constants.FEATURE +_F = hidpp20_constants.SupportedFeature _GG = hidpp20_constants.GESTURE _GP = hidpp20_constants.PARAM @@ -1795,18 +1798,20 @@ def build(cls, setting_class, device): ] -def check_feature(device, sclass): - if sclass.feature not in device.features: +def check_feature(device, settings_class: settings.Setting) -> None | bool | Any: + if settings_class.feature not in device.features: return - if sclass.min_version > device.features.get_feature_version(sclass.feature): + if settings_class.min_version > device.features.get_feature_version(settings_class.feature): return try: - detected = sclass.build(device) + detected = settings_class.build(device) if logger.isEnabledFor(logging.DEBUG): - logger.debug("check_feature %s [%s] detected %s", sclass.name, sclass.feature, detected) + logger.debug("check_feature %s [%s] detected %s", settings_class.name, settings_class.feature, detected) return detected except Exception as e: - logger.error("check_feature %s [%s] error %s\n%s", sclass.name, sclass.feature, e, traceback.format_exc()) + logger.error( + "check_feature %s [%s] error %s\n%s", settings_class.name, settings_class.feature, e, traceback.format_exc() + ) return False # differentiate from an error-free determination that the setting is not supported diff --git a/lib/solaar/cli/show.py b/lib/solaar/cli/show.py index f9650b2be..79fc3dbe5 100644 --- a/lib/solaar/cli/show.py +++ b/lib/solaar/cli/show.py @@ -24,6 +24,7 @@ from logitech_receiver.common import LOGITECH_VENDOR_ID from logitech_receiver.common import NamedInt from logitech_receiver.common import strhex +from logitech_receiver.hidpp20_constants import SupportedFeature from solaar import NAME from solaar import __version__ @@ -152,7 +153,7 @@ def _print_device(dev, num=None): version = dev.features.get_feature_version(int(feature)) version = version if version else 0 print(" %2d: %-22s {%04X} V%s %s " % (index, feature, feature, version, ", ".join(flags))) - if feature == hidpp20_constants.FEATURE.HIRES_WHEEL: + if feature == SupportedFeature.HIRES_WHEEL: wheel = _hidpp20.get_hires_wheel(dev) if wheel: multi, has_invert, has_switch, inv, res, target, ratchet = wheel @@ -169,7 +170,7 @@ def _print_device(dev, num=None): print(" HID++ notification") else: print(" HID notification") - elif feature == hidpp20_constants.FEATURE.MOUSE_POINTER: + elif feature == SupportedFeature.MOUSE_POINTER: mouse_pointer = _hidpp20.get_mouse_pointer_info(dev) if mouse_pointer: print(f" DPI: {mouse_pointer['dpi']}") @@ -182,13 +183,13 @@ def _print_device(dev, num=None): print(" Provide vertical tuning, trackball") else: print(" No vertical tuning, standard mice") - elif feature == hidpp20_constants.FEATURE.VERTICAL_SCROLLING: + elif feature == SupportedFeature.VERTICAL_SCROLLING: vertical_scrolling_info = _hidpp20.get_vertical_scrolling_info(dev) if vertical_scrolling_info: print(f" Roller type: {vertical_scrolling_info['roller']}") print(f" Ratchet per turn: {vertical_scrolling_info['ratchet']}") print(f" Scroll lines: {vertical_scrolling_info['lines']}") - elif feature == hidpp20_constants.FEATURE.HI_RES_SCROLLING: + elif feature == SupportedFeature.HI_RES_SCROLLING: scrolling_mode, scrolling_resolution = _hidpp20.get_hi_res_scrolling_info(dev) if scrolling_mode: print(" Hi-res scrolling enabled") @@ -196,30 +197,30 @@ def _print_device(dev, num=None): print(" Hi-res scrolling disabled") if scrolling_resolution: print(f" Hi-res scrolling multiplier: {scrolling_resolution}") - elif feature == hidpp20_constants.FEATURE.POINTER_SPEED: + elif feature == SupportedFeature.POINTER_SPEED: pointer_speed = _hidpp20.get_pointer_speed_info(dev) if pointer_speed: print(f" Pointer Speed: {pointer_speed}") - elif feature == hidpp20_constants.FEATURE.LOWRES_WHEEL: + elif feature == SupportedFeature.LOWRES_WHEEL: wheel_status = _hidpp20.get_lowres_wheel_status(dev) if wheel_status: print(f" Wheel Reports: {wheel_status}") - elif feature == hidpp20_constants.FEATURE.NEW_FN_INVERSION: + elif feature == SupportedFeature.NEW_FN_INVERSION: inversion = _hidpp20.get_new_fn_inversion(dev) if inversion: inverted, default_inverted = inversion print(" Fn-swap:", "enabled" if inverted else "disabled") print(" Fn-swap default:", "enabled" if default_inverted else "disabled") - elif feature == hidpp20_constants.FEATURE.HOSTS_INFO: + elif feature == SupportedFeature.HOSTS_INFO: host_names = _hidpp20.get_host_names(dev) for host, (paired, name) in host_names.items(): print(f" Host {host} ({'paired' if paired else 'unpaired'}): {name}") - elif feature == hidpp20_constants.FEATURE.DEVICE_NAME: + elif feature == SupportedFeature.DEVICE_NAME: print(f" Name: {_hidpp20.get_name(dev)}") print(f" Kind: {_hidpp20.get_kind(dev)}") - elif feature == hidpp20_constants.FEATURE.DEVICE_FRIENDLY_NAME: + elif feature == SupportedFeature.DEVICE_FRIENDLY_NAME: print(f" Friendly Name: {_hidpp20.get_friendly_name(dev)}") - elif feature == hidpp20_constants.FEATURE.DEVICE_FW_VERSION: + elif feature == SupportedFeature.DEVICE_FW_VERSION: for fw in _hidpp20.get_firmware(dev): extras = strhex(fw.extras) if fw.extras else "" print(f" Firmware: {fw.kind} {fw.name} {fw.version} {extras}") @@ -227,17 +228,14 @@ def _print_device(dev, num=None): if ids: unitId, modelId, tid_map = ids print(f" Unit ID: {unitId} Model ID: {modelId} Transport IDs: {tid_map}") - elif ( - feature == hidpp20_constants.FEATURE.REPORT_RATE - or feature == hidpp20_constants.FEATURE.EXTENDED_ADJUSTABLE_REPORT_RATE - ): + elif feature == SupportedFeature.REPORT_RATE or feature == SupportedFeature.EXTENDED_ADJUSTABLE_REPORT_RATE: print(f" Report Rate: {_hidpp20.get_polling_rate(dev)}") - elif feature == hidpp20_constants.FEATURE.CONFIG_CHANGE: - response = dev.feature_request(hidpp20_constants.FEATURE.CONFIG_CHANGE, 0x00) + elif feature == SupportedFeature.CONFIG_CHANGE: + response = dev.feature_request(SupportedFeature.CONFIG_CHANGE, 0x00) print(f" Configuration: {response.hex()}") - elif feature == hidpp20_constants.FEATURE.REMAINING_PAIRING: + elif feature == SupportedFeature.REMAINING_PAIRING: print(f" Remaining Pairings: {int(_hidpp20.get_remaining_pairing(dev))}") - elif feature == hidpp20_constants.FEATURE.ONBOARD_PROFILES: + elif feature == SupportedFeature.ONBOARD_PROFILES: if _hidpp20.get_onboard_mode(dev) == hidpp20_constants.ONBOARD_MODES.MODE_HOST: mode = "Host" else: @@ -267,9 +265,9 @@ def _print_device(dev, num=None): print(f" Has {len(dev.keys)} reprogrammable keys:") for k in dev.keys: # TODO: add here additional variants for other REPROG_CONTROLS - if dev.keys.keyversion == hidpp20_constants.FEATURE.REPROG_CONTROLS_V2: + if dev.keys.keyversion == SupportedFeature.REPROG_CONTROLS_V2: print(" %2d: %-26s => %-27s %s" % (k.index, k.key, k.default_task, ", ".join(k.flags))) - if dev.keys.keyversion == hidpp20_constants.FEATURE.REPROG_CONTROLS_V4: + if dev.keys.keyversion == SupportedFeature.REPROG_CONTROLS_V4: print(" %2d: %-26s, default: %-27s => %-26s" % (k.index, k.key, k.default_task, k.mapped_to)) gmask_fmt = ",".join(k.group_mask) gmask_fmt = gmask_fmt if gmask_fmt else "empty" diff --git a/lib/solaar/ui/rule_conditions.py b/lib/solaar/ui/rule_conditions.py index 77a580458..82fb8429e 100644 --- a/lib/solaar/ui/rule_conditions.py +++ b/lib/solaar/ui/rule_conditions.py @@ -18,7 +18,7 @@ from gi.repository import Gtk from logitech_receiver import diversion from logitech_receiver.diversion import Key -from logitech_receiver.hidpp20 import FEATURE +from logitech_receiver.hidpp20 import SupportedFeature from logitech_receiver.special_keys import CONTROL from solaar.i18n import _ @@ -97,15 +97,15 @@ def right_label(cls, component): class FeatureUI(ConditionUI): CLASS = diversion.Feature FEATURES_WITH_DIVERSION = [ - str(FEATURE.CROWN), - str(FEATURE.THUMB_WHEEL), - str(FEATURE.LOWRES_WHEEL), - str(FEATURE.HIRES_WHEEL), - str(FEATURE.GESTURE_2), - str(FEATURE.REPROG_CONTROLS_V4), - str(FEATURE.GKEY), - str(FEATURE.MKEYS), - str(FEATURE.MR), + str(SupportedFeature.CROWN), + str(SupportedFeature.THUMB_WHEEL), + str(SupportedFeature.LOWRES_WHEEL), + str(SupportedFeature.HIRES_WHEEL), + str(SupportedFeature.GESTURE_2), + str(SupportedFeature.REPROG_CONTROLS_V4), + str(SupportedFeature.GKEY), + str(SupportedFeature.MKEYS), + str(SupportedFeature.MR), ] def create_widgets(self): @@ -120,7 +120,7 @@ def create_widgets(self): self.field.set_valign(Gtk.Align.CENTER) self.field.set_size_request(600, 0) self.field.connect("changed", self._on_update) - all_features = [str(f) for f in FEATURE] + all_features = [str(f) for f in SupportedFeature] CompletionEntry.add_completion_to_entry(self.field.get_child(), all_features) self.widgets[self.field] = (0, 1, 1, 1) diff --git a/tests/logitech_receiver/fake_hidpp.py b/tests/logitech_receiver/fake_hidpp.py index 08b715c3f..5b47e48ef 100644 --- a/tests/logitech_receiver/fake_hidpp.py +++ b/tests/logitech_receiver/fake_hidpp.py @@ -407,8 +407,17 @@ def __post_init__(self): self.settings = [] if self.feature is not None: self.features = hidpp20.FeaturesArray(self) - self.responses = [Response("010001", 0x0000, "0001"), Response("20", 0x0100)] + self.responses - self.responses.append(Response(f"{self.offset:0>2X}00{self.version:0>2X}", 0x0000, f"{self.feature:0>4X}")) + self.responses = [ + Response("010001", 0x0000, "0001"), + Response("20", 0x0100), + ] + self.responses + self.responses.append( + Response( + f"{int(self.offset):0>2X}00{int(self.version):0>2X}", + 0x0000, + f"{int(self.feature):0>4X}", + ) + ) if self.setting_callback is None: self.setting_callback = lambda x, y, z: None self.add_notification_handler = lambda x, y: None diff --git a/tests/logitech_receiver/test_diversion.py b/tests/logitech_receiver/test_diversion.py index d2fcb703a..70aba163f 100644 --- a/tests/logitech_receiver/test_diversion.py +++ b/tests/logitech_receiver/test_diversion.py @@ -7,7 +7,7 @@ from logitech_receiver import diversion from logitech_receiver.base import HIDPPNotification -from logitech_receiver.hidpp20_constants import FEATURE +from logitech_receiver.hidpp20_constants import SupportedFeature @pytest.fixture @@ -104,14 +104,14 @@ def test_feature(): "feature, data", [ ( - FEATURE.REPROG_CONTROLS_V4, + SupportedFeature.REPROG_CONTROLS_V4, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], ), - (FEATURE.GKEY, [0x01, 0x02, 0x03, 0x04]), - (FEATURE.MKEYS, [0x01, 0x02, 0x03, 0x04]), - (FEATURE.MR, [0x01, 0x02, 0x03, 0x04]), - (FEATURE.THUMB_WHEEL, [0x01, 0x02, 0x03, 0x04, 0x05]), - (FEATURE.DEVICE_UNIT_ID, [0x01, 0x02, 0x03, 0x04, 0x05]), + (SupportedFeature.GKEY, [0x01, 0x02, 0x03, 0x04]), + (SupportedFeature.MKEYS, [0x01, 0x02, 0x03, 0x04]), + (SupportedFeature.MR, [0x01, 0x02, 0x03, 0x04]), + (SupportedFeature.THUMB_WHEEL, [0x01, 0x02, 0x03, 0x04, 0x05]), + (SupportedFeature.DEVICE_UNIT_ID, [0x01, 0x02, 0x03, 0x04, 0x05]), ], ) def test_process_notification(feature, data): diff --git a/tests/logitech_receiver/test_hidpp20_complex.py b/tests/logitech_receiver/test_hidpp20_complex.py index 3b4632ef5..1d8c752b2 100644 --- a/tests/logitech_receiver/test_hidpp20_complex.py +++ b/tests/logitech_receiver/test_hidpp20_complex.py @@ -56,7 +56,7 @@ def test_FeaturesArray_check(device, expected_result, expected_count): assert result == expected_result assert result2 == expected_result - assert (hidpp20_constants.FEATURE.ROOT in featuresarray) == expected_result + assert (hidpp20_constants.SupportedFeature.ROOT in featuresarray) == expected_result assert len(featuresarray) == expected_count assert bool(featuresarray) == expected_result @@ -65,7 +65,7 @@ def test_FeaturesArray_check(device, expected_result, expected_count): "device, expected0, expected1, expected2, expected5, expected5v", [ (device_zerofeatures, None, None, None, None, None), - (device_standard, 0x0000, 0x0001, 0x0020, hidpp20_constants.FEATURE.REPROG_CONTROLS_V4, 3), + (device_standard, 0x0000, 0x0001, 0x0020, hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4, 3), ], ) def test_FeaturesArray_get_feature(device, expected0, expected1, expected2, expected5, expected5v): @@ -77,7 +77,7 @@ def test_FeaturesArray_get_feature(device, expected0, expected1, expected2, expe result2 = featuresarray.get_feature(2) result5 = featuresarray.get_feature(5) result2r = featuresarray.get_feature(2) - result5v = featuresarray.get_feature_version(hidpp20_constants.FEATURE.REPROG_CONTROLS_V4) + result5v = featuresarray.get_feature_version(hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4) assert result0 == expected0 assert result1 == expected1 @@ -94,15 +94,15 @@ def test_FeaturesArray_get_feature(device, expected0, expected1, expected2, expe ( device_standard, [ - (hidpp20_constants.FEATURE.ROOT, 0), - (hidpp20_constants.FEATURE.FEATURE_SET, 1), - (hidpp20_constants.FEATURE.CONFIG_CHANGE, 2), - (hidpp20_constants.FEATURE.DEVICE_FW_VERSION, 3), + (hidpp20_constants.SupportedFeature.ROOT, 0), + (hidpp20_constants.SupportedFeature.FEATURE_SET, 1), + (hidpp20_constants.SupportedFeature.CONFIG_CHANGE, 2), + (hidpp20_constants.SupportedFeature.DEVICE_FW_VERSION, 3), (common.NamedInt(256, "unknown:0100"), 4), - (hidpp20_constants.FEATURE.REPROG_CONTROLS_V4, 5), + (hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4, 5), (None, 6), (None, 7), - (hidpp20_constants.FEATURE.BATTERY_STATUS, 8), + (hidpp20_constants.SupportedFeature.BATTERY_STATUS, 8), ], ), ], @@ -118,12 +118,12 @@ def test_FeaturesArray_enumerate(device, expected_result): def test_FeaturesArray_setitem(): featuresarray = hidpp20.FeaturesArray(device_standard) - featuresarray[hidpp20_constants.FEATURE.ROOT] = 3 - featuresarray[hidpp20_constants.FEATURE.FEATURE_SET] = 5 - featuresarray[hidpp20_constants.FEATURE.FEATURE_SET] = 4 + featuresarray[hidpp20_constants.SupportedFeature.ROOT] = 3 + featuresarray[hidpp20_constants.SupportedFeature.FEATURE_SET] = 5 + featuresarray[hidpp20_constants.SupportedFeature.FEATURE_SET] = 4 - assert featuresarray[hidpp20_constants.FEATURE.FEATURE_SET] == 4 - assert featuresarray.inverse[4] == hidpp20_constants.FEATURE.FEATURE_SET + assert featuresarray[hidpp20_constants.SupportedFeature.FEATURE_SET] == 4 + assert featuresarray.inverse[4] == hidpp20_constants.SupportedFeature.FEATURE_SET def test_FeaturesArray_delitem(): @@ -141,10 +141,10 @@ def test_FeaturesArray_getitem(device, expected0, expected1, expected2, expected featuresarray = hidpp20.FeaturesArray(device) device.features = featuresarray - result_get0 = featuresarray[hidpp20_constants.FEATURE.ROOT] - result_get1 = featuresarray[hidpp20_constants.FEATURE.REPROG_CONTROLS_V4] - result_get2 = featuresarray[hidpp20_constants.FEATURE.GKEY] - result_1v = featuresarray.get_feature_version(hidpp20_constants.FEATURE.REPROG_CONTROLS_V4) + result_get0 = featuresarray[hidpp20_constants.SupportedFeature.ROOT] + result_get1 = featuresarray[hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4] + result_get2 = featuresarray[hidpp20_constants.SupportedFeature.GKEY] + result_1v = featuresarray.get_feature_version(hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4) assert result_get0 == expected0 assert result_get1 == expected1 @@ -220,7 +220,9 @@ def test_ReprogrammableKeyV4_key(device, index, cid, tid, flags, pos, group, gma ) # these fields need access all the key data, so start by setting up a device and its key data def test_ReprogrammableKeyV4_query(responses, index, mapped_to, remappable_to, mapping_flags): - device = fake_hidpp.Device("KEY", responses=responses, feature=hidpp20_constants.FEATURE.REPROG_CONTROLS_V4, offset=5) + device = fake_hidpp.Device( + "KEY", responses=responses, feature=hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4, offset=5 + ) device._keys = _hidpp20.get_keys(device) key = device.keys[index] @@ -241,7 +243,9 @@ def test_ReprogrammableKeyV4_query(responses, index, mapped_to, remappable_to, m ) def test_ReprogrammableKeyV4_set(responses, index, diverted, persistently_diverted, rawXY_reporting, remap, sets, mocker): responses += [fake_hidpp.Response(r, 0x530, r) for r in sets] - device = fake_hidpp.Device("KEY", responses=responses, feature=hidpp20_constants.FEATURE.REPROG_CONTROLS_V4, offset=5) + device = fake_hidpp.Device( + "KEY", responses=responses, feature=hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4, offset=5 + ) device._keys = _hidpp20.get_keys(device) device._keys._ensure_all_keys_queried() # do this now so that the last requests are sets spy_request = mocker.spy(device, "request") @@ -299,7 +303,9 @@ def test_remappable_action(r, index, cid, actionId, remapped, mask, status, acti fake_hidpp.Response("040000", 0x0000, "1C00"), fake_hidpp.Response("00", 0x440, f"{cid:04X}" + "FF" + remap), ] - device = fake_hidpp.Device("KEY", responses=responses, feature=hidpp20_constants.FEATURE.REPROG_CONTROLS_V4, offset=5) + device = fake_hidpp.Device( + "KEY", responses=responses, feature=hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4, offset=5 + ) key = hidpp20.PersistentRemappableAction(device, index, cid, actionId, remapped, mask, status) spy_request = mocker.spy(device, "request") @@ -387,7 +393,7 @@ def test_KeysArrayV4_index(key, index): device_key = fake_hidpp.Device( - "KEY", responses=fake_hidpp.responses_key, feature=hidpp20_constants.FEATURE.REPROG_CONTROLS_V4, offset=5 + "KEY", responses=fake_hidpp.responses_key, feature=hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4, offset=5 ) @@ -450,7 +456,9 @@ def test_KeysArrayPersistent_index_error(device, index): ], ) def test_KeysArrayPersistent_key(responses, key, index, mapped_to, capabilities): - device = fake_hidpp.Device("REMAP", responses=responses, feature=hidpp20_constants.FEATURE.PERSISTENT_REMAPPABLE_ACTION) + device = fake_hidpp.Device( + "REMAP", responses=responses, feature=hidpp20_constants.SupportedFeature.PERSISTENT_REMAPPABLE_ACTION + ) device._remap_keys = _hidpp20.get_remap_keys(device) device._remap_keys._ensure_all_keys_queried() @@ -510,7 +518,7 @@ def test_Gesture(device, low, high, next_index, next_diversion_index, name, cbe, ], ) def test_Gesture_set(responses, gest, enabled, diverted, set_result, unset_result, divert_result, undivert_result): - device = fake_hidpp.Device("GESTURE", responses=responses, feature=hidpp20_constants.FEATURE.GESTURE_2) + device = fake_hidpp.Device("GESTURE", responses=responses, feature=hidpp20_constants.SupportedFeature.GESTURE_2) gestures = _hidpp20.get_gestures(device) gesture = gestures.gesture(gest) @@ -530,7 +538,7 @@ def test_Gesture_set(responses, gest, enabled, diverted, set_result, unset_resul ], ) def test_Param(responses, prm, id, index, size, value, default_value, write1, write2): - device = fake_hidpp.Device("GESTURE", responses=responses, feature=hidpp20_constants.FEATURE.GESTURE_2) + device = fake_hidpp.Device("GESTURE", responses=responses, feature=hidpp20_constants.SupportedFeature.GESTURE_2) gestures = _hidpp20.get_gestures(device) param = gestures.param(prm) @@ -555,7 +563,7 @@ def test_Param(responses, prm, id, index, size, value, default_value, write1, wr ], ) def test_Spec(responses, id, s, byte_count, value, string): - device = fake_hidpp.Device("GESTURE", responses=responses, feature=hidpp20_constants.FEATURE.GESTURE_2) + device = fake_hidpp.Device("GESTURE", responses=responses, feature=hidpp20_constants.SupportedFeature.GESTURE_2) gestures = _hidpp20.get_gestures(device) spec = gestures.specs[id] @@ -569,7 +577,7 @@ def test_Spec(responses, id, s, byte_count, value, string): def test_Gestures(): device = fake_hidpp.Device( - "GESTURES", responses=fake_hidpp.responses_gestures, feature=hidpp20_constants.FEATURE.GESTURE_2 + "GESTURES", responses=fake_hidpp.responses_gestures, feature=hidpp20_constants.SupportedFeature.GESTURE_2 ) gestures = _hidpp20.get_gestures(device) @@ -600,7 +608,9 @@ def test_Gestures(): fake_hidpp.Response("0101FF00020003000400", 0x0410, "0101FF00020003000400"), ] -device_backlight = fake_hidpp.Device("BACKLIGHT", responses=responses_backlight, feature=hidpp20_constants.FEATURE.BACKLIGHT2) +device_backlight = fake_hidpp.Device( + "BACKLIGHT", responses=responses_backlight, feature=hidpp20_constants.SupportedFeature.BACKLIGHT2 +) def test_Backlight(): @@ -655,7 +665,7 @@ def test_LEDEffectSetting(hex, ID, color, speed, period, intensity, ramp, form): "feature, function, response, ID, capabilities, period", [ [ - hidpp20_constants.FEATURE.COLOR_LED_EFFECTS, + hidpp20_constants.SupportedFeature.COLOR_LED_EFFECTS, 0x20, fake_hidpp.Response("0102000300040005", 0x0420, "010200"), 3, @@ -663,7 +673,7 @@ def test_LEDEffectSetting(hex, ID, color, speed, period, intensity, ramp, form): 5, ], [ - hidpp20_constants.FEATURE.COLOR_LED_EFFECTS, + hidpp20_constants.SupportedFeature.COLOR_LED_EFFECTS, 0x20, fake_hidpp.Response("0102000700080009", 0x0420, "010200"), 7, @@ -687,8 +697,8 @@ def test_LEDEffectInfo(feature, function, response, ID, capabilities, period): @pytest.mark.parametrize( "feature, function, offset, effect_function, responses, index, location, count, id_1", [ - [hidpp20_constants.FEATURE.COLOR_LED_EFFECTS, 0x10, 0, 0x20, fake_hidpp.zone_responses_1, 0, 1, 2, 0xB], - [hidpp20_constants.FEATURE.RGB_EFFECTS, 0x00, 1, 0x00, fake_hidpp.zone_responses_2, 0, 1, 2, 2], + [hidpp20_constants.SupportedFeature.COLOR_LED_EFFECTS, 0x10, 0, 0x20, fake_hidpp.zone_responses_1, 0, 1, 2, 0xB], + [hidpp20_constants.SupportedFeature.RGB_EFFECTS, 0x00, 1, 0x00, fake_hidpp.zone_responses_2, 0, 1, 2, 2], ], ) def test_LEDZoneInfo(feature, function, offset, effect_function, responses, index, location, count, id_1): @@ -716,8 +726,8 @@ def test_LEDZoneInfo(feature, function, offset, effect_function, responses, inde ], ) def test_LEDZoneInfo_to_command(responses, setting, expected_command): - device = fake_hidpp.Device(feature=hidpp20_constants.FEATURE.COLOR_LED_EFFECTS, responses=responses, offset=0x07) - zone = hidpp20.LEDZoneInfo(hidpp20_constants.FEATURE.COLOR_LED_EFFECTS, 0x10, 0, 0x20, device, 0) + device = fake_hidpp.Device(feature=hidpp20_constants.SupportedFeature.COLOR_LED_EFFECTS, responses=responses, offset=0x07) + zone = hidpp20.LEDZoneInfo(hidpp20_constants.SupportedFeature.COLOR_LED_EFFECTS, 0x10, 0, 0x20, device, 0) command = zone.to_command(setting) @@ -727,8 +737,15 @@ def test_LEDZoneInfo_to_command(responses, setting, expected_command): @pytest.mark.parametrize( "feature, cls, responses, readable, count, count_0", [ - [hidpp20_constants.FEATURE.COLOR_LED_EFFECTS, hidpp20.LEDEffectsInfo, fake_hidpp.effects_responses_1, 1, 1, 2], - [hidpp20_constants.FEATURE.RGB_EFFECTS, hidpp20.RGBEffectsInfo, fake_hidpp.effects_responses_2, 1, 1, 2], + [ + hidpp20_constants.SupportedFeature.COLOR_LED_EFFECTS, + hidpp20.LEDEffectsInfo, + fake_hidpp.effects_responses_1, + 1, + 1, + 2, + ], + [hidpp20_constants.SupportedFeature.RGB_EFFECTS, hidpp20.RGBEffectsInfo, fake_hidpp.effects_responses_2, 1, 1, 2], ], ) def test_LED_RGB_EffectsInfo(feature, cls, responses, readable, count, count_0): @@ -857,7 +874,7 @@ def test_OnboardProfile_bytes(hex, name, sector, enabled, buttons, gbuttons, res ) def test_OnboardProfiles_device(responses, name, count, buttons, gbuttons, sectors, size): device = fake_hidpp.Device( - name, True, 4.5, responses=responses, feature=hidpp20_constants.FEATURE.ONBOARD_PROFILES, offset=0x9 + name, True, 4.5, responses=responses, feature=hidpp20_constants.SupportedFeature.ONBOARD_PROFILES, offset=0x9 ) device._profiles = None profiles = _hidpp20.get_profiles(device) diff --git a/tests/logitech_receiver/test_hidpp20_simple.py b/tests/logitech_receiver/test_hidpp20_simple.py index 983322f2d..43ae4d3f3 100644 --- a/tests/logitech_receiver/test_hidpp20_simple.py +++ b/tests/logitech_receiver/test_hidpp20_simple.py @@ -18,7 +18,7 @@ from logitech_receiver import common from logitech_receiver import hidpp20 -from logitech_receiver import hidpp20_constants +from logitech_receiver.hidpp20_constants import SupportedFeature from . import fake_hidpp @@ -31,7 +31,7 @@ def test_get_firmware(): fake_hidpp.Response("01414243030401000101000102030405", 0x0410, "00"), fake_hidpp.Response("02414243030401000101000102030405", 0x0410, "01"), ] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.DEVICE_FW_VERSION) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.DEVICE_FW_VERSION) result = _hidpp20.get_firmware(device) @@ -42,7 +42,7 @@ def test_get_firmware(): def test_get_ids(): responses = [fake_hidpp.Response("FF12345678000D123456789ABC", 0x0400)] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.DEVICE_FW_VERSION) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.DEVICE_FW_VERSION) unitId, modelId, tid_map = _hidpp20.get_ids(device) @@ -53,7 +53,7 @@ def test_get_ids(): def test_get_kind(): responses = [fake_hidpp.Response("00", 0x0420)] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.DEVICE_NAME) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.DEVICE_NAME) result = _hidpp20.get_kind(device) @@ -67,7 +67,7 @@ def test_get_name(): fake_hidpp.Response("4142434445464748494A4B4C4D4E4F", 0x0410, "00"), fake_hidpp.Response("505152530000000000000000000000", 0x0410, "0F"), ] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.DEVICE_NAME) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.DEVICE_NAME) result = _hidpp20.get_name(device) @@ -80,7 +80,7 @@ def test_get_friendly_name(): fake_hidpp.Response("004142434445464748494A4B4C4D4E", 0x0410, "00"), fake_hidpp.Response("0E4F50515253000000000000000000", 0x0410, "0E"), ] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.DEVICE_FRIENDLY_NAME) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.DEVICE_FRIENDLY_NAME) result = _hidpp20.get_friendly_name(device) @@ -89,11 +89,11 @@ def test_get_friendly_name(): def test_get_battery_status(): responses = [fake_hidpp.Response("502000FFFF", 0x0400)] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.BATTERY_STATUS) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.BATTERY_STATUS) feature, battery = _hidpp20.get_battery_status(device) - assert feature == hidpp20_constants.FEATURE.BATTERY_STATUS + assert feature == SupportedFeature.BATTERY_STATUS assert battery.level == 80 assert battery.next_level == 32 assert battery.status == common.BatteryStatus.DISCHARGING @@ -101,11 +101,11 @@ def test_get_battery_status(): def test_get_battery_voltage(): responses = [fake_hidpp.Response("1000FFFFFF", 0x0400)] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.BATTERY_VOLTAGE) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.BATTERY_VOLTAGE) feature, battery = _hidpp20.get_battery_voltage(device) - assert feature == hidpp20_constants.FEATURE.BATTERY_VOLTAGE + assert feature == SupportedFeature.BATTERY_VOLTAGE assert battery.level == 90 assert battery.status == common.BatteryStatus.RECHARGING assert battery.voltage == 0x1000 @@ -113,22 +113,22 @@ def test_get_battery_voltage(): def test_get_battery_unified(): responses = [fake_hidpp.Response("500100FFFF", 0x0410)] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.UNIFIED_BATTERY) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.UNIFIED_BATTERY) feature, battery = _hidpp20.get_battery_unified(device) - assert feature == hidpp20_constants.FEATURE.UNIFIED_BATTERY + assert feature == SupportedFeature.UNIFIED_BATTERY assert battery.level == 80 assert battery.status == common.BatteryStatus.DISCHARGING def test_get_adc_measurement(): responses = [fake_hidpp.Response("100003", 0x0400)] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.ADC_MEASUREMENT) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.ADC_MEASUREMENT) feature, battery = _hidpp20.get_adc_measurement(device) - assert feature == hidpp20_constants.FEATURE.ADC_MEASUREMENT + assert feature == SupportedFeature.ADC_MEASUREMENT assert battery.level == 90 assert battery.status == common.BatteryStatus.RECHARGING assert battery.voltage == 0x1000 @@ -136,11 +136,11 @@ def test_get_adc_measurement(): def test_get_battery(): responses = [fake_hidpp.Response("502000FFFF", 0x0400)] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.BATTERY_STATUS) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.BATTERY_STATUS) - feature, battery = _hidpp20.get_battery(device, hidpp20_constants.FEATURE.BATTERY_STATUS) + feature, battery = _hidpp20.get_battery(device, SupportedFeature.BATTERY_STATUS) - assert feature == hidpp20_constants.FEATURE.BATTERY_STATUS + assert feature == SupportedFeature.BATTERY_STATUS assert battery.level == 80 assert battery.next_level == 32 assert battery.status == common.BatteryStatus.DISCHARGING @@ -148,15 +148,15 @@ def test_get_battery(): def test_get_battery_none(): responses = [ - fake_hidpp.Response(None, 0x0000, f"{hidpp20_constants.FEATURE.BATTERY_STATUS:0>4X}"), - fake_hidpp.Response(None, 0x0000, f"{hidpp20_constants.FEATURE.BATTERY_VOLTAGE:0>4X}"), + fake_hidpp.Response(None, 0x0000, f"{int(SupportedFeature.BATTERY_STATUS):0>4X}"), + fake_hidpp.Response(None, 0x0000, f"{int(SupportedFeature.BATTERY_VOLTAGE):0>4X}"), fake_hidpp.Response("500100ffff", 0x0410), ] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.UNIFIED_BATTERY) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.UNIFIED_BATTERY) feature, battery = _hidpp20.get_battery(device, None) - assert feature == hidpp20_constants.FEATURE.UNIFIED_BATTERY + assert feature == SupportedFeature.UNIFIED_BATTERY assert battery.level == 80 assert battery.status == common.BatteryStatus.DISCHARGING @@ -170,7 +170,7 @@ def test_get_battery_none(): def test_get_mouse_pointer_info(): responses = [fake_hidpp.Response("01000A", 0x0400)] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.MOUSE_POINTER) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.MOUSE_POINTER) result = _hidpp20.get_mouse_pointer_info(device) @@ -184,7 +184,7 @@ def test_get_mouse_pointer_info(): def test_get_vertical_scrolling_info(): responses = [fake_hidpp.Response("01080C", 0x0400)] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.VERTICAL_SCROLLING) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.VERTICAL_SCROLLING) result = _hidpp20.get_vertical_scrolling_info(device) @@ -193,7 +193,7 @@ def test_get_vertical_scrolling_info(): def test_get_hi_res_scrolling_info(): responses = [fake_hidpp.Response("0102", 0x0400)] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.HI_RES_SCROLLING) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.HI_RES_SCROLLING) mode, resolution = _hidpp20.get_hi_res_scrolling_info(device) @@ -203,7 +203,7 @@ def test_get_hi_res_scrolling_info(): def test_get_pointer_speed_info(): responses = [fake_hidpp.Response("0102", 0x0400)] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.POINTER_SPEED) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.POINTER_SPEED) result = _hidpp20.get_pointer_speed_info(device) @@ -212,7 +212,7 @@ def test_get_pointer_speed_info(): def test_get_lowres_wheel_status(): responses = [fake_hidpp.Response("01", 0x0400)] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.LOWRES_WHEEL) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.LOWRES_WHEEL) result = _hidpp20.get_lowres_wheel_status(device) @@ -225,7 +225,7 @@ def test_get_hires_wheel(): fake_hidpp.Response("05FF", 0x0410), fake_hidpp.Response("03FF", 0x0430), ] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.HIRES_WHEEL) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.HIRES_WHEEL) multi, has_invert, has_ratchet, inv, res, target, ratchet = _hidpp20.get_hires_wheel(device) @@ -240,7 +240,7 @@ def test_get_hires_wheel(): def test_get_new_fn_inversion(): responses = [fake_hidpp.Response("0300", 0x0400)] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.NEW_FN_INVERSION) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.NEW_FN_INVERSION) result = _hidpp20.get_new_fn_inversion(device) @@ -273,7 +273,7 @@ def mock_gethostname(mocker): ], ) def test_get_host_names(responses, expected_result, mock_gethostname): - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.HOSTS_INFO) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.HOSTS_INFO) result = _hidpp20.get_host_names(device) @@ -304,7 +304,7 @@ def test_get_host_names(responses, expected_result, mock_gethostname): ], ) def test_set_host_name(responses, expected_result): - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.HOSTS_INFO) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.HOSTS_INFO) result = _hidpp20.set_host_name(device, "ABCDEFGHIJKLMNOPQRSTUVWX") @@ -313,7 +313,7 @@ def test_set_host_name(responses, expected_result): def test_get_onboard_mode(): responses = [fake_hidpp.Response("03FFFFFFFF", 0x0420)] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.ONBOARD_PROFILES) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.ONBOARD_PROFILES) result = _hidpp20.get_onboard_mode(device) @@ -322,7 +322,7 @@ def test_get_onboard_mode(): def test_set_onboard_mode(): responses = [fake_hidpp.Response("03FFFFFFFF", 0x0410, "03")] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.ONBOARD_PROFILES) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.ONBOARD_PROFILES) res = _hidpp20.set_onboard_mode(device, 0x3) @@ -335,7 +335,7 @@ def test_set_onboard_mode(): ([fake_hidpp.Response("03FFFF", 0x0420)], "1ms"), ( [ - fake_hidpp.Response(None, 0x0000, f"{hidpp20_constants.FEATURE.REPORT_RATE:0>4X}"), + fake_hidpp.Response(None, 0x0000, f"{int(SupportedFeature.REPORT_RATE):04X}"), fake_hidpp.Response("04FFFF", 0x0420), ], "500us", @@ -346,7 +346,7 @@ def test_get_polling_rate( responses, expected_result, ): - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.EXTENDED_ADJUSTABLE_REPORT_RATE) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.EXTENDED_ADJUSTABLE_REPORT_RATE) result = _hidpp20.get_polling_rate(device) @@ -355,7 +355,7 @@ def test_get_polling_rate( def test_get_remaining_pairing(): responses = [fake_hidpp.Response("03FFFF", 0x0400)] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.REMAINING_PAIRING) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.REMAINING_PAIRING) result = _hidpp20.get_remaining_pairing(device) @@ -364,7 +364,7 @@ def test_get_remaining_pairing(): def test_config_change(): responses = [fake_hidpp.Response("03FFFF", 0x0410, "02")] - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.CONFIG_CHANGE) + device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.CONFIG_CHANGE) result = _hidpp20.config_change(device, 0x2) @@ -376,7 +376,7 @@ def test_decipher_battery_status(): feature, battery = hidpp20.decipher_battery_status(report) - assert feature == hidpp20_constants.FEATURE.BATTERY_STATUS + assert feature == SupportedFeature.BATTERY_STATUS assert battery.level == 80 assert battery.next_level == 32 assert battery.status == common.BatteryStatus.DISCHARGING @@ -387,7 +387,7 @@ def test_decipher_battery_voltage(): feature, battery = hidpp20.decipher_battery_voltage(report) - assert feature == hidpp20_constants.FEATURE.BATTERY_VOLTAGE + assert feature == SupportedFeature.BATTERY_VOLTAGE assert battery.level == 90 assert battery.status == common.BatteryStatus.RECHARGING assert battery.voltage == 0x1000 @@ -398,7 +398,7 @@ def test_decipher_battery_unified(): feature, battery = hidpp20.decipher_battery_unified(report) - assert feature == hidpp20_constants.FEATURE.UNIFIED_BATTERY + assert feature == SupportedFeature.UNIFIED_BATTERY assert battery.level == 80 assert battery.status == common.BatteryStatus.DISCHARGING @@ -408,7 +408,7 @@ def test_decipher_adc_measurement(): feature, battery = hidpp20.decipher_adc_measurement(report) - assert feature == hidpp20_constants.FEATURE.ADC_MEASUREMENT + assert feature == SupportedFeature.ADC_MEASUREMENT assert battery.level == 90 assert battery.status == common.BatteryStatus.RECHARGING assert battery.voltage == 0x1000 diff --git a/tests/logitech_receiver/test_setting_templates.py b/tests/logitech_receiver/test_setting_templates.py index ef1f5ffdb..ec8349764 100644 --- a/tests/logitech_receiver/test_setting_templates.py +++ b/tests/logitech_receiver/test_setting_templates.py @@ -795,7 +795,7 @@ def test_key_template(test, mocker): ], ) def test_SpeedChange_action(responses, currentSpeed, newSpeed, mocker): - device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.FEATURE.POINTER_SPEED) + device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.SupportedFeature.POINTER_SPEED) spy_setting_callback = mocker.spy(device, "setting_callback") settings_templates.check_feature_settings(device, device.settings) # need to set up all the settings device.persister = {"pointer_speed": currentSpeed, "_speed-change": newSpeed}