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