receiver: handle bluetooth-connected devices
This commit is contained in:
parent
1e7050595e
commit
815c9755b5
|
@ -156,7 +156,7 @@ def close(handle):
|
|||
return False
|
||||
|
||||
|
||||
def write(handle, devnumber, data):
|
||||
def write(handle, devnumber, data, long_message=False):
|
||||
"""Writes some data to the receiver, addressed to a certain device.
|
||||
|
||||
:param handle: an open UR handle.
|
||||
|
@ -173,7 +173,7 @@ def write(handle, devnumber, data):
|
|||
assert data is not None
|
||||
assert isinstance(data, bytes), (repr(data), type(data))
|
||||
|
||||
if len(data) > _SHORT_MESSAGE_SIZE - 2 or data[:1] == b'\x82':
|
||||
if long_message or len(data) > _SHORT_MESSAGE_SIZE - 2 or data[:1] == b'\x82':
|
||||
wdata = _pack('!BB18s', 0x11, devnumber, data)
|
||||
else:
|
||||
wdata = _pack('!BB5s', 0x10, devnumber, data)
|
||||
|
@ -232,7 +232,7 @@ def _read(handle, timeout):
|
|||
timeout = int(timeout * 1000)
|
||||
data = _hid.read(int(handle), _MAX_READ_SIZE, timeout)
|
||||
except Exception as reason:
|
||||
_log.error('read failed, assuming handle %r no longer available', handle)
|
||||
_log.warn('read failed, assuming handle %r no longer available', handle)
|
||||
close(handle)
|
||||
raise NoReceiver(reason=reason)
|
||||
|
||||
|
@ -320,7 +320,7 @@ del namedtuple
|
|||
|
||||
|
||||
# a very few requests (e.g., host switching) do not expect a reply, but use no_reply=True with extreme caution
|
||||
def request(handle, devnumber, request_id, *params, no_reply=False, return_error=False):
|
||||
def request(handle, devnumber, request_id, *params, no_reply=False, return_error=False, long_message=False):
|
||||
"""Makes a feature call to a device and waits for a matching reply.
|
||||
:param handle: an open UR handle.
|
||||
:param devnumber: attached device number.
|
||||
|
@ -357,7 +357,7 @@ def request(handle, devnumber, request_id, *params, no_reply=False, return_error
|
|||
ihandle = int(handle)
|
||||
notifications_hook = getattr(handle, 'notifications_hook', None)
|
||||
_skip_incoming(handle, ihandle, notifications_hook)
|
||||
write(ihandle, devnumber, request_data)
|
||||
write(ihandle, devnumber, request_data, long_message)
|
||||
|
||||
if no_reply:
|
||||
return None
|
||||
|
@ -443,7 +443,7 @@ def request(handle, devnumber, request_id, *params, no_reply=False, return_error
|
|||
# raise DeviceUnreachable(number=devnumber, request=request_id)
|
||||
|
||||
|
||||
def ping(handle, devnumber):
|
||||
def ping(handle, devnumber, long_message=False):
|
||||
"""Check if a device is connected to the receiver.
|
||||
|
||||
:returns: The HID protocol supported by the device, as a floating point number, if the device is active.
|
||||
|
@ -467,7 +467,7 @@ def ping(handle, devnumber):
|
|||
ihandle = int(handle)
|
||||
notifications_hook = getattr(handle, 'notifications_hook', None)
|
||||
_skip_incoming(handle, ihandle, notifications_hook)
|
||||
write(ihandle, devnumber, request_data)
|
||||
write(ihandle, devnumber, request_data, long_message)
|
||||
|
||||
# we consider timeout from this point
|
||||
request_started = _timestamp()
|
||||
|
|
|
@ -11,10 +11,10 @@ import hidapi as _hid
|
|||
import solaar.configuration as _configuration
|
||||
|
||||
from . import base as _base
|
||||
from . import descriptors as _descriptors
|
||||
from . import hidpp10 as _hidpp10
|
||||
from . import hidpp20 as _hidpp20
|
||||
from .common import strhex as _strhex
|
||||
from .descriptors import DEVICES as _DESCRIPTORS
|
||||
from .i18n import _
|
||||
from .settings_templates import check_feature_settings as _check_feature_settings
|
||||
|
||||
|
@ -51,7 +51,8 @@ class Device(object):
|
|||
# the Wireless PID is unique per device model
|
||||
self.wpid = None
|
||||
self.descriptor = None
|
||||
|
||||
# Bluetooth connections need long messages
|
||||
self.bluetooth = False
|
||||
# mouse, keyboard, etc (see _hidpp10.DEVICE_KIND)
|
||||
self._kind = None
|
||||
# Unifying peripherals report a codename.
|
||||
|
@ -127,7 +128,6 @@ class Device(object):
|
|||
if device_info is None:
|
||||
_log.error('failed to read Nano wpid for device %d of %s', number, receiver)
|
||||
raise _base.NoSuchDevice(number=number, receiver=receiver, error='read Nano wpid')
|
||||
|
||||
self.wpid = _strhex(device_info[3:5])
|
||||
self._power_switch = '(' + _('unknown') + ')'
|
||||
|
||||
|
@ -147,7 +147,7 @@ class Device(object):
|
|||
except Exception: # give up
|
||||
self.handle = None
|
||||
|
||||
self.descriptor = _DESCRIPTORS.get(self.wpid)
|
||||
self.descriptor = _descriptors.get_wpid(self.wpid)
|
||||
if self.descriptor is None:
|
||||
# Last chance to correctly identify the device; many Nano
|
||||
# receivers do not support this call.
|
||||
|
@ -156,21 +156,24 @@ class Device(object):
|
|||
codename_length = ord(codename[1:2])
|
||||
codename = codename[2:2 + codename_length]
|
||||
self._codename = codename.decode('ascii')
|
||||
self.descriptor = _DESCRIPTORS.get(self._codename)
|
||||
|
||||
if self.descriptor:
|
||||
self._name = self.descriptor.name
|
||||
self._protocol = self.descriptor.protocol
|
||||
if self._codename is None:
|
||||
self._codename = self.descriptor.codename
|
||||
if self._kind is None:
|
||||
self._kind = self.descriptor.kind
|
||||
self.descriptor = _descriptors.get_codename(self._codename)
|
||||
else:
|
||||
self.path = info.path
|
||||
self.handle = _hid.open_path(self.path)
|
||||
self.product_id = info.product_id
|
||||
self._serial = ''.join(info.serial.split('-')).upper()
|
||||
self.online = True
|
||||
self.product_id = info.product_id
|
||||
self.bluetooth = info.bus_id == 0x0005
|
||||
self.descriptor = _descriptors.get_btid(self.product_id
|
||||
) if self.bluetooth else _descriptors.get_usbid(self.product_id)
|
||||
|
||||
if self.descriptor:
|
||||
self._name = self.descriptor.name
|
||||
if self.descriptor.protocol:
|
||||
self._protocol = self.descriptor.protocol
|
||||
if self._codename is None:
|
||||
self._codename = self.descriptor.codename
|
||||
if self._kind is None:
|
||||
self._kind = self.descriptor.kind
|
||||
|
||||
if self._protocol is not None:
|
||||
self.features = None if self._protocol < 2.0 else _hidpp20.FeaturesArray(self)
|
||||
|
@ -181,7 +184,7 @@ class Device(object):
|
|||
@property
|
||||
def protocol(self):
|
||||
if not self._protocol and self.online:
|
||||
self._protocol = _base.ping(self.handle or self.receiver.handle, self.number)
|
||||
self._protocol = _base.ping(self.handle or self.receiver.handle, self.number, long_message=self.bluetooth)
|
||||
# if the ping failed, the peripheral is (almost) certainly offline
|
||||
self.online = self._protocol is not None
|
||||
|
||||
|
@ -417,7 +420,14 @@ class Device(object):
|
|||
return None
|
||||
|
||||
def request(self, request_id, *params, no_reply=False):
|
||||
return _base.request(self.handle or self.receiver.handle, self.number, request_id, *params, no_reply=no_reply)
|
||||
return _base.request(
|
||||
self.handle or self.receiver.handle,
|
||||
self.number,
|
||||
request_id,
|
||||
*params,
|
||||
no_reply=no_reply,
|
||||
long_message=self.bluetooth
|
||||
)
|
||||
|
||||
def feature_request(self, feature, function=0x00, *params, no_reply=False):
|
||||
if self.protocol >= 2.0:
|
||||
|
@ -425,7 +435,7 @@ class Device(object):
|
|||
|
||||
def ping(self):
|
||||
"""Checks if the device is online, returns True of False"""
|
||||
protocol = _base.ping(self.handle or self.receiver.handle, self.number)
|
||||
protocol = _base.ping(self.handle or self.receiver.handle, self.number, long_message=self.bluetooth)
|
||||
self.online = protocol is not None
|
||||
if protocol:
|
||||
self._protocol = protocol
|
||||
|
|
|
@ -117,7 +117,7 @@ def _wired_devices(dev_path=None):
|
|||
if dev_path is not None and dev_path != dev_info.path:
|
||||
continue
|
||||
try:
|
||||
d = Device(None, 0, info=dev_info)
|
||||
d = Device.open(dev_info)
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug('[%s] => %s', dev_info.path, d)
|
||||
if d is not None:
|
||||
|
|
|
@ -137,7 +137,7 @@ def persister(device):
|
|||
_configuration[key] = entry
|
||||
elif device.wpid and not entry: # create now with wpid:serial
|
||||
key = '%s:%s' % (device.wpid, device.serial)
|
||||
else: # create now with modelId:unitId
|
||||
elif not entry: # create now with modelId:unitId
|
||||
key = '%s:%s' % (device.modelId, device.unitId)
|
||||
else: # defer until more is known (i.e., device comes on line)
|
||||
return
|
||||
|
|
|
@ -324,16 +324,22 @@ def stop_all():
|
|||
# so mark its saved status to ensure that the status is pushed to the device when it comes back
|
||||
def ping_all(resuming=False):
|
||||
for l in _all_listeners.values():
|
||||
count = l.receiver.count()
|
||||
if count:
|
||||
for dev in l.receiver:
|
||||
if resuming:
|
||||
dev.status._active = False
|
||||
dev.ping()
|
||||
l._status_changed(dev)
|
||||
count -= 1
|
||||
if not count:
|
||||
break
|
||||
if l.receiver.isDevice:
|
||||
if resuming:
|
||||
l.receiver.status._active = False
|
||||
l.receiver.ping()
|
||||
l._status_changed(l.receiver)
|
||||
else:
|
||||
count = l.receiver.count()
|
||||
if count:
|
||||
for dev in l.receiver:
|
||||
if resuming:
|
||||
dev.status._active = False
|
||||
dev.ping()
|
||||
l._status_changed(dev)
|
||||
count -= 1
|
||||
if not count:
|
||||
break
|
||||
|
||||
|
||||
_status_callback = None
|
||||
|
|
|
@ -7,12 +7,14 @@
|
|||
ACTION != "add", GOTO="solaar_end"
|
||||
SUBSYSTEM != "hidraw", GOTO="solaar_end"
|
||||
|
||||
# Logitech receivers and direct-connected devices
|
||||
# USB-connected Logitech receivers and devices
|
||||
ATTRS{idVendor}=="046d", GOTO="solaar_apply"
|
||||
|
||||
# Lenovo nano receiver
|
||||
ATTRS{idVendor}=="17ef", ATTRS{idProduct}=="6042", GOTO="solaar_apply"
|
||||
|
||||
# Bluetooth-connected Logitech devices
|
||||
KERNELS == "0005:046D:*", GOTO="solaar_apply"
|
||||
|
||||
GOTO="solaar_end"
|
||||
|
||||
|
|
Loading…
Reference in New Issue