device: Remove hard dependency on hidapi

This commit is contained in:
MattHag 2024-09-26 01:30:18 +02:00 committed by Peter F. Patel-Schneider
parent 9907cb2875
commit 615499dce2
3 changed files with 25 additions and 25 deletions

View File

@ -19,13 +19,12 @@ import logging
import threading import threading
import time import time
from typing import Any
from typing import Callable from typing import Callable
from typing import Optional from typing import Optional
from typing import Protocol from typing import Protocol
from typing import cast from typing import cast
import hidapi
from solaar import configuration from solaar import configuration
from . import base from . import base
@ -65,7 +64,9 @@ low_level_interface = cast(LowLevelInterface, base)
class DeviceFactory: class DeviceFactory:
@staticmethod @staticmethod
def create_device(low_level: LowLevelInterface, device_info, setting_callback=None): def create_device(
find_paired_node_func: Callable[[str, int, int], Any], low_level: LowLevelInterface, device_info, setting_callback=None
):
"""Opens a Logitech Device found attached to the machine, by Linux device path. """Opens a Logitech Device found attached to the machine, by Linux device path.
:returns: An open file handle for the found receiver, or None. :returns: An open file handle for the found receiver, or None.
""" """
@ -74,6 +75,7 @@ class DeviceFactory:
if handle: if handle:
# a direct connected device might not be online (as reported by user) # a direct connected device might not be online (as reported by user)
return Device( return Device(
find_paired_node_func,
low_level, low_level,
None, None,
None, None,
@ -98,6 +100,7 @@ class Device:
def __init__( def __init__(
self, self,
find_paired_node_func: Callable[[str, int, int], Any],
low_level: LowLevelInterface, low_level: LowLevelInterface,
receiver, receiver,
number, number,
@ -154,7 +157,7 @@ class Device:
self.cleanups = [] # functions to run on the device when it is closed self.cleanups = [] # functions to run on the device when it is closed
if not self.path: if not self.path:
self.path = hidapi.find_paired_node(receiver.path, number, 1) if receiver else None self.path = find_paired_node_func(receiver.path, number, 1) if receiver else None
if not self.handle: if not self.handle:
try: try:
self.handle = self.low_level.open_path(self.path) if self.path else None self.handle = self.low_level.open_path(self.path) if self.path else None

View File

@ -24,6 +24,7 @@ from collections import namedtuple
from functools import partial from functools import partial
import gi import gi
import hidapi
import logitech_receiver import logitech_receiver
from logitech_receiver import base from logitech_receiver import base
@ -257,7 +258,9 @@ def _start(device_info):
if not isDevice: if not isDevice:
receiver_ = logitech_receiver.receiver.ReceiverFactory.create_receiver(device_info, _setting_callback) receiver_ = logitech_receiver.receiver.ReceiverFactory.create_receiver(device_info, _setting_callback)
else: else:
receiver_ = logitech_receiver.device.DeviceFactory.create_device(base, device_info, _setting_callback) receiver_ = logitech_receiver.device.DeviceFactory.create_device(
hidapi.find_paired_node, base, device_info, _setting_callback
)
if receiver_: if receiver_:
configuration.attach_to(receiver_) configuration.attach_to(receiver_)
if receiver_.bluetooth and receiver_.hid_serial: if receiver_.bluetooth and receiver_.hid_serial:

View File

@ -80,12 +80,12 @@ def test_create_device(device_info, responses, expected_success):
low_level_mock = LowLevelInterfaceFake(responses) low_level_mock = LowLevelInterfaceFake(responses)
if expected_success is None: if expected_success is None:
with pytest.raises(PermissionError): with pytest.raises(PermissionError):
device.DeviceFactory.create_device(low_level_mock, device_info) device.DeviceFactory.create_device(mock.Mock(), low_level_mock, device_info)
elif not expected_success: elif not expected_success:
with pytest.raises(TypeError): with pytest.raises(TypeError):
device.DeviceFactory.create_device(low_level_mock, device_info) device.DeviceFactory.create_device(mock.Mock(), low_level_mock, device_info)
else: else:
test_device = device.DeviceFactory.create_device(low_level_mock, device_info) test_device = device.DeviceFactory.create_device(mock.Mock(), low_level_mock, device_info)
assert bool(test_device) == expected_success assert bool(test_device) == expected_success
@ -96,7 +96,7 @@ def test_create_device(device_info, responses, expected_success):
def test_device_name(device_info, responses, expected_codename, expected_name, expected_kind): def test_device_name(device_info, responses, expected_codename, expected_name, expected_kind):
low_level = LowLevelInterfaceFake(responses) low_level = LowLevelInterfaceFake(responses)
test_device = device.DeviceFactory.create_device(low_level, device_info) test_device = device.DeviceFactory.create_device(mock.Mock(), low_level, device_info)
assert test_device.codename == expected_codename assert test_device.codename == expected_codename
assert test_device.name == expected_name assert test_device.name == expected_name
@ -124,7 +124,9 @@ def test_device_name(device_info, responses, expected_codename, expected_name, e
), ),
) )
def test_device_info(device_info, responses, handle, _name, _codename, number, protocol, registers): def test_device_info(device_info, responses, handle, _name, _codename, number, protocol, registers):
test_device = device.Device(LowLevelInterfaceFake(responses), None, None, None, handle=handle, device_info=device_info) test_device = device.Device(
mock.Mock(), LowLevelInterfaceFake(responses), None, None, None, handle=handle, device_info=device_info
)
assert test_device.handle == handle assert test_device.handle == handle
assert test_device._name == _name assert test_device._name == _name
@ -152,12 +154,6 @@ class FakeReceiver:
return True return True
@pytest.fixture
def mock_hid():
with mock.patch("hidapi.find_paired_node", return_value=None) as find_paired_node:
yield find_paired_node
pi_CCCC = {"wpid": "CCCC", "kind": 0, "serial": None, "polling": "1ms", "power_switch": "top"} pi_CCCC = {"wpid": "CCCC", "kind": 0, "serial": None, "polling": "1ms", "power_switch": "top"}
pi_2011 = {"wpid": "2011", "kind": 1, "serial": "1234", "polling": "2ms", "power_switch": "bottom"} pi_2011 = {"wpid": "2011", "kind": 1, "serial": "1234", "polling": "2ms", "power_switch": "bottom"}
pi_4066 = {"wpid": "4066", "kind": 1, "serial": "5678", "polling": "4ms", "power_switch": "left"} pi_4066 = {"wpid": "4066", "kind": 1, "serial": "5678", "polling": "4ms", "power_switch": "left"}
@ -194,14 +190,14 @@ pi_DDDD = {"wpid": "DDDD", "kind": 2, "serial": "1234", "polling": "2ms", "power
], ],
), ),
) )
def test_device_receiver(number, pairing_info, responses, handle, _name, codename, p, p2, name, mock_hid): def test_device_receiver(number, pairing_info, responses, handle, _name, codename, p, p2, name):
mock_hid.side_effect = lambda x, y, z: x
low_level = LowLevelInterfaceFake(responses) low_level = LowLevelInterfaceFake(responses)
low_level.request = partial(fake_hidpp.request, fake_hidpp.replace_number(responses, number)) low_level.request = partial(fake_hidpp.request, fake_hidpp.replace_number(responses, number))
low_level.ping = partial(fake_hidpp.ping, fake_hidpp.replace_number(responses, number)) low_level.ping = partial(fake_hidpp.ping, fake_hidpp.replace_number(responses, number))
test_device = device.Device(low_level, FakeReceiver(codename="CODE"), number, True, pairing_info, handle=handle) test_device = device.Device(
mock.Mock(), low_level, FakeReceiver(codename="CODE"), number, True, pairing_info, handle=handle
)
test_device.receiver.device = test_device test_device.receiver.device = test_device
assert test_device.handle == handle assert test_device.handle == handle
@ -245,14 +241,12 @@ def test_device_receiver(number, pairing_info, responses, handle, _name, codenam
["1ms", "2ms", "4ms", "8ms", "1ms", "9ms"], # polling rate ["1ms", "2ms", "4ms", "8ms", "1ms", "9ms"], # polling rate
), ),
) )
def test_device_ids(number, info, responses, handle, unitId, modelId, tid, kind, firmware, serial, id, psl, rate, mock_hid): def test_device_ids(number, info, responses, handle, unitId, modelId, tid, kind, firmware, serial, id, psl, rate):
mock_hid.side_effect = lambda x, y, z: x
low_level = LowLevelInterfaceFake(responses) low_level = LowLevelInterfaceFake(responses)
low_level.request = partial(fake_hidpp.request, fake_hidpp.replace_number(responses, number)) low_level.request = partial(fake_hidpp.request, fake_hidpp.replace_number(responses, number))
low_level.ping = partial(fake_hidpp.ping, fake_hidpp.replace_number(responses, number)) low_level.ping = partial(fake_hidpp.ping, fake_hidpp.replace_number(responses, number))
test_device = device.Device(low_level, FakeReceiver(), number, True, info, handle=handle) test_device = device.Device(mock.Mock(), low_level, FakeReceiver(), number, True, info, handle=handle)
assert test_device.unitId == unitId assert test_device.unitId == unitId
assert test_device.modelId == modelId assert test_device.modelId == modelId
@ -267,7 +261,7 @@ def test_device_ids(number, info, responses, handle, unitId, modelId, tid, kind,
class FakeDevice(device.Device): # a fully functional Device but its HID++ functions look at local data class FakeDevice(device.Device): # a fully functional Device but its HID++ functions look at local data
def __init__(self, responses, *args, **kwargs): def __init__(self, responses, *args, **kwargs):
self.responses = responses self.responses = responses
super().__init__(LowLevelInterfaceFake(responses), *args, **kwargs) super().__init__(mock.Mock(), LowLevelInterfaceFake(responses), *args, **kwargs)
request = fake_hidpp.Device.request request = fake_hidpp.Device.request
ping = fake_hidpp.Device.ping ping = fake_hidpp.Device.ping