flake8: initial fix
Signed-off-by: Filipe Laíns <lains@archlinux.org>
This commit is contained in:
parent
de5514aa23
commit
627185079f
|
@ -36,9 +36,8 @@ def init_paths():
|
||||||
sys.path[0].encode(sys.getfilesystemencoding())
|
sys.path[0].encode(sys.getfilesystemencoding())
|
||||||
|
|
||||||
except UnicodeError:
|
except UnicodeError:
|
||||||
sys.stderr.write(
|
sys.stderr.write('ERROR: Solaar cannot recognize encoding of filesystem path, '
|
||||||
'ERROR: Solaar cannot recognize encoding of filesystem path, this may happen because non UTF-8 characters in the pathname.\n'
|
'this may happen because non UTF-8 characters in the pathname.\n')
|
||||||
)
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
prefix = _path.normpath(_path.join(_path.realpath(decoded_path), '..'))
|
prefix = _path.normpath(_path.join(_path.realpath(decoded_path), '..'))
|
||||||
|
|
|
@ -20,6 +20,15 @@
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||||
|
|
||||||
from hidapi.udev import close, enumerate, get_manufacturer, get_product, get_serial, monitor_glib, open, open_path, read, write
|
from hidapi.udev import close # noqa: F401
|
||||||
|
from hidapi.udev import enumerate # noqa: F401
|
||||||
|
from hidapi.udev import get_manufacturer # noqa: F401
|
||||||
|
from hidapi.udev import get_product # noqa: F401
|
||||||
|
from hidapi.udev import get_serial # noqa: F401
|
||||||
|
from hidapi.udev import monitor_glib # noqa: F401
|
||||||
|
from hidapi.udev import open # noqa: F401
|
||||||
|
from hidapi.udev import open_path # noqa: F401
|
||||||
|
from hidapi.udev import read # noqa: F401
|
||||||
|
from hidapi.udev import write # noqa: F401
|
||||||
|
|
||||||
__version__ = '0.9'
|
__version__ = '0.9'
|
||||||
|
|
|
@ -45,15 +45,15 @@ start_time = time.time()
|
||||||
|
|
||||||
strhex = lambda d: hexlify(d).decode('ascii').upper()
|
strhex = lambda d: hexlify(d).decode('ascii').upper()
|
||||||
try:
|
try:
|
||||||
unicode
|
unicode # noqa: F821
|
||||||
# this is certanly Python 2
|
# this is certanly Python 2
|
||||||
is_string = lambda d: isinstance(d, unicode)
|
is_string = lambda d: isinstance(d, unicode) # noqa: F821
|
||||||
# no easy way to distinguish between b'' and '' :(
|
# no easy way to distinguish between b'' and '' :(
|
||||||
# or (isinstance(d, str) \
|
# or (isinstance(d, str) \
|
||||||
# and not any((chr(k) in d for k in range(0x00, 0x1F))) \
|
# and not any((chr(k) in d for k in range(0x00, 0x1F))) \
|
||||||
# and not any((chr(k) in d for k in range(0x80, 0xFF))) \
|
# and not any((chr(k) in d for k in range(0x80, 0xFF))) \
|
||||||
# )
|
# )
|
||||||
except:
|
except Exception:
|
||||||
# this is certanly Python 3
|
# this is certanly Python 3
|
||||||
# In Py3, unicode and str are equal (the unicode object does not exist)
|
# In Py3, unicode and str are equal (the unicode object does not exist)
|
||||||
is_string = lambda d: isinstance(d, str)
|
is_string = lambda d: isinstance(d, str)
|
||||||
|
@ -225,7 +225,7 @@ def main():
|
||||||
'.hidconsole-history')
|
'.hidconsole-history')
|
||||||
try:
|
try:
|
||||||
readline.read_history_file(args.history)
|
readline.read_history_file(args.history)
|
||||||
except:
|
except Exception:
|
||||||
# file may not exist yet
|
# file may not exist yet
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -63,19 +63,19 @@ del namedtuple
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
"""This function is a no-op, and exists only to match the native hidapi
|
"""This function is a no-op, and exists only to match the native hidapi
|
||||||
implementation.
|
implementation.
|
||||||
|
|
||||||
:returns: ``True``.
|
:returns: ``True``.
|
||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def exit():
|
def exit():
|
||||||
"""This function is a no-op, and exists only to match the native hidapi
|
"""This function is a no-op, and exists only to match the native hidapi
|
||||||
implementation.
|
implementation.
|
||||||
|
|
||||||
:returns: ``True``.
|
:returns: ``True``.
|
||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -157,12 +157,12 @@ def monitor_glib(callback, *device_filters):
|
||||||
|
|
||||||
# already existing devices
|
# already existing devices
|
||||||
# for device in c.list_devices(subsystem='hidraw'):
|
# for device in c.list_devices(subsystem='hidraw'):
|
||||||
# # print (device, dict(device), dict(device.attributes))
|
# # print (device, dict(device), dict(device.attributes))
|
||||||
# for filter in device_filters:
|
# for filter in device_filters:
|
||||||
# d_info = _match('add', device, *filter)
|
# d_info = _match('add', device, *filter)
|
||||||
# if d_info:
|
# if d_info:
|
||||||
# GLib.idle_add(callback, 'add', d_info)
|
# GLib.idle_add(callback, 'add', d_info)
|
||||||
# break
|
# break
|
||||||
|
|
||||||
m = _Monitor.from_netlink(c)
|
m = _Monitor.from_netlink(c)
|
||||||
m.filter_by(subsystem='hidraw')
|
m.filter_by(subsystem='hidraw')
|
||||||
|
@ -195,7 +195,7 @@ def monitor_glib(callback, *device_filters):
|
||||||
GLib.io_add_watch(m, GLib.PRIORITY_LOW, GLib.IO_IN,
|
GLib.io_add_watch(m, GLib.PRIORITY_LOW, GLib.IO_IN,
|
||||||
_process_udev_event, callback, device_filters)
|
_process_udev_event, callback, device_filters)
|
||||||
# print ("did io_add_watch with priority")
|
# print ("did io_add_watch with priority")
|
||||||
except:
|
except Exception:
|
||||||
GLib.io_add_watch(m, GLib.IO_IN, _process_udev_event, callback,
|
GLib.io_add_watch(m, GLib.IO_IN, _process_udev_event, callback,
|
||||||
device_filters)
|
device_filters)
|
||||||
# print ("did io_add_watch")
|
# print ("did io_add_watch")
|
||||||
|
@ -206,11 +206,11 @@ def monitor_glib(callback, *device_filters):
|
||||||
def enumerate(usb_id):
|
def enumerate(usb_id):
|
||||||
"""Enumerate the HID Devices.
|
"""Enumerate the HID Devices.
|
||||||
|
|
||||||
List all the HID devices attached to the system, optionally filtering by
|
List all the HID devices attached to the system, optionally filtering by
|
||||||
vendor_id, product_id, and/or interface_number.
|
vendor_id, product_id, and/or interface_number.
|
||||||
|
|
||||||
:returns: a list of matching ``DeviceInfo`` tuples.
|
:returns: a list of matching ``DeviceInfo`` tuples.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for dev in _Context().list_devices(subsystem='hidraw'):
|
for dev in _Context().list_devices(subsystem='hidraw'):
|
||||||
dev_info = _match('add', dev, usb_id)
|
dev_info = _match('add', dev, usb_id)
|
||||||
|
@ -221,10 +221,10 @@ def enumerate(usb_id):
|
||||||
def open(vendor_id, product_id, serial=None):
|
def open(vendor_id, product_id, serial=None):
|
||||||
"""Open a HID device by its Vendor ID, Product ID and optional serial number.
|
"""Open a HID device by its Vendor ID, Product ID and optional serial number.
|
||||||
|
|
||||||
If no serial is provided, the first device with the specified IDs is opened.
|
If no serial is provided, the first device with the specified IDs is opened.
|
||||||
|
|
||||||
:returns: an opaque device handle, or ``None``.
|
:returns: an opaque device handle, or ``None``.
|
||||||
"""
|
"""
|
||||||
for device in enumerate(vendor_id, product_id):
|
for device in enumerate(vendor_id, product_id):
|
||||||
if serial is None or serial == device.serial:
|
if serial is None or serial == device.serial:
|
||||||
return open_path(device.path)
|
return open_path(device.path)
|
||||||
|
@ -233,11 +233,11 @@ def open(vendor_id, product_id, serial=None):
|
||||||
def open_path(device_path):
|
def open_path(device_path):
|
||||||
"""Open a HID device by its path name.
|
"""Open a HID device by its path name.
|
||||||
|
|
||||||
:param device_path: the path of a ``DeviceInfo`` tuple returned by
|
:param device_path: the path of a ``DeviceInfo`` tuple returned by
|
||||||
enumerate().
|
enumerate().
|
||||||
|
|
||||||
:returns: an opaque device handle, or ``None``.
|
:returns: an opaque device handle, or ``None``.
|
||||||
"""
|
"""
|
||||||
assert device_path
|
assert device_path
|
||||||
assert device_path.startswith('/dev/hidraw')
|
assert device_path.startswith('/dev/hidraw')
|
||||||
return _os.open(device_path, _os.O_RDWR | _os.O_SYNC)
|
return _os.open(device_path, _os.O_RDWR | _os.O_SYNC)
|
||||||
|
@ -246,8 +246,8 @@ def open_path(device_path):
|
||||||
def close(device_handle):
|
def close(device_handle):
|
||||||
"""Close a HID device.
|
"""Close a HID device.
|
||||||
|
|
||||||
:param device_handle: a device handle returned by open() or open_path().
|
:param device_handle: a device handle returned by open() or open_path().
|
||||||
"""
|
"""
|
||||||
assert device_handle
|
assert device_handle
|
||||||
_os.close(device_handle)
|
_os.close(device_handle)
|
||||||
|
|
||||||
|
@ -255,24 +255,24 @@ def close(device_handle):
|
||||||
def write(device_handle, data):
|
def write(device_handle, data):
|
||||||
"""Write an Output report to a HID device.
|
"""Write an Output report to a HID device.
|
||||||
|
|
||||||
:param device_handle: a device handle returned by open() or open_path().
|
:param device_handle: a device handle returned by open() or open_path().
|
||||||
:param data: the data bytes to send including the report number as the
|
:param data: the data bytes to send including the report number as the
|
||||||
first byte.
|
first byte.
|
||||||
|
|
||||||
The first byte of data[] must contain the Report ID. For
|
The first byte of data[] must contain the Report ID. For
|
||||||
devices which only support a single report, this must be set
|
devices which only support a single report, this must be set
|
||||||
to 0x0. The remaining bytes contain the report data. Since
|
to 0x0. The remaining bytes contain the report data. Since
|
||||||
the Report ID is mandatory, calls to hid_write() will always
|
the Report ID is mandatory, calls to hid_write() will always
|
||||||
contain one more byte than the report contains. For example,
|
contain one more byte than the report contains. For example,
|
||||||
if a hid report is 16 bytes long, 17 bytes must be passed to
|
if a hid report is 16 bytes long, 17 bytes must be passed to
|
||||||
hid_write(), the Report ID (or 0x0, for devices with a
|
hid_write(), the Report ID (or 0x0, for devices with a
|
||||||
single report), followed by the report data (16 bytes). In
|
single report), followed by the report data (16 bytes). In
|
||||||
this example, the length passed in would be 17.
|
this example, the length passed in would be 17.
|
||||||
|
|
||||||
write() will send the data on the first OUT endpoint, if
|
write() will send the data on the first OUT endpoint, if
|
||||||
one exists. If it does not, it will send the data through
|
one exists. If it does not, it will send the data through
|
||||||
the Control Endpoint (Endpoint 0).
|
the Control Endpoint (Endpoint 0).
|
||||||
"""
|
"""
|
||||||
assert device_handle
|
assert device_handle
|
||||||
assert data
|
assert data
|
||||||
assert isinstance(data, bytes), (repr(data), type(data))
|
assert isinstance(data, bytes), (repr(data), type(data))
|
||||||
|
@ -296,19 +296,19 @@ def write(device_handle, data):
|
||||||
def read(device_handle, bytes_count, timeout_ms=-1):
|
def read(device_handle, bytes_count, timeout_ms=-1):
|
||||||
"""Read an Input report from a HID device.
|
"""Read an Input report from a HID device.
|
||||||
|
|
||||||
:param device_handle: a device handle returned by open() or open_path().
|
:param device_handle: a device handle returned by open() or open_path().
|
||||||
:param bytes_count: maximum number of bytes to read.
|
:param bytes_count: maximum number of bytes to read.
|
||||||
:param timeout_ms: can be -1 (default) to wait for data indefinitely, 0 to
|
:param timeout_ms: can be -1 (default) to wait for data indefinitely, 0 to
|
||||||
read whatever is in the device's input buffer, or a positive integer to
|
read whatever is in the device's input buffer, or a positive integer to
|
||||||
wait that many milliseconds.
|
wait that many milliseconds.
|
||||||
|
|
||||||
Input reports are returned to the host through the INTERRUPT IN endpoint.
|
Input reports are returned to the host through the INTERRUPT IN endpoint.
|
||||||
The first byte will contain the Report number if the device uses numbered
|
The first byte will contain the Report number if the device uses numbered
|
||||||
reports.
|
reports.
|
||||||
|
|
||||||
:returns: the data packet read, an empty bytes string if a timeout was
|
:returns: the data packet read, an empty bytes string if a timeout was
|
||||||
reached, or None if there was an error while reading.
|
reached, or None if there was an error while reading.
|
||||||
"""
|
"""
|
||||||
assert device_handle
|
assert device_handle
|
||||||
timeout = None if timeout_ms < 0 else timeout_ms / 1000.0
|
timeout = None if timeout_ms < 0 else timeout_ms / 1000.0
|
||||||
rlist, wlist, xlist = _select([device_handle], [], [device_handle],
|
rlist, wlist, xlist = _select([device_handle], [], [device_handle],
|
||||||
|
@ -339,24 +339,24 @@ _DEVICE_STRINGS = {
|
||||||
def get_manufacturer(device_handle):
|
def get_manufacturer(device_handle):
|
||||||
"""Get the Manufacturer String from a HID device.
|
"""Get the Manufacturer String from a HID device.
|
||||||
|
|
||||||
:param device_handle: a device handle returned by open() or open_path().
|
:param device_handle: a device handle returned by open() or open_path().
|
||||||
"""
|
"""
|
||||||
return get_indexed_string(device_handle, 0)
|
return get_indexed_string(device_handle, 0)
|
||||||
|
|
||||||
|
|
||||||
def get_product(device_handle):
|
def get_product(device_handle):
|
||||||
"""Get the Product String from a HID device.
|
"""Get the Product String from a HID device.
|
||||||
|
|
||||||
:param device_handle: a device handle returned by open() or open_path().
|
:param device_handle: a device handle returned by open() or open_path().
|
||||||
"""
|
"""
|
||||||
return get_indexed_string(device_handle, 1)
|
return get_indexed_string(device_handle, 1)
|
||||||
|
|
||||||
|
|
||||||
def get_serial(device_handle):
|
def get_serial(device_handle):
|
||||||
"""Get the serial number from a HID device.
|
"""Get the serial number from a HID device.
|
||||||
|
|
||||||
:param device_handle: a device handle returned by open() or open_path().
|
:param device_handle: a device handle returned by open() or open_path().
|
||||||
"""
|
"""
|
||||||
serial = get_indexed_string(device_handle, 2)
|
serial = get_indexed_string(device_handle, 2)
|
||||||
if serial is not None:
|
if serial is not None:
|
||||||
return ''.join(hex(ord(c)) for c in serial)
|
return ''.join(hex(ord(c)) for c in serial)
|
||||||
|
@ -365,13 +365,13 @@ def get_serial(device_handle):
|
||||||
def get_indexed_string(device_handle, index):
|
def get_indexed_string(device_handle, index):
|
||||||
"""Get a string from a HID device, based on its string index.
|
"""Get a string from a HID device, based on its string index.
|
||||||
|
|
||||||
Note: currently not working in the ``hidraw`` native implementation.
|
Note: currently not working in the ``hidraw`` native implementation.
|
||||||
|
|
||||||
:param device_handle: a device handle returned by open() or open_path().
|
:param device_handle: a device handle returned by open() or open_path().
|
||||||
:param index: the index of the string to get.
|
:param index: the index of the string to get.
|
||||||
:returns: the value corresponding to index, or None if no value found
|
:returns: the value corresponding to index, or None if no value found
|
||||||
:rtype: bytes or NoneType
|
:rtype: bytes or NoneType
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
key = _DEVICE_STRINGS[index]
|
key = _DEVICE_STRINGS[index]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|
|
@ -33,18 +33,18 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from . import listener, status
|
from . import listener, status # noqa: F401
|
||||||
from .base import DeviceUnreachable, NoReceiver, NoSuchDevice
|
from .base import DeviceUnreachable, NoReceiver, NoSuchDevice # noqa: F401
|
||||||
from .common import strhex
|
from .common import strhex # noqa: F401
|
||||||
from .hidpp20 import FeatureCallError, FeatureNotSupported
|
from .hidpp20 import FeatureCallError, FeatureNotSupported # noqa: F401
|
||||||
from .receiver import PairedDevice, Receiver
|
from .receiver import PairedDevice, Receiver # noqa: F401
|
||||||
|
|
||||||
_DEBUG = logging.DEBUG
|
_DEBUG = logging.DEBUG
|
||||||
_log = logging.getLogger(__name__)
|
_log = logging.getLogger(__name__)
|
||||||
_log.setLevel(logging.root.level)
|
_log.setLevel(logging.root.level)
|
||||||
# if logging.root.level > logging.DEBUG:
|
# if logging.root.level > logging.DEBUG:
|
||||||
# _log.addHandler(logging.NullHandler())
|
# _log.addHandler(logging.NullHandler())
|
||||||
# _log.propagate = 0
|
# _log.propagate = 0
|
||||||
|
|
||||||
del logging
|
del logging
|
||||||
|
|
||||||
|
|
|
@ -72,9 +72,9 @@ _PING_TIMEOUT = DEFAULT_TIMEOUT * 2
|
||||||
|
|
||||||
class NoReceiver(_KwException):
|
class NoReceiver(_KwException):
|
||||||
"""Raised when trying to talk through a previously open handle, when the
|
"""Raised when trying to talk through a previously open handle, when the
|
||||||
receiver is no longer available. Should only happen if the receiver is
|
receiver is no longer available. Should only happen if the receiver is
|
||||||
physically disconnected from the machine, or its kernel driver module is
|
physically disconnected from the machine, or its kernel driver module is
|
||||||
unloaded."""
|
unloaded."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -113,24 +113,24 @@ def notify_on_receivers_glib(callback):
|
||||||
def open_path(path):
|
def open_path(path):
|
||||||
"""Checks if the given Linux device path points to the right UR device.
|
"""Checks if the given Linux device path points to the right UR device.
|
||||||
|
|
||||||
:param path: the Linux device path.
|
:param path: the Linux device path.
|
||||||
|
|
||||||
The UR physical device may expose multiple linux devices with the same
|
The UR physical device may expose multiple linux devices with the same
|
||||||
interface, so we have to check for the right one. At this moment the only
|
interface, so we have to check for the right one. At this moment the only
|
||||||
way to distinguish betheen them is to do a test ping on an invalid
|
way to distinguish betheen them is to do a test ping on an invalid
|
||||||
(attached) device number (i.e., 0), expecting a 'ping failed' reply.
|
(attached) device number (i.e., 0), expecting a 'ping failed' reply.
|
||||||
|
|
||||||
:returns: an open receiver handle if this is the right Linux device, or
|
:returns: an open receiver handle if this is the right Linux device, or
|
||||||
``None``.
|
``None``.
|
||||||
"""
|
"""
|
||||||
return _hid.open_path(path)
|
return _hid.open_path(path)
|
||||||
|
|
||||||
|
|
||||||
def open():
|
def open():
|
||||||
"""Opens the first Logitech Unifying Receiver found attached to the machine.
|
"""Opens the first Logitech Unifying Receiver found attached to the machine.
|
||||||
|
|
||||||
:returns: An open file handle for the found receiver, or ``None``.
|
:returns: An open file handle for the found receiver, or ``None``.
|
||||||
"""
|
"""
|
||||||
for rawdevice in receivers():
|
for rawdevice in receivers():
|
||||||
handle = open_path(rawdevice.path)
|
handle = open_path(rawdevice.path)
|
||||||
if handle:
|
if handle:
|
||||||
|
@ -147,7 +147,7 @@ def close(handle):
|
||||||
handle.close()
|
handle.close()
|
||||||
# _log.info("closed receiver handle %r", handle)
|
# _log.info("closed receiver handle %r", handle)
|
||||||
return True
|
return True
|
||||||
except:
|
except Exception:
|
||||||
# _log.exception("closing receiver handle %r", handle)
|
# _log.exception("closing receiver handle %r", handle)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -157,16 +157,16 @@ def close(handle):
|
||||||
def write(handle, devnumber, data):
|
def write(handle, devnumber, data):
|
||||||
"""Writes some data to the receiver, addressed to a certain device.
|
"""Writes some data to the receiver, addressed to a certain device.
|
||||||
|
|
||||||
:param handle: an open UR handle.
|
:param handle: an open UR handle.
|
||||||
:param devnumber: attached device number.
|
:param devnumber: attached device number.
|
||||||
:param data: data to send, up to 5 bytes.
|
:param data: data to send, up to 5 bytes.
|
||||||
|
|
||||||
The first two (required) bytes of data must be the SubId and address.
|
The first two (required) bytes of data must be the SubId and address.
|
||||||
|
|
||||||
:raises NoReceiver: if the receiver is no longer available, i.e. has
|
:raises NoReceiver: if the receiver is no longer available, i.e. has
|
||||||
been physically removed from the machine, or the kernel driver has been
|
been physically removed from the machine, or the kernel driver has been
|
||||||
unloaded. The handle will be closed automatically.
|
unloaded. The handle will be closed automatically.
|
||||||
"""
|
"""
|
||||||
# the data is padded to either 5 or 18 bytes
|
# the data is padded to either 5 or 18 bytes
|
||||||
assert data is not None
|
assert data is not None
|
||||||
assert isinstance(data, bytes), (repr(data), type(data))
|
assert isinstance(data, bytes), (repr(data), type(data))
|
||||||
|
@ -190,17 +190,17 @@ def write(handle, devnumber, data):
|
||||||
|
|
||||||
def read(handle, timeout=DEFAULT_TIMEOUT):
|
def read(handle, timeout=DEFAULT_TIMEOUT):
|
||||||
"""Read some data from the receiver. Usually called after a write (feature
|
"""Read some data from the receiver. Usually called after a write (feature
|
||||||
call), to get the reply.
|
call), to get the reply.
|
||||||
|
|
||||||
:param: handle open handle to the receiver
|
:param: handle open handle to the receiver
|
||||||
:param: timeout how long to wait for a reply, in seconds
|
:param: timeout how long to wait for a reply, in seconds
|
||||||
|
|
||||||
:returns: a tuple of (devnumber, message data), or `None`
|
:returns: a tuple of (devnumber, message data), or `None`
|
||||||
|
|
||||||
:raises NoReceiver: if the receiver is no longer available, i.e. has
|
:raises NoReceiver: if the receiver is no longer available, i.e. has
|
||||||
been physically removed from the machine, or the kernel driver has been
|
been physically removed from the machine, or the kernel driver has been
|
||||||
unloaded. The handle will be closed automatically.
|
unloaded. The handle will be closed automatically.
|
||||||
"""
|
"""
|
||||||
reply = _read(handle, timeout)
|
reply = _read(handle, timeout)
|
||||||
if reply:
|
if reply:
|
||||||
return reply[1:]
|
return reply[1:]
|
||||||
|
@ -222,12 +222,12 @@ def check_message(data):
|
||||||
def _read(handle, timeout):
|
def _read(handle, timeout):
|
||||||
"""Read an incoming packet from the receiver.
|
"""Read an incoming packet from the receiver.
|
||||||
|
|
||||||
:returns: a tuple of (report_id, devnumber, data), or `None`.
|
:returns: a tuple of (report_id, devnumber, data), or `None`.
|
||||||
|
|
||||||
:raises NoReceiver: if the receiver is no longer available, i.e. has
|
:raises NoReceiver: if the receiver is no longer available, i.e. has
|
||||||
been physically removed from the machine, or the kernel driver has been
|
been physically removed from the machine, or the kernel driver has been
|
||||||
unloaded. The handle will be closed automatically.
|
unloaded. The handle will be closed automatically.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# convert timeout to milliseconds, the hidapi expects it
|
# convert timeout to milliseconds, the hidapi expects it
|
||||||
timeout = int(timeout * 1000)
|
timeout = int(timeout * 1000)
|
||||||
|
@ -257,8 +257,8 @@ def _read(handle, timeout):
|
||||||
def _skip_incoming(handle, ihandle, notifications_hook):
|
def _skip_incoming(handle, ihandle, notifications_hook):
|
||||||
"""Read anything already in the input buffer.
|
"""Read anything already in the input buffer.
|
||||||
|
|
||||||
Used by request() and ping() before their write.
|
Used by request() and ping() before their write.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
@ -272,7 +272,7 @@ def _skip_incoming(handle, ihandle, notifications_hook):
|
||||||
|
|
||||||
if data:
|
if data:
|
||||||
if check_message(data): # only process messages that pass check
|
if check_message(data): # only process messages that pass check
|
||||||
report_id = ord(data[:1])
|
# report_id = ord(data[:1])
|
||||||
if notifications_hook:
|
if notifications_hook:
|
||||||
n = make_notification(ord(data[1:2]), data[2:])
|
n = make_notification(ord(data[1:2]), data[2:])
|
||||||
if n:
|
if n:
|
||||||
|
@ -284,7 +284,7 @@ def _skip_incoming(handle, ihandle, notifications_hook):
|
||||||
|
|
||||||
def make_notification(devnumber, data):
|
def make_notification(devnumber, data):
|
||||||
"""Guess if this is a notification (and not just a request reply), and
|
"""Guess if this is a notification (and not just a request reply), and
|
||||||
return a Notification tuple if it is."""
|
return a Notification tuple if it is."""
|
||||||
|
|
||||||
sub_id = ord(data[:1])
|
sub_id = ord(data[:1])
|
||||||
if sub_id & 0x80 == 0x80:
|
if sub_id & 0x80 == 0x80:
|
||||||
|
@ -299,13 +299,13 @@ def make_notification(devnumber, data):
|
||||||
address = ord(data[1:2])
|
address = ord(data[1:2])
|
||||||
if (
|
if (
|
||||||
# standard HID++ 1.0 notification, SubId may be 0x40 - 0x7F
|
# standard HID++ 1.0 notification, SubId may be 0x40 - 0x7F
|
||||||
(sub_id >= 0x40) or
|
(sub_id >= 0x40) or # noqa: E131
|
||||||
# custom HID++1.0 battery events, where SubId is 0x07/0x0D
|
# custom HID++1.0 battery events, where SubId is 0x07/0x0D
|
||||||
(sub_id in (0x07, 0x0D) and len(data) == 5 and data[4:5] == b'\x00') or
|
(sub_id in (0x07, 0x0D) and len(data) == 5 and data[4:5] == b'\x00') or
|
||||||
# custom HID++1.0 illumination event, where SubId is 0x17
|
# custom HID++1.0 illumination event, where SubId is 0x17
|
||||||
(sub_id == 0x17 and len(data) == 5) or
|
(sub_id == 0x17 and len(data) == 5) or
|
||||||
# HID++ 2.0 feature notifications have the SoftwareID 0
|
# HID++ 2.0 feature notifications have the SoftwareID 0
|
||||||
(address & 0x0F == 0x00)):
|
(address & 0x0F == 0x00)): # noqa: E129
|
||||||
return _HIDPP_Notification(devnumber, sub_id, address, data[2:])
|
return _HIDPP_Notification(devnumber, sub_id, address, data[2:])
|
||||||
|
|
||||||
|
|
||||||
|
@ -325,14 +325,14 @@ del namedtuple
|
||||||
def request(handle, devnumber, request_id, *params):
|
def request(handle, devnumber, request_id, *params):
|
||||||
"""Makes a feature call to a device and waits for a matching reply.
|
"""Makes a feature call to a device and waits for a matching reply.
|
||||||
|
|
||||||
This function will wait for a matching reply indefinitely.
|
This function will wait for a matching reply indefinitely.
|
||||||
|
|
||||||
:param handle: an open UR handle.
|
:param handle: an open UR handle.
|
||||||
:param devnumber: attached device number.
|
:param devnumber: attached device number.
|
||||||
:param request_id: a 16-bit integer.
|
:param request_id: a 16-bit integer.
|
||||||
:param params: parameters for the feature call, 3 to 16 bytes.
|
:param params: parameters for the feature call, 3 to 16 bytes.
|
||||||
:returns: the reply data, or ``None`` if some error occurred.
|
:returns: the reply data, or ``None`` if some error occurred.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# import inspect as _inspect
|
# import inspect as _inspect
|
||||||
# print ('\n '.join(str(s) for s in _inspect.stack()))
|
# print ('\n '.join(str(s) for s in _inspect.stack()))
|
||||||
|
@ -357,7 +357,7 @@ def request(handle, devnumber, request_id, *params):
|
||||||
else:
|
else:
|
||||||
params = b''
|
params = b''
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("(%s) device %d request_id {%04X} params [%s]", handle, devnumber, request_id, _strhex(params))
|
# _log.debug("(%s) device %d request_id {%04X} params [%s]", handle, devnumber, request_id, _strhex(params))
|
||||||
request_data = _pack('!H', request_id) + params
|
request_data = _pack('!H', request_id) + params
|
||||||
|
|
||||||
ihandle = int(handle)
|
ihandle = int(handle)
|
||||||
|
@ -380,12 +380,12 @@ def request(handle, devnumber, request_id, *params):
|
||||||
error = ord(reply_data[3:4])
|
error = ord(reply_data[3:4])
|
||||||
|
|
||||||
# if error == _hidpp10.ERROR.resource_error: # device unreachable
|
# if error == _hidpp10.ERROR.resource_error: # device unreachable
|
||||||
# _log.warn("(%s) device %d error on request {%04X}: unknown device", handle, devnumber, request_id)
|
# _log.warn("(%s) device %d error on request {%04X}: unknown device", handle, devnumber, request_id)
|
||||||
# raise DeviceUnreachable(number=devnumber, request=request_id)
|
# raise DeviceUnreachable(number=devnumber, request=request_id)
|
||||||
|
|
||||||
# if error == _hidpp10.ERROR.unknown_device: # unknown device
|
# if error == _hidpp10.ERROR.unknown_device: # unknown device
|
||||||
# _log.error("(%s) device %d error on request {%04X}: unknown device", handle, devnumber, request_id)
|
# _log.error("(%s) device %d error on request {%04X}: unknown device", handle, devnumber, request_id)
|
||||||
# raise NoSuchDevice(number=devnumber, request=request_id)
|
# raise NoSuchDevice(number=devnumber, request=request_id)
|
||||||
|
|
||||||
if _log.isEnabledFor(_DEBUG):
|
if _log.isEnabledFor(_DEBUG):
|
||||||
_log.debug(
|
_log.debug(
|
||||||
|
@ -437,13 +437,13 @@ def request(handle, devnumber, request_id, *params):
|
||||||
if n:
|
if n:
|
||||||
notifications_hook(n)
|
notifications_hook(n)
|
||||||
# elif _log.isEnabledFor(_DEBUG):
|
# elif _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data))
|
# _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data))
|
||||||
# elif _log.isEnabledFor(_DEBUG):
|
# elif _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data))
|
# _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data))
|
||||||
|
|
||||||
delta = _timestamp() - request_started
|
delta = _timestamp() - request_started
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("(%s) still waiting for reply, delta %f", handle, delta)
|
# _log.debug("(%s) still waiting for reply, delta %f", handle, delta)
|
||||||
|
|
||||||
_log.warn('timeout (%0.2f/%0.2f) on device %d request {%04X} params [%s]',
|
_log.warn('timeout (%0.2f/%0.2f) on device %d request {%04X} params [%s]',
|
||||||
delta, timeout, devnumber, request_id, _strhex(params))
|
delta, timeout, devnumber, request_id, _strhex(params))
|
||||||
|
@ -453,8 +453,8 @@ def request(handle, devnumber, request_id, *params):
|
||||||
def ping(handle, devnumber):
|
def ping(handle, devnumber):
|
||||||
"""Check if a device is connected to the receiver.
|
"""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.
|
:returns: The HID protocol supported by the device, as a floating point number, if the device is active.
|
||||||
"""
|
"""
|
||||||
if _log.isEnabledFor(_DEBUG):
|
if _log.isEnabledFor(_DEBUG):
|
||||||
_log.debug('(%s) pinging device %d', handle, devnumber)
|
_log.debug('(%s) pinging device %d', handle, devnumber)
|
||||||
|
|
||||||
|
@ -514,7 +514,7 @@ def ping(handle, devnumber):
|
||||||
if n:
|
if n:
|
||||||
notifications_hook(n)
|
notifications_hook(n)
|
||||||
# elif _log.isEnabledFor(_DEBUG):
|
# elif _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data))
|
# _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data))
|
||||||
|
|
||||||
delta = _timestamp() - request_started
|
delta = _timestamp() - request_started
|
||||||
|
|
||||||
|
|
|
@ -22,19 +22,19 @@
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||||
|
|
||||||
_DRIVER = ('hid-generic', 'generic-usb', 'logitech-djreceiver')
|
|
||||||
|
|
||||||
# max_devices is only used for receivers that do not support reading from _R.receiver_info offset 0x03, default to 1
|
# max_devices is only used for receivers that do not support reading from _R.receiver_info offset 0x03, default to 1
|
||||||
# may_unpair is only used for receivers that do not support reading from _R.receiver_info offset 0x03, default to False
|
# may_unpair is only used for receivers that do not support reading from _R.receiver_info offset 0x03, default to False
|
||||||
## should this last be changed so that may_unpair is used for all receivers? writing to _R.receiver_pairing doesn't seem right
|
## should this last be changed so that may_unpair is used for all receivers? writing to _R.receiver_pairing doesn't seem right
|
||||||
# re_pairs determines whether a receiver pairs by replacing existing pairings, default to False
|
# re_pairs determines whether a receiver pairs by replacing existing pairings, default to False
|
||||||
## currently only one receiver is so marked - should there be more?
|
## currently only one receiver is so marked - should there be more?
|
||||||
|
|
||||||
|
_DRIVER = ('hid-generic', 'generic-usb', 'logitech-djreceiver')
|
||||||
|
|
||||||
_unifying_receiver = lambda product_id: {
|
_unifying_receiver = lambda product_id: {
|
||||||
'vendor_id': 0x046d,
|
'vendor_id': 0x046d,
|
||||||
'product_id': product_id,
|
'product_id': product_id,
|
||||||
'usb_interface': 2,
|
'usb_interface': 2,
|
||||||
'hid_driver': _DRIVER,
|
'hid_driver': _DRIVER, # noqa: F821
|
||||||
'name': 'Unifying Receiver'
|
'name': 'Unifying Receiver'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ _nano_receiver = lambda product_id: {
|
||||||
'vendor_id': 0x046d,
|
'vendor_id': 0x046d,
|
||||||
'product_id': product_id,
|
'product_id': product_id,
|
||||||
'usb_interface': 1,
|
'usb_interface': 1,
|
||||||
'hid_driver': _DRIVER,
|
'hid_driver': _DRIVER, # noqa: F821
|
||||||
'name': 'Nano Receiver',
|
'name': 'Nano Receiver',
|
||||||
'may_unpair': False,
|
'may_unpair': False,
|
||||||
're_pairs': True
|
're_pairs': True
|
||||||
|
@ -52,7 +52,7 @@ _nano_receiver_max2 = lambda product_id: {
|
||||||
'vendor_id': 0x046d,
|
'vendor_id': 0x046d,
|
||||||
'product_id': product_id,
|
'product_id': product_id,
|
||||||
'usb_interface': 1,
|
'usb_interface': 1,
|
||||||
'hid_driver': _DRIVER,
|
'hid_driver': _DRIVER, # noqa: F821
|
||||||
'name': 'Nano Receiver',
|
'name': 'Nano Receiver',
|
||||||
'max_devices': 2,
|
'max_devices': 2,
|
||||||
'may_unpair': False,
|
'may_unpair': False,
|
||||||
|
@ -63,7 +63,7 @@ _nano_receiver_maxn = lambda product_id, max: {
|
||||||
'vendor_id': 0x046d,
|
'vendor_id': 0x046d,
|
||||||
'product_id': product_id,
|
'product_id': product_id,
|
||||||
'usb_interface': 1,
|
'usb_interface': 1,
|
||||||
'hid_driver': _DRIVER,
|
'hid_driver': _DRIVER, # noqa: F821
|
||||||
'name': 'Nano Receiver',
|
'name': 'Nano Receiver',
|
||||||
'max_devices': max,
|
'max_devices': max,
|
||||||
'may_unpair': False,
|
'may_unpair': False,
|
||||||
|
@ -74,7 +74,7 @@ _lenovo_receiver = lambda product_id: {
|
||||||
'vendor_id': 0x17ef,
|
'vendor_id': 0x17ef,
|
||||||
'product_id': product_id,
|
'product_id': product_id,
|
||||||
'usb_interface': 1,
|
'usb_interface': 1,
|
||||||
'hid_driver': _DRIVER,
|
'hid_driver': _DRIVER, # noqa: F821
|
||||||
'name': 'Nano Receiver'
|
'name': 'Nano Receiver'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ _lightspeed_receiver = lambda product_id: {
|
||||||
'vendor_id': 0x046d,
|
'vendor_id': 0x046d,
|
||||||
'product_id': product_id,
|
'product_id': product_id,
|
||||||
'usb_interface': 2,
|
'usb_interface': 2,
|
||||||
'hid_driver': _DRIVER,
|
'hid_driver': _DRIVER, # noqa: F821
|
||||||
'name': 'Lightspeed Receiver'
|
'name': 'Lightspeed Receiver'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,20 +26,20 @@ from collections import namedtuple
|
||||||
from struct import pack, unpack
|
from struct import pack, unpack
|
||||||
|
|
||||||
try:
|
try:
|
||||||
unicode
|
unicode # noqa: F821
|
||||||
# if Python2, unicode_literals will mess our first (un)pack() argument
|
# if Python2, unicode_literals will mess our first (un)pack() argument
|
||||||
_pack_str = pack
|
_pack_str = pack
|
||||||
_unpack_str = unpack
|
_unpack_str = unpack
|
||||||
pack = lambda x, *args: _pack_str(str(x), *args)
|
pack = lambda x, *args: _pack_str(str(x), *args)
|
||||||
unpack = lambda x, *args: _unpack_str(str(x), *args)
|
unpack = lambda x, *args: _unpack_str(str(x), *args)
|
||||||
|
|
||||||
is_string = lambda d: isinstance(d, unicode) or isinstance(d, str)
|
is_string = lambda d: isinstance(d, unicode) or isinstance(d, str) # noqa: F821
|
||||||
# no easy way to distinguish between b'' and '' :(
|
# no easy way to distinguish between b'' and '' :(
|
||||||
# or (isinstance(d, str) \
|
# or (isinstance(d, str) \
|
||||||
# and not any((chr(k) in d for k in range(0x00, 0x1F))) \
|
# and not any((chr(k) in d for k in range(0x00, 0x1F))) \
|
||||||
# and not any((chr(k) in d for k in range(0x80, 0xFF))) \
|
# and not any((chr(k) in d for k in range(0x80, 0xFF))) \
|
||||||
# )
|
# )
|
||||||
except:
|
except Exception:
|
||||||
# this is certanly Python 3
|
# this is certanly Python 3
|
||||||
# In Py3, unicode and str are equal (the unicode object does not exist)
|
# In Py3, unicode and str are equal (the unicode object does not exist)
|
||||||
is_string = lambda d: isinstance(d, str)
|
is_string = lambda d: isinstance(d, str)
|
||||||
|
@ -52,8 +52,8 @@ except:
|
||||||
class NamedInt(int):
|
class NamedInt(int):
|
||||||
"""An reqular Python integer with an attached name.
|
"""An reqular Python integer with an attached name.
|
||||||
|
|
||||||
Caution: comparison with strings will also match this NamedInt's name
|
Caution: comparison with strings will also match this NamedInt's name
|
||||||
(case-insensitive)."""
|
(case-insensitive)."""
|
||||||
def __new__(cls, value, name):
|
def __new__(cls, value, name):
|
||||||
assert is_string(name)
|
assert is_string(name)
|
||||||
obj = int.__new__(cls, value)
|
obj = int.__new__(cls, value)
|
||||||
|
@ -92,16 +92,16 @@ class NamedInt(int):
|
||||||
class NamedInts(object):
|
class NamedInts(object):
|
||||||
"""An ordered set of NamedInt values.
|
"""An ordered set of NamedInt values.
|
||||||
|
|
||||||
Indexing can be made by int or string, and will return the corresponding
|
Indexing can be made by int or string, and will return the corresponding
|
||||||
NamedInt if it exists in this set, or `None`.
|
NamedInt if it exists in this set, or `None`.
|
||||||
|
|
||||||
Extracting slices will return all present NamedInts in the given interval
|
Extracting slices will return all present NamedInts in the given interval
|
||||||
(extended slices are not supported).
|
(extended slices are not supported).
|
||||||
|
|
||||||
Assigning a string to an indexed int will create a new NamedInt in this set;
|
Assigning a string to an indexed int will create a new NamedInt in this set;
|
||||||
if the value already exists in the set (int or string), ValueError will be
|
if the value already exists in the set (int or string), ValueError will be
|
||||||
raised.
|
raised.
|
||||||
"""
|
"""
|
||||||
__slots__ = ('__dict__', '_values', '_indexed', '_fallback')
|
__slots__ = ('__dict__', '_values', '_indexed', '_fallback')
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
@ -119,7 +119,8 @@ class NamedInts(object):
|
||||||
self.__dict__ = values
|
self.__dict__ = values
|
||||||
self._values = sorted(list(values.values()))
|
self._values = sorted(list(values.values()))
|
||||||
self._indexed = {int(v): v for v in self._values}
|
self._indexed = {int(v): v for v in self._values}
|
||||||
# assert len(values) == len(self._indexed), "(%d) %r\n=> (%d) %r" % (len(values), values, len(self._indexed), self._indexed)
|
# assert len(values) == len(self._indexed)
|
||||||
|
# "(%d) %r\n=> (%d) %r" % (len(values), values, len(self._indexed), self._indexed)
|
||||||
self._fallback = None
|
self._fallback = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -237,8 +238,8 @@ def strhex(x):
|
||||||
|
|
||||||
def bytes2int(x):
|
def bytes2int(x):
|
||||||
"""Convert a bytes string to an int.
|
"""Convert a bytes string to an int.
|
||||||
The bytes are assumed to be in most-significant-first order.
|
The bytes are assumed to be in most-significant-first order.
|
||||||
"""
|
"""
|
||||||
assert isinstance(x, bytes)
|
assert isinstance(x, bytes)
|
||||||
assert len(x) < 9
|
assert len(x) < 9
|
||||||
qx = (b'\x00' * 8) + x
|
qx = (b'\x00' * 8) + x
|
||||||
|
@ -249,9 +250,9 @@ def bytes2int(x):
|
||||||
|
|
||||||
def int2bytes(x, count=None):
|
def int2bytes(x, count=None):
|
||||||
"""Convert an int to a bytes representation.
|
"""Convert an int to a bytes representation.
|
||||||
The bytes are ordered in most-significant-first order.
|
The bytes are ordered in most-significant-first order.
|
||||||
If 'count' is not given, the necessary number of bytes is computed.
|
If 'count' is not given, the necessary number of bytes is computed.
|
||||||
"""
|
"""
|
||||||
assert isinstance(x, int)
|
assert isinstance(x, int)
|
||||||
result = pack('!Q', x)
|
result = pack('!Q', x)
|
||||||
assert isinstance(result, bytes)
|
assert isinstance(result, bytes)
|
||||||
|
@ -268,8 +269,8 @@ def int2bytes(x, count=None):
|
||||||
|
|
||||||
class KwException(Exception):
|
class KwException(Exception):
|
||||||
"""An exception that remembers all arguments passed to the constructor.
|
"""An exception that remembers all arguments passed to the constructor.
|
||||||
They can be later accessed by simple member access.
|
They can be later accessed by simple member access.
|
||||||
"""
|
"""
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(KwException, self).__init__(kwargs)
|
super(KwException, self).__init__(kwargs)
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,8 @@ del getLogger
|
||||||
#
|
#
|
||||||
|
|
||||||
# <FeaturesSupported.xml sed '/LD_FID_/{s/.*LD_FID_/\t/;s/"[ \t]*Id="/=/;s/" \/>/,/p}' | sort -t= -k2
|
# <FeaturesSupported.xml sed '/LD_FID_/{s/.*LD_FID_/\t/;s/"[ \t]*Id="/=/;s/" \/>/,/p}' | sort -t= -k2
|
||||||
# additional features names taken from https://github.com/cvuchener/hidpp and https://github.com/Logitech/cpg-docs/tree/master/hidpp20
|
# additional features names taken from https://github.com/cvuchener/hidpp and
|
||||||
|
# https://github.com/Logitech/cpg-docs/tree/master/hidpp20
|
||||||
"""Possible features available on a Logitech device.
|
"""Possible features available on a Logitech device.
|
||||||
|
|
||||||
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
|
||||||
|
@ -414,8 +415,8 @@ class KeysArray(object):
|
||||||
remapped = key
|
remapped = key
|
||||||
except Exception:
|
except Exception:
|
||||||
remapped = key
|
remapped = key
|
||||||
remap_key = key
|
# remap_key = key
|
||||||
remap_flag = 0
|
# remap_flag = 0
|
||||||
|
|
||||||
remapped_text = special_keys.CONTROL[remapped]
|
remapped_text = special_keys.CONTROL[remapped]
|
||||||
self.keys[index] = _ReprogrammableKeyInfoV4(
|
self.keys[index] = _ReprogrammableKeyInfoV4(
|
||||||
|
@ -463,8 +464,8 @@ def feature_request(device, feature, function=0x00, *params):
|
||||||
def get_firmware(device):
|
def get_firmware(device):
|
||||||
"""Reads a device's firmware info.
|
"""Reads a device's firmware info.
|
||||||
|
|
||||||
:returns: a list of FirmwareInfo tuples, ordered by firmware layer.
|
:returns: a list of FirmwareInfo tuples, ordered by firmware layer.
|
||||||
"""
|
"""
|
||||||
count = feature_request(device, FEATURE.DEVICE_FW_VERSION)
|
count = feature_request(device, FEATURE.DEVICE_FW_VERSION)
|
||||||
if count:
|
if count:
|
||||||
count = ord(count[:1])
|
count = ord(count[:1])
|
||||||
|
@ -493,31 +494,31 @@ def get_firmware(device):
|
||||||
|
|
||||||
fw.append(fw_info)
|
fw.append(fw_info)
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("device %d firmware %s", devnumber, fw_info)
|
# _log.debug("device %d firmware %s", devnumber, fw_info)
|
||||||
return tuple(fw)
|
return tuple(fw)
|
||||||
|
|
||||||
|
|
||||||
def get_kind(device):
|
def get_kind(device):
|
||||||
"""Reads a device's type.
|
"""Reads a device's type.
|
||||||
|
|
||||||
:see DEVICE_KIND:
|
:see DEVICE_KIND:
|
||||||
: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 = feature_request(device, FEATURE.DEVICE_NAME, 0x20)
|
kind = feature_request(device, FEATURE.DEVICE_NAME, 0x20)
|
||||||
if kind:
|
if kind:
|
||||||
kind = ord(kind[:1])
|
kind = ord(kind[:1])
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("device %d type %d = %s", devnumber, kind, DEVICE_KIND[kind])
|
# _log.debug("device %d type %d = %s", devnumber, kind, DEVICE_KIND[kind])
|
||||||
return DEVICE_KIND[kind]
|
return DEVICE_KIND[kind]
|
||||||
|
|
||||||
|
|
||||||
def get_name(device):
|
def get_name(device):
|
||||||
"""Reads a device's name.
|
"""Reads a device's name.
|
||||||
|
|
||||||
: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 = feature_request(device, FEATURE.DEVICE_NAME)
|
name_length = feature_request(device, FEATURE.DEVICE_NAME)
|
||||||
if name_length:
|
if name_length:
|
||||||
name_length = ord(name_length[:1])
|
name_length = ord(name_length[:1])
|
||||||
|
@ -582,10 +583,8 @@ def decipher_voltage(voltage_report):
|
||||||
charge_lvl = CHARGE_LEVEL.critical
|
charge_lvl = CHARGE_LEVEL.critical
|
||||||
|
|
||||||
if _log.isEnabledFor(_DEBUG):
|
if _log.isEnabledFor(_DEBUG):
|
||||||
_log.debug(
|
_log.debug('device ???, battery voltage %d mV, charging = %s, charge status %d = %s, charge level %s, charge type %s',
|
||||||
'device %d, battery voltage %d mV, charging = %s, charge status %d = %s, charge level %s, charge type %s',
|
voltage, status, (flags & 0x03), charge_sts, charge_lvl, charge_type)
|
||||||
device.number, voltage, status, (flags & 0x03), charge_sts,
|
|
||||||
charge_lvl, charge_type)
|
|
||||||
|
|
||||||
return charge_lvl, status, voltage, charge_sts, charge_type
|
return charge_lvl, status, voltage, charge_sts, charge_type
|
||||||
|
|
||||||
|
@ -635,10 +634,9 @@ def get_hi_res_scrolling_info(device):
|
||||||
def get_pointer_speed_info(device):
|
def get_pointer_speed_info(device):
|
||||||
pointer_speed_info = feature_request(device, FEATURE.POINTER_SPEED)
|
pointer_speed_info = feature_request(device, FEATURE.POINTER_SPEED)
|
||||||
if pointer_speed_info:
|
if pointer_speed_info:
|
||||||
pointer_speed_hi, pointer_speed_lo = _unpack('!BB',
|
pointer_speed_hi, pointer_speed_lo = _unpack('!BB', pointer_speed_info[:2])
|
||||||
pointer_speed_info[:2])
|
# if pointer_speed_lo > 0:
|
||||||
#if pointer_speed_lo > 0:
|
# pointer_speed_lo = pointer_speed_lo
|
||||||
# pointer_speed_lo = pointer_speed_lo
|
|
||||||
return pointer_speed_hi + pointer_speed_lo / 256
|
return pointer_speed_hi + pointer_speed_lo / 256
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,10 +24,10 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
||||||
import gettext as _gettext
|
import gettext as _gettext
|
||||||
|
|
||||||
try:
|
try:
|
||||||
unicode
|
unicode # noqa: F821
|
||||||
_ = lambda x: _gettext.gettext(x).decode('UTF-8')
|
_ = lambda x: _gettext.gettext(x).decode('UTF-8')
|
||||||
ngettext = lambda *x: _gettext.ngettext(*x).decode('UTF-8')
|
ngettext = lambda *x: _gettext.ngettext(*x).decode('UTF-8')
|
||||||
except:
|
except Exception:
|
||||||
_ = _gettext.gettext
|
_ = _gettext.gettext
|
||||||
ngettext = _gettext.ngettext
|
ngettext = _gettext.ngettext
|
||||||
|
|
||||||
|
|
|
@ -46,8 +46,8 @@ del getLogger
|
||||||
class _ThreadedHandle(object):
|
class _ThreadedHandle(object):
|
||||||
"""A thread-local wrapper with different open handles for each thread.
|
"""A thread-local wrapper with different open handles for each thread.
|
||||||
|
|
||||||
Closing a ThreadedHandle will close all handles.
|
Closing a ThreadedHandle will close all handles.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('path', '_local', '_handles', '_listener')
|
__slots__ = ('path', '_local', '_handles', '_listener')
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ class _ThreadedHandle(object):
|
||||||
_log.error('%r failed to open new handle', self)
|
_log.error('%r failed to open new handle', self)
|
||||||
else:
|
else:
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("%r opened new handle %d", self, handle)
|
# _log.debug("%r opened new handle %d", self, handle)
|
||||||
self._local.handle = handle
|
self._local.handle = handle
|
||||||
self._handles.append(handle)
|
self._handles.append(handle)
|
||||||
return handle
|
return handle
|
||||||
|
@ -99,7 +99,7 @@ class _ThreadedHandle(object):
|
||||||
if self._local:
|
if self._local:
|
||||||
try:
|
try:
|
||||||
return self._local.handle
|
return self._local.handle
|
||||||
except:
|
except Exception:
|
||||||
return self._open()
|
return self._open()
|
||||||
|
|
||||||
__int__ = __index__
|
__int__ = __index__
|
||||||
|
@ -139,8 +139,8 @@ _EVENT_READ_TIMEOUT = 0.4 # in seconds
|
||||||
class EventsListener(_threading.Thread):
|
class EventsListener(_threading.Thread):
|
||||||
"""Listener thread for notifications from the Unifying Receiver.
|
"""Listener thread for notifications from the Unifying Receiver.
|
||||||
|
|
||||||
Incoming packets will be passed to the callback function in sequence.
|
Incoming packets will be passed to the callback function in sequence.
|
||||||
"""
|
"""
|
||||||
def __init__(self, receiver, notifications_callback):
|
def __init__(self, receiver, notifications_callback):
|
||||||
super(EventsListener, self).__init__(name=self.__class__.__name__ +
|
super(EventsListener, self).__init__(name=self.__class__.__name__ +
|
||||||
':' + receiver.path.split('/')[2])
|
':' + receiver.path.split('/')[2])
|
||||||
|
@ -190,20 +190,20 @@ class EventsListener(_threading.Thread):
|
||||||
|
|
||||||
if n:
|
if n:
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("%s: processing %s", self.receiver, n)
|
# _log.debug("%s: processing %s", self.receiver, n)
|
||||||
try:
|
try:
|
||||||
self._notifications_callback(n)
|
self._notifications_callback(n)
|
||||||
except:
|
except Exception:
|
||||||
_log.exception('processing %s', n)
|
_log.exception('processing %s', n)
|
||||||
|
|
||||||
# elif self.tick_period:
|
# elif self.tick_period:
|
||||||
# idle_reads -= 1
|
# idle_reads -= 1
|
||||||
# if idle_reads <= 0:
|
# if idle_reads <= 0:
|
||||||
# idle_reads = _IDLE_READS
|
# idle_reads = _IDLE_READS
|
||||||
# now = _timestamp()
|
# now = _timestamp()
|
||||||
# if now - last_tick >= self.tick_period:
|
# if now - last_tick >= self.tick_period:
|
||||||
# last_tick = now
|
# last_tick = now
|
||||||
# self.tick(now)
|
# self.tick(now)
|
||||||
|
|
||||||
del self._queued_notifications
|
del self._queued_notifications
|
||||||
self.has_stopped()
|
self.has_stopped()
|
||||||
|
@ -214,7 +214,7 @@ class EventsListener(_threading.Thread):
|
||||||
|
|
||||||
def has_started(self):
|
def has_started(self):
|
||||||
"""Called right after the thread has started, and before it starts
|
"""Called right after the thread has started, and before it starts
|
||||||
reading notification packets."""
|
reading notification packets."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def has_stopped(self):
|
def has_stopped(self):
|
||||||
|
@ -222,8 +222,8 @@ class EventsListener(_threading.Thread):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# def tick(self, timestamp):
|
# def tick(self, timestamp):
|
||||||
# """Called about every tick_period seconds."""
|
# """Called about every tick_period seconds."""
|
||||||
# pass
|
# pass
|
||||||
|
|
||||||
def _notifications_hook(self, n):
|
def _notifications_hook(self, n):
|
||||||
# Only consider unhandled notifications that were sent from this thread,
|
# Only consider unhandled notifications that were sent from this thread,
|
||||||
|
@ -231,7 +231,7 @@ class EventsListener(_threading.Thread):
|
||||||
assert _threading.current_thread() == self
|
assert _threading.current_thread() == self
|
||||||
if self._active: # and _threading.current_thread() == self:
|
if self._active: # and _threading.current_thread() == self:
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("queueing unhandled %s", n)
|
# _log.debug("queueing unhandled %s", n)
|
||||||
if not self._queued_notifications.full():
|
if not self._queued_notifications.full():
|
||||||
self._queued_notifications.put(n)
|
self._queued_notifications.put(n)
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ class PairedDevice(object):
|
||||||
self._power_switch = None
|
self._power_switch = None
|
||||||
|
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("new PairedDevice(%s, %s, %s)", receiver, number, link_notification)
|
# _log.debug("new PairedDevice(%s, %s, %s)", receiver, number, link_notification)
|
||||||
|
|
||||||
if link_notification is not None:
|
if link_notification is not None:
|
||||||
self.online = not bool(ord(link_notification.data[0:1]) & 0x40)
|
self.online = not bool(ord(link_notification.data[0:1]) & 0x40)
|
||||||
|
@ -156,7 +156,7 @@ class PairedDevice(object):
|
||||||
self.online = self._protocol is not None
|
self.online = self._protocol is not None
|
||||||
|
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("device %d protocol %s", self.number, self._protocol)
|
# _log.debug("device %d protocol %s", self.number, self._protocol)
|
||||||
return self._protocol or 0
|
return self._protocol or 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -169,7 +169,7 @@ class PairedDevice(object):
|
||||||
codename = codename[2:2 + codename_length]
|
codename = codename[2:2 + codename_length]
|
||||||
self._codename = codename.decode('ascii')
|
self._codename = codename.decode('ascii')
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("device %d codename %s", self.number, self._codename)
|
# _log.debug("device %d codename %s", self.number, self._codename)
|
||||||
else:
|
else:
|
||||||
self._codename = '? (%s)' % self.wpid
|
self._codename = '? (%s)' % self.wpid
|
||||||
return self._codename
|
return self._codename
|
||||||
|
@ -276,7 +276,7 @@ class PairedDevice(object):
|
||||||
|
|
||||||
def enable_notifications(self, enable=True):
|
def enable_notifications(self, enable=True):
|
||||||
"""Enable or disable device (dis)connection notifications on this
|
"""Enable or disable device (dis)connection notifications on this
|
||||||
receiver."""
|
receiver."""
|
||||||
if not bool(self.receiver) or self.protocol >= 2.0:
|
if not bool(self.receiver) or self.protocol >= 2.0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -350,8 +350,8 @@ class PairedDevice(object):
|
||||||
class Receiver(object):
|
class Receiver(object):
|
||||||
"""A Unifying Receiver instance.
|
"""A Unifying Receiver instance.
|
||||||
|
|
||||||
The paired devices are available through the sequence interface.
|
The paired devices are available through the sequence interface.
|
||||||
"""
|
"""
|
||||||
number = 0xFF
|
number = 0xFF
|
||||||
kind = None
|
kind = None
|
||||||
|
|
||||||
|
@ -414,7 +414,7 @@ class Receiver(object):
|
||||||
|
|
||||||
def enable_notifications(self, enable=True):
|
def enable_notifications(self, enable=True):
|
||||||
"""Enable or disable device (dis)connection notifications on this
|
"""Enable or disable device (dis)connection notifications on this
|
||||||
receiver."""
|
receiver."""
|
||||||
if not self.handle:
|
if not self.handle:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -482,7 +482,7 @@ class Receiver(object):
|
||||||
return 0 if count is None else ord(count[1:2])
|
return 0 if count is None else ord(count[1:2])
|
||||||
|
|
||||||
# def has_devices(self):
|
# def has_devices(self):
|
||||||
# return len(self) > 0 or self.count() > 0
|
# return len(self) > 0 or self.count() > 0
|
||||||
|
|
||||||
def request(self, request_id, *params):
|
def request(self, request_id, *params):
|
||||||
if bool(self):
|
if bool(self):
|
||||||
|
@ -579,8 +579,8 @@ class Receiver(object):
|
||||||
def open(self, device_info):
|
def open(self, device_info):
|
||||||
"""Opens a Logitech Receiver found attached to the machine, by Linux device path.
|
"""Opens a Logitech Receiver 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``.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
handle = _base.open_path(device_info.path)
|
handle = _base.open_path(device_info.path)
|
||||||
if handle:
|
if handle:
|
||||||
|
@ -589,5 +589,5 @@ class Receiver(object):
|
||||||
_log.exception('open %s', device_info)
|
_log.exception('open %s', device_info)
|
||||||
if e.errno == _errno.EACCES:
|
if e.errno == _errno.EACCES:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception:
|
||||||
_log.exception('open %s', device_info)
|
_log.exception('open %s', device_info)
|
||||||
|
|
|
@ -23,7 +23,6 @@ import math
|
||||||
|
|
||||||
from copy import copy as _copy
|
from copy import copy as _copy
|
||||||
from logging import DEBUG as _DEBUG
|
from logging import DEBUG as _DEBUG
|
||||||
from logging import INFO as _INFO
|
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
|
||||||
from .common import NamedInt as _NamedInt
|
from .common import NamedInt as _NamedInt
|
||||||
|
@ -47,7 +46,7 @@ KIND = _NamedInts(toggle=0x01,
|
||||||
|
|
||||||
class Setting(object):
|
class Setting(object):
|
||||||
"""A setting descriptor.
|
"""A setting descriptor.
|
||||||
Needs to be instantiated for each specific device."""
|
Needs to be instantiated for each specific device."""
|
||||||
__slots__ = ('name', 'label', 'description', 'kind', 'device_kind',
|
__slots__ = ('name', 'label', 'description', 'kind', 'device_kind',
|
||||||
'feature', '_rw', '_validator', '_device', '_value')
|
'feature', '_rw', '_validator', '_device', '_value')
|
||||||
|
|
||||||
|
@ -196,7 +195,7 @@ class Setting(object):
|
||||||
|
|
||||||
class Settings(Setting):
|
class Settings(Setting):
|
||||||
"""A setting descriptor for multiple choices, being a map from keys to values.
|
"""A setting descriptor for multiple choices, being a map from keys to values.
|
||||||
Needs to be instantiated for each specific device."""
|
Needs to be instantiated for each specific device."""
|
||||||
def read(self, cached=True):
|
def read(self, cached=True):
|
||||||
assert hasattr(self, '_value')
|
assert hasattr(self, '_value')
|
||||||
assert hasattr(self, '_device')
|
assert hasattr(self, '_device')
|
||||||
|
@ -220,7 +219,7 @@ class Settings(Setting):
|
||||||
|
|
||||||
if self._device.online:
|
if self._device.online:
|
||||||
reply_map = {}
|
reply_map = {}
|
||||||
for key, value in self._validator.choices.items():
|
for key in self._validator.choices:
|
||||||
reply = self._rw.read(self._device, key)
|
reply = self._rw.read(self._device, key)
|
||||||
if reply:
|
if reply:
|
||||||
# keys are ints, because that is what the device uses,
|
# keys are ints, because that is what the device uses,
|
||||||
|
@ -327,7 +326,7 @@ class Settings(Setting):
|
||||||
|
|
||||||
class BitFieldSetting(Setting):
|
class BitFieldSetting(Setting):
|
||||||
"""A setting descriptor for a set of choices represented by one bit each, being a map from options to booleans.
|
"""A setting descriptor for a set of choices represented by one bit each, being a map from options to booleans.
|
||||||
Needs to be instantiated for each specific device."""
|
Needs to be instantiated for each specific device."""
|
||||||
def read(self, cached=True):
|
def read(self, cached=True):
|
||||||
assert hasattr(self, '_value')
|
assert hasattr(self, '_value')
|
||||||
assert hasattr(self, '_device')
|
assert hasattr(self, '_device')
|
||||||
|
@ -666,7 +665,7 @@ class BitFieldValidator(object):
|
||||||
r = _bytes2int(reply_bytes[:self.byte_count])
|
r = _bytes2int(reply_bytes[:self.byte_count])
|
||||||
value = {str(int(k)): False for k in self.options}
|
value = {str(int(k)): False for k in self.options}
|
||||||
m = 1
|
m = 1
|
||||||
for i in range(8 * self.byte_count):
|
for _ in range(8 * self.byte_count):
|
||||||
if m in self.options:
|
if m in self.options:
|
||||||
value[str(int(m))] = bool(r & m)
|
value[str(int(m))] = bool(r & m)
|
||||||
m <<= 1
|
m <<= 1
|
||||||
|
@ -686,9 +685,9 @@ class ChoicesValidator(object):
|
||||||
|
|
||||||
kind = KIND.choice
|
kind = KIND.choice
|
||||||
"""Translates between NamedInts and a byte sequence.
|
"""Translates between NamedInts and a byte sequence.
|
||||||
:param choices: a list of NamedInts
|
:param choices: a list of NamedInts
|
||||||
:param bytes_count: the size of the derived byte sequence. If None, it
|
:param bytes_count: the size of the derived byte sequence. If None, it
|
||||||
will be calculated from the choices."""
|
will be calculated from the choices."""
|
||||||
def __init__(self, choices, bytes_count=None):
|
def __init__(self, choices, bytes_count=None):
|
||||||
assert choices is not None
|
assert choices is not None
|
||||||
assert isinstance(choices, _NamedInts)
|
assert isinstance(choices, _NamedInts)
|
||||||
|
@ -790,10 +789,10 @@ class RangeValidator(object):
|
||||||
|
|
||||||
kind = KIND.range
|
kind = KIND.range
|
||||||
"""Translates between integers and a byte sequence.
|
"""Translates between integers and a byte sequence.
|
||||||
:param min_value: minimum accepted value (inclusive)
|
:param min_value: minimum accepted value (inclusive)
|
||||||
:param max_value: maximum accepted value (inclusive)
|
:param max_value: maximum accepted value (inclusive)
|
||||||
:param bytes_count: the size of the derived byte sequence. If None, it
|
:param bytes_count: the size of the derived byte sequence. If None, it
|
||||||
will be calculated from the range."""
|
will be calculated from the range."""
|
||||||
def __init__(self, min_value, max_value, bytes_count=None):
|
def __init__(self, min_value, max_value, bytes_count=None):
|
||||||
assert max_value > min_value
|
assert max_value > min_value
|
||||||
self.min_value = min_value
|
self.min_value = min_value
|
||||||
|
|
|
@ -28,7 +28,6 @@ from . import hidpp20 as _hidpp20
|
||||||
from . import special_keys as _special_keys
|
from . import special_keys as _special_keys
|
||||||
from .common import NamedInt as _NamedInt
|
from .common import NamedInt as _NamedInt
|
||||||
from .common import NamedInts as _NamedInts
|
from .common import NamedInts as _NamedInts
|
||||||
from .common import ReprogrammableKeyInfoV4 as _ReprogrammableKeyInfoV4
|
|
||||||
from .common import bytes2int as _bytes2int
|
from .common import bytes2int as _bytes2int
|
||||||
from .common import int2bytes as _int2bytes
|
from .common import int2bytes as _int2bytes
|
||||||
from .common import unpack as _unpack
|
from .common import unpack as _unpack
|
||||||
|
@ -449,7 +448,8 @@ def _feature_k375s_fn_swap():
|
||||||
device_kind=(_DK.keyboard, ))
|
device_kind=(_DK.keyboard, ))
|
||||||
|
|
||||||
|
|
||||||
# FIXME: This will enable all supported backlight settings, we should allow the users to select which settings they want to enable.
|
# FIXME: This will enable all supported backlight settings,
|
||||||
|
# we should allow the users to select which settings they want to enable.
|
||||||
def _feature_backlight2():
|
def _feature_backlight2():
|
||||||
return feature_toggle(_BACKLIGHT[0],
|
return feature_toggle(_BACKLIGHT[0],
|
||||||
_F.BACKLIGHT2,
|
_F.BACKLIGHT2,
|
||||||
|
@ -729,11 +729,11 @@ def check_feature_settings(device, already_known):
|
||||||
|
|
||||||
def check_feature(name, featureId, featureFn):
|
def check_feature(name, featureId, featureFn):
|
||||||
"""
|
"""
|
||||||
:param name: name for the setting
|
:param name: name for the setting
|
||||||
:param featureId: the numeric Feature ID for this setting implementation
|
:param featureId: the numeric Feature ID for this setting implementation
|
||||||
:param featureFn: the function for this setting implementation
|
:param featureFn: the function for this setting implementation
|
||||||
"""
|
"""
|
||||||
if not featureId in device.features:
|
if featureId not in device.features:
|
||||||
return
|
return
|
||||||
if any(s.name == name for s in already_known):
|
if any(s.name == name for s in already_known):
|
||||||
return
|
return
|
||||||
|
@ -749,7 +749,7 @@ def check_feature_settings(device, already_known):
|
||||||
_log.error('check_feature[%s] inconsistent feature %s', featureId,
|
_log.error('check_feature[%s] inconsistent feature %s', featureId,
|
||||||
reason)
|
reason)
|
||||||
|
|
||||||
for name, featureId, featureFn, _, _ in _SETTINGS_TABLE:
|
for name, featureId, featureFn, __, __ in _SETTINGS_TABLE:
|
||||||
if featureId and featureFn:
|
if featureId and featureFn:
|
||||||
check_feature(name, featureId, featureFn)
|
check_feature(name, featureId, featureFn)
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -194,7 +194,8 @@ CONTROL = _NamedInts(
|
||||||
Fn_Left_Click=0x00B7, # from K400 Plus
|
Fn_Left_Click=0x00B7, # from K400 Plus
|
||||||
# https://docs.google.com/document/u/0/d/1YvXICgSe8BcBAuMr4Xu_TutvAxaa-RnGfyPFWBWzhkc/export?format=docx
|
# https://docs.google.com/document/u/0/d/1YvXICgSe8BcBAuMr4Xu_TutvAxaa-RnGfyPFWBWzhkc/export?format=docx
|
||||||
# Extract to csv. Eliminate extra linefeeds and spaces.
|
# Extract to csv. Eliminate extra linefeeds and spaces.
|
||||||
# awk -F, '/0x/{gsub(" \\+ ","_",$2); gsub("/","__",$2); gsub(" -","_Down",$2); gsub(" \\+","_Up",$2); gsub("[()\"-]","",$2); gsub(" ","_",$2); printf("\t%s=0x%04X,\n", $2, $1)}' < controls.cvs
|
# awk -F, '/0x/{gsub(" \\+ ","_",$2); gsub("/","__",$2); gsub(" -","_Down",$2);
|
||||||
|
# gsub(" \\+","_Up",$2); gsub("[()\"-]","",$2); gsub(" ","_",$2); printf("\t%s=0x%04X,\n", $2, $1)}' < controls.cvs
|
||||||
Second_Left_Click=0x00B8, # Second_LClick / on K400 Plus
|
Second_Left_Click=0x00B8, # Second_LClick / on K400 Plus
|
||||||
Fn_Second_Left_Click=0x00B9, # Fn_Second_LClick
|
Fn_Second_Left_Click=0x00B9, # Fn_Second_LClick
|
||||||
MultiPlatform_App_Switch=0x00BA,
|
MultiPlatform_App_Switch=0x00BA,
|
||||||
|
@ -415,7 +416,8 @@ TASK = _NamedInts(
|
||||||
ShowUI=0x0092,
|
ShowUI=0x0092,
|
||||||
# https://docs.google.com/document/d/1Dpx_nWRQAZox_zpZ8SNc9nOkSDE9svjkghOCbzopabc/edit
|
# https://docs.google.com/document/d/1Dpx_nWRQAZox_zpZ8SNc9nOkSDE9svjkghOCbzopabc/edit
|
||||||
# Extract to csv. Eliminate extra linefeeds and spaces. Turn / into __ and space into _
|
# Extract to csv. Eliminate extra linefeeds and spaces. Turn / into __ and space into _
|
||||||
# awk -F, '/0x/{gsub(" \\+ ","_",$2); gsub("_-","_Down",$2); gsub("_\\+","_Up",$2); gsub("[()\"-]","",$2); gsub(" ","_",$2); printf("\t%s=0x%04X,\n", $2, $1)}' < tasks.csv > tasks.py
|
# awk -F, '/0x/{gsub(" \\+ ","_",$2); gsub("_-","_Down",$2); gsub("_\\+","_Up",$2);
|
||||||
|
# gsub("[()\"-]","",$2); gsub(" ","_",$2); printf("\t%s=0x%04X,\n", $2, $1)}' < tasks.csv > tasks.py
|
||||||
Switch_Presentation__Switch_Screen=0x0093, # on K400 Plus
|
Switch_Presentation__Switch_Screen=0x0093, # on K400 Plus
|
||||||
Minimize_Window=0x0094,
|
Minimize_Window=0x0094,
|
||||||
Maximize_Window=0x0095, # on K400 Plus
|
Maximize_Window=0x0095, # on K400 Plus
|
||||||
|
|
|
@ -88,8 +88,8 @@ def attach_to(device, changed_callback):
|
||||||
|
|
||||||
class ReceiverStatus(dict):
|
class ReceiverStatus(dict):
|
||||||
"""The 'runtime' status of a receiver, mostly about the pairing process --
|
"""The 'runtime' status of a receiver, mostly about the pairing process --
|
||||||
is the pairing lock open or closed, any pairing errors, etc.
|
is the pairing lock open or closed, any pairing errors, etc.
|
||||||
"""
|
"""
|
||||||
def __init__(self, receiver, changed_callback):
|
def __init__(self, receiver, changed_callback):
|
||||||
assert receiver
|
assert receiver
|
||||||
self._receiver = receiver
|
self._receiver = receiver
|
||||||
|
@ -118,17 +118,17 @@ class ReceiverStatus(dict):
|
||||||
self._changed_callback(self._receiver, alert=alert, reason=reason)
|
self._changed_callback(self._receiver, alert=alert, reason=reason)
|
||||||
|
|
||||||
# def poll(self, timestamp):
|
# def poll(self, timestamp):
|
||||||
# r = self._receiver
|
# r = self._receiver
|
||||||
# assert r
|
# assert r
|
||||||
#
|
#
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("polling status of %s", r)
|
# _log.debug("polling status of %s", r)
|
||||||
#
|
#
|
||||||
# # make sure to read some stuff that may be read later by the UI
|
# # make sure to read some stuff that may be read later by the UI
|
||||||
# r.serial, r.firmware, None
|
# r.serial, r.firmware, None
|
||||||
#
|
#
|
||||||
# # get an update of the notification flags
|
# # get an update of the notification flags
|
||||||
# # self[KEYS.NOTIFICATION_FLAGS] = _hidpp10.get_notification_flags(r)
|
# # self[KEYS.NOTIFICATION_FLAGS] = _hidpp10.get_notification_flags(r)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -138,9 +138,9 @@ class ReceiverStatus(dict):
|
||||||
|
|
||||||
class DeviceStatus(dict):
|
class DeviceStatus(dict):
|
||||||
"""Holds the 'runtime' status of a peripheral -- things like
|
"""Holds the 'runtime' status of a peripheral -- things like
|
||||||
active/inactive, battery charge, lux, etc. It updates them mostly by
|
active/inactive, battery charge, lux, etc. It updates them mostly by
|
||||||
processing incoming notification events from the device itself.
|
processing incoming notification events from the device itself.
|
||||||
"""
|
"""
|
||||||
def __init__(self, device, changed_callback):
|
def __init__(self, device, changed_callback):
|
||||||
assert device
|
assert device
|
||||||
self._device = device
|
self._device = device
|
||||||
|
@ -177,7 +177,8 @@ class DeviceStatus(dict):
|
||||||
|
|
||||||
light_level = self.get(KEYS.LIGHT_LEVEL)
|
light_level = self.get(KEYS.LIGHT_LEVEL)
|
||||||
if light_level is not None:
|
if light_level is not None:
|
||||||
if comma: yield ', '
|
if comma:
|
||||||
|
yield ', '
|
||||||
yield _('Lighting: %(level)s lux') % {'level': light_level}
|
yield _('Lighting: %(level)s lux') % {'level': light_level}
|
||||||
|
|
||||||
return ''.join(i for i in _items())
|
return ''.join(i for i in _items())
|
||||||
|
@ -352,7 +353,7 @@ class DeviceStatus(dict):
|
||||||
if battery is not None:
|
if battery is not None:
|
||||||
self[KEYS.BATTERY_LEVEL] = battery
|
self[KEYS.BATTERY_LEVEL] = battery
|
||||||
|
|
||||||
if self.updated == 0 and active == True:
|
if self.updated == 0 and active is True:
|
||||||
# if the device is active on the very first status notification,
|
# if the device is active on the very first status notification,
|
||||||
# (meaning just when the program started or a new receiver was just
|
# (meaning just when the program started or a new receiver was just
|
||||||
# detected), pop-up a notification about it
|
# detected), pop-up a notification about it
|
||||||
|
@ -360,40 +361,40 @@ class DeviceStatus(dict):
|
||||||
self.updated = timestamp
|
self.updated = timestamp
|
||||||
|
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("device %d changed: active=%s %s", d.number, self._active, dict(self))
|
# _log.debug("device %d changed: active=%s %s", d.number, self._active, dict(self))
|
||||||
self._changed_callback(d, alert, reason)
|
self._changed_callback(d, alert, reason)
|
||||||
|
|
||||||
# def poll(self, timestamp):
|
# def poll(self, timestamp):
|
||||||
# d = self._device
|
# d = self._device
|
||||||
# if not d:
|
# if not d:
|
||||||
# _log.error("polling status of invalid device")
|
# _log.error("polling status of invalid device")
|
||||||
# return
|
# return
|
||||||
#
|
#
|
||||||
# if self._active:
|
# if self._active:
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("polling status of %s", d)
|
# _log.debug("polling status of %s", d)
|
||||||
#
|
#
|
||||||
# # read these from the device, the UI may need them later
|
# # read these from the device, the UI may need them later
|
||||||
# d.protocol, d.serial, d.firmware, d.kind, d.name, d.settings, None
|
# d.protocol, d.serial, d.firmware, d.kind, d.name, d.settings, None
|
||||||
#
|
#
|
||||||
# # make sure we know all the features of the device
|
# # make sure we know all the features of the device
|
||||||
# # if d.features:
|
# # if d.features:
|
||||||
# # d.features[:]
|
# # d.features[:]
|
||||||
#
|
#
|
||||||
# # devices may go out-of-range while still active, or the computer
|
# # devices may go out-of-range while still active, or the computer
|
||||||
# # may go to sleep and wake up without the devices available
|
# # may go to sleep and wake up without the devices available
|
||||||
# if timestamp - self.updated > _STATUS_TIMEOUT:
|
# if timestamp - self.updated > _STATUS_TIMEOUT:
|
||||||
# if d.ping():
|
# if d.ping():
|
||||||
# timestamp = self.updated = _timestamp()
|
# timestamp = self.updated = _timestamp()
|
||||||
# else:
|
# else:
|
||||||
# self.changed(active=False, reason='out of range')
|
# self.changed(active=False, reason='out of range')
|
||||||
#
|
#
|
||||||
# # if still active, make sure we know the battery level
|
# # if still active, make sure we know the battery level
|
||||||
# if KEYS.BATTERY_LEVEL not in self:
|
# if KEYS.BATTERY_LEVEL not in self:
|
||||||
# self.read_battery(timestamp)
|
# self.read_battery(timestamp)
|
||||||
#
|
#
|
||||||
# elif timestamp - self.updated > _STATUS_TIMEOUT:
|
# elif timestamp - self.updated > _STATUS_TIMEOUT:
|
||||||
# if d.ping():
|
# if d.ping():
|
||||||
# self.changed(active=True)
|
# self.changed(active=True)
|
||||||
# else:
|
# else:
|
||||||
# self.updated = _timestamp()
|
# self.updated = _timestamp()
|
||||||
|
|
|
@ -139,11 +139,12 @@ def _find_device(receivers, name):
|
||||||
if len(name) == 1:
|
if len(name) == 1:
|
||||||
try:
|
try:
|
||||||
number = int(name)
|
number = int(name)
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
assert not (number < 0)
|
assert not (number < 0)
|
||||||
if number > 6: number = None
|
if number > 6:
|
||||||
|
number = None
|
||||||
|
|
||||||
for r in receivers:
|
for r in receivers:
|
||||||
if number and number <= r.max_devices:
|
if number and number <= r.max_devices:
|
||||||
|
@ -168,7 +169,7 @@ def run(cli_args=None, hidraw_path=None):
|
||||||
args = _cli_parser.parse_args()
|
args = _cli_parser.parse_args()
|
||||||
# Python 3 has an undocumented 'feature' that breaks parsing empty args
|
# Python 3 has an undocumented 'feature' that breaks parsing empty args
|
||||||
# http://bugs.python.org/issue16308
|
# http://bugs.python.org/issue16308
|
||||||
if not 'cmd' in args:
|
if 'cmd' not in args:
|
||||||
_cli_parser.print_usage(_sys.stderr)
|
_cli_parser.print_usage(_sys.stderr)
|
||||||
_sys.stderr.write('%s: error: too few arguments\n' % NAME.lower())
|
_sys.stderr.write('%s: error: too few arguments\n' % NAME.lower())
|
||||||
_sys.exit(2)
|
_sys.exit(2)
|
||||||
|
@ -183,11 +184,10 @@ def run(cli_args=None, hidraw_path=None):
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
m = import_module('.' + action, package=__name__)
|
m = import_module('.' + action, package=__name__)
|
||||||
m.run(c, args, _find_receiver, _find_device)
|
m.run(c, args, _find_receiver, _find_device)
|
||||||
except AssertionError as e:
|
except AssertionError:
|
||||||
from traceback import extract_tb
|
from traceback import extract_tb
|
||||||
tb_last = extract_tb(_sys.exc_info()[2])[-1]
|
tb_last = extract_tb(_sys.exc_info()[2])[-1]
|
||||||
_sys.exit('%s: assertion failed: %s line %d' %
|
_sys.exit('%s: assertion failed: %s line %d' % (NAME.lower(), tb_last[0], tb_last[1]))
|
||||||
(NAME.lower(), tb_last[0], tb_last[1]))
|
except Exception:
|
||||||
except Exception as e:
|
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
_sys.exit('%s: error: %s' % (NAME.lower(), format_exc()))
|
_sys.exit('%s: error: %s' % (NAME.lower(), format_exc()))
|
||||||
|
|
|
@ -84,7 +84,7 @@ def run(receivers, args, find_receiver, find_device):
|
||||||
value = args.value
|
value = args.value
|
||||||
try:
|
try:
|
||||||
value = bool(int(value))
|
value = bool(int(value))
|
||||||
except:
|
except Exception:
|
||||||
if value.lower() in ('true', 'yes', 'on', 't', 'y'):
|
if value.lower() in ('true', 'yes', 'on', 't', 'y'):
|
||||||
value = True
|
value = True
|
||||||
elif value.lower() in ('false', 'no', 'off', 'f', 'n'):
|
elif value.lower() in ('false', 'no', 'off', 'f', 'n'):
|
||||||
|
|
|
@ -19,12 +19,7 @@
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||||
|
|
||||||
from time import time as _timestamp
|
|
||||||
|
|
||||||
from logitech_receiver import base as _base
|
|
||||||
from logitech_receiver import hidpp10 as _hidpp10
|
from logitech_receiver import hidpp10 as _hidpp10
|
||||||
from logitech_receiver import notifications as _notifications
|
|
||||||
from logitech_receiver import status as _status
|
|
||||||
from logitech_receiver.common import strhex as _strhex
|
from logitech_receiver.common import strhex as _strhex
|
||||||
from solaar.cli.show import _print_receiver
|
from solaar.cli.show import _print_receiver
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ def _load():
|
||||||
try:
|
try:
|
||||||
with open(_file_path, 'r') as config_file:
|
with open(_file_path, 'r') as config_file:
|
||||||
loaded_configuration = _json_load(config_file)
|
loaded_configuration = _json_load(config_file)
|
||||||
except:
|
except Exception:
|
||||||
_log.error('failed to load from %s', _file_path)
|
_log.error('failed to load from %s', _file_path)
|
||||||
|
|
||||||
# loaded_configuration.update(_configuration)
|
# loaded_configuration.update(_configuration)
|
||||||
|
@ -70,7 +70,7 @@ def save():
|
||||||
if not _path.isdir(dirname):
|
if not _path.isdir(dirname):
|
||||||
try:
|
try:
|
||||||
_os.makedirs(dirname)
|
_os.makedirs(dirname)
|
||||||
except:
|
except Exception:
|
||||||
_log.error('failed to create %s', dirname)
|
_log.error('failed to create %s', dirname)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ def save():
|
||||||
if _log.isEnabledFor(_INFO):
|
if _log.isEnabledFor(_INFO):
|
||||||
_log.info('saved %s to %s', _configuration, _file_path)
|
_log.info('saved %s to %s', _configuration, _file_path)
|
||||||
return True
|
return True
|
||||||
except:
|
except Exception:
|
||||||
_log.error('failed to save to %s', _file_path)
|
_log.error('failed to save to %s', _file_path)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -129,7 +129,8 @@ def main():
|
||||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||||
|
|
||||||
args = _parse_arguments()
|
args = _parse_arguments()
|
||||||
if not args: return
|
if not args:
|
||||||
|
return
|
||||||
if args.action:
|
if args.action:
|
||||||
# if any argument, run comandline and exit
|
# if any argument, run comandline and exit
|
||||||
return _cli.run(args.action, args.hidraw_path)
|
return _cli.run(args.action, args.hidraw_path)
|
||||||
|
@ -149,9 +150,8 @@ def main():
|
||||||
_upower.watch(lambda: listener.ping_all(True))
|
_upower.watch(lambda: listener.ping_all(True))
|
||||||
|
|
||||||
# main UI event loop
|
# main UI event loop
|
||||||
ui.run_loop(listener.start_all, listener.stop_all,
|
ui.run_loop(listener.start_all, listener.stop_all, args.window != 'only', args.window != 'hide')
|
||||||
args.window != 'only', args.window != 'hide')
|
except Exception:
|
||||||
except Exception as e:
|
|
||||||
import sys
|
import sys
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
sys.exit('%s: error: %s' % (NAME.lower(), format_exc()))
|
sys.exit('%s: error: %s' % (NAME.lower(), format_exc()))
|
||||||
|
|
|
@ -63,9 +63,9 @@ _gettext.textdomain(_LOCALE_DOMAIN)
|
||||||
_gettext.install(_LOCALE_DOMAIN)
|
_gettext.install(_LOCALE_DOMAIN)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
unicode
|
unicode # noqa: F821
|
||||||
_ = lambda x: _gettext.gettext(x).decode('UTF-8')
|
_ = lambda x: _gettext.gettext(x).decode('UTF-8')
|
||||||
ngettext = lambda *x: _gettext.ngettext(*x).decode('UTF-8')
|
ngettext = lambda *x: _gettext.ngettext(*x).decode('UTF-8')
|
||||||
except:
|
except Exception:
|
||||||
_ = _gettext.gettext
|
_ = _gettext.gettext
|
||||||
ngettext = _gettext.ngettext
|
ngettext = _gettext.ngettext
|
||||||
|
|
|
@ -70,7 +70,7 @@ def _ghost(device):
|
||||||
|
|
||||||
class ReceiverListener(_listener.EventsListener):
|
class ReceiverListener(_listener.EventsListener):
|
||||||
"""Keeps the status of a Receiver.
|
"""Keeps the status of a Receiver.
|
||||||
"""
|
"""
|
||||||
def __init__(self, receiver, status_changed_callback):
|
def __init__(self, receiver, status_changed_callback):
|
||||||
super(ReceiverListener, self).__init__(receiver,
|
super(ReceiverListener, self).__init__(receiver,
|
||||||
self._notifications_handler)
|
self._notifications_handler)
|
||||||
|
@ -90,7 +90,7 @@ class ReceiverListener(_listener.EventsListener):
|
||||||
self.receiver.status[
|
self.receiver.status[
|
||||||
_status.KEYS.NOTIFICATION_FLAGS] = notification_flags
|
_status.KEYS.NOTIFICATION_FLAGS] = notification_flags
|
||||||
self.receiver.notify_devices()
|
self.receiver.notify_devices()
|
||||||
self._status_changed(self.receiver) #, _status.ALERT.NOTIFICATION)
|
self._status_changed(self.receiver) # , _status.ALERT.NOTIFICATION)
|
||||||
|
|
||||||
def has_stopped(self):
|
def has_stopped(self):
|
||||||
r, self.receiver = self.receiver, None
|
r, self.receiver = self.receiver, None
|
||||||
|
@ -106,46 +106,46 @@ class ReceiverListener(_listener.EventsListener):
|
||||||
if r:
|
if r:
|
||||||
try:
|
try:
|
||||||
r.close()
|
r.close()
|
||||||
except:
|
except Exception:
|
||||||
_log.exception('closing receiver %s' % r.path)
|
_log.exception('closing receiver %s' % r.path)
|
||||||
self.status_changed_callback(r) #, _status.ALERT.NOTIFICATION)
|
self.status_changed_callback(r) # , _status.ALERT.NOTIFICATION)
|
||||||
|
|
||||||
# def tick(self, timestamp):
|
# def tick(self, timestamp):
|
||||||
# if not self.tick_period:
|
# if not self.tick_period:
|
||||||
# raise Exception("tick() should not be called without a tick_period: %s", self)
|
# raise Exception("tick() should not be called without a tick_period: %s", self)
|
||||||
#
|
#
|
||||||
# # not necessary anymore, we're now using udev monitor to watch for receiver status
|
# # not necessary anymore, we're now using udev monitor to watch for receiver status
|
||||||
# # if self._last_tick > 0 and timestamp - self._last_tick > _POLL_TICK * 2:
|
# # if self._last_tick > 0 and timestamp - self._last_tick > _POLL_TICK * 2:
|
||||||
# # # if we missed a couple of polls, most likely the computer went into
|
# # # if we missed a couple of polls, most likely the computer went into
|
||||||
# # # sleep, and we have to reinitialize the receiver again
|
# # # sleep, and we have to reinitialize the receiver again
|
||||||
# # _log.warn("%s: possible sleep detected, closing this listener", self.receiver)
|
# # _log.warn("%s: possible sleep detected, closing this listener", self.receiver)
|
||||||
# # self.stop()
|
# # self.stop()
|
||||||
# # return
|
# # return
|
||||||
#
|
#
|
||||||
# self._last_tick = timestamp
|
# self._last_tick = timestamp
|
||||||
#
|
#
|
||||||
# try:
|
# try:
|
||||||
# # read these in case they haven't been read already
|
# # read these in case they haven't been read already
|
||||||
# # self.receiver.serial, self.receiver.firmware
|
# # self.receiver.serial, self.receiver.firmware
|
||||||
# if self.receiver.status.lock_open:
|
# if self.receiver.status.lock_open:
|
||||||
# # don't mess with stuff while pairing
|
# # don't mess with stuff while pairing
|
||||||
# return
|
# return
|
||||||
#
|
#
|
||||||
# self.receiver.status.poll(timestamp)
|
# self.receiver.status.poll(timestamp)
|
||||||
#
|
#
|
||||||
# # Iterating directly through the reciver would unnecessarily probe
|
# # Iterating directly through the reciver would unnecessarily probe
|
||||||
# # all possible devices, even unpaired ones.
|
# # all possible devices, even unpaired ones.
|
||||||
# # Checking for each device number in turn makes sure only already
|
# # Checking for each device number in turn makes sure only already
|
||||||
# # known devices are polled.
|
# # known devices are polled.
|
||||||
# # This is okay because we should have already known about them all
|
# # This is okay because we should have already known about them all
|
||||||
# # long before the first poll() happents, through notifications.
|
# # long before the first poll() happents, through notifications.
|
||||||
# for number in range(1, 6):
|
# for number in range(1, 6):
|
||||||
# if number in self.receiver:
|
# if number in self.receiver:
|
||||||
# dev = self.receiver[number]
|
# dev = self.receiver[number]
|
||||||
# if dev and dev.status is not None:
|
# if dev and dev.status is not None:
|
||||||
# dev.status.poll(timestamp)
|
# dev.status.poll(timestamp)
|
||||||
# except Exception as e:
|
# except Exception as e:
|
||||||
# _log.exception("polling", e)
|
# _log.exception("polling", e)
|
||||||
|
|
||||||
def _status_changed(self, device, alert=_status.ALERT.NONE, reason=None):
|
def _status_changed(self, device, alert=_status.ALERT.NONE, reason=None):
|
||||||
assert device is not None
|
assert device is not None
|
||||||
|
@ -184,7 +184,7 @@ class ReceiverListener(_listener.EventsListener):
|
||||||
def _notifications_handler(self, n):
|
def _notifications_handler(self, n):
|
||||||
assert self.receiver
|
assert self.receiver
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("%s: handling %s", self.receiver, n)
|
# _log.debug("%s: handling %s", self.receiver, n)
|
||||||
if n.devnumber == 0xFF:
|
if n.devnumber == 0xFF:
|
||||||
# a receiver notification
|
# a receiver notification
|
||||||
_notifications.process(self.receiver, n)
|
_notifications.process(self.receiver, n)
|
||||||
|
@ -370,10 +370,11 @@ def _process_receiver_event(action, device_info):
|
||||||
# (It would be easier to use pylibacl but adding the pylibacl dependencies
|
# (It would be easier to use pylibacl but adding the pylibacl dependencies
|
||||||
# for this special case is not good.)
|
# for this special case is not good.)
|
||||||
try:
|
try:
|
||||||
import subprocess, re
|
import subprocess
|
||||||
|
import re
|
||||||
output = subprocess.check_output(
|
output = subprocess.check_output(
|
||||||
['/usr/bin/getfacl', '-p', device_info.path])
|
['/usr/bin/getfacl', '-p', device_info.path])
|
||||||
if not re.search(b'user:.+:', output):
|
if not re.search(b'user:.+:', output):
|
||||||
_error_callback('permissions', device_info.path)
|
_error_callback('permissions', device_info.path)
|
||||||
except:
|
except Exception:
|
||||||
_error_callback('permissions', device_info.path)
|
_error_callback('permissions', device_info.path)
|
||||||
|
|
|
@ -65,7 +65,7 @@ class TaskRunner(_Thread):
|
||||||
assert function
|
assert function
|
||||||
try:
|
try:
|
||||||
function(*args, **kwargs)
|
function(*args, **kwargs)
|
||||||
except:
|
except Exception:
|
||||||
_log.exception('calling %s', function)
|
_log.exception('calling %s', function)
|
||||||
|
|
||||||
if _log.isEnabledFor(_DEBUG):
|
if _log.isEnabledFor(_DEBUG):
|
||||||
|
|
|
@ -47,14 +47,12 @@ def _error_dialog(reason, object):
|
||||||
|
|
||||||
if reason == 'permissions':
|
if reason == 'permissions':
|
||||||
title = _('Permissions error')
|
title = _('Permissions error')
|
||||||
text = _('Found a Logitech Receiver (%s), but did not have permission to open it.') % object + \
|
text = (_('Found a Logitech Receiver (%s), but did not have permission to open it.') % object + '\n\n' +
|
||||||
'\n\n' + \
|
_("If you've just installed Solaar, try removing the receiver and plugging it back in."))
|
||||||
_("If you've just installed Solaar, try removing the receiver and plugging it back in.")
|
|
||||||
elif reason == 'unpair':
|
elif reason == 'unpair':
|
||||||
title = _('Unpairing failed')
|
title = _('Unpairing failed')
|
||||||
text = _('Failed to unpair %{device} from %{receiver}.').format(device=object.name, receiver=object.receiver.name) + \
|
text = (_('Failed to unpair %{device} from %{receiver}.').format(device=object.name, receiver=object.receiver.name) +
|
||||||
'\n\n' + \
|
'\n\n' + _('The receiver returned an error, with no further details.'))
|
||||||
_('The receiver returned an error, with no further details.')
|
|
||||||
else:
|
else:
|
||||||
raise Exception("ui.error_dialog: don't know how to handle (%s, %s)",
|
raise Exception("ui.error_dialog: don't know how to handle (%s, %s)",
|
||||||
reason, object)
|
reason, object)
|
||||||
|
|
|
@ -61,7 +61,7 @@ def _create():
|
||||||
# gtk3 < ~3.6.4 has incorrect gi bindings
|
# gtk3 < ~3.6.4 has incorrect gi bindings
|
||||||
import logging
|
import logging
|
||||||
logging.exception('failed to fully create the about dialog')
|
logging.exception('failed to fully create the about dialog')
|
||||||
except:
|
except Exception:
|
||||||
# the Gtk3 version may be too old, and the function does not exist
|
# the Gtk3 version may be too old, and the function does not exist
|
||||||
import logging
|
import logging
|
||||||
logging.exception('failed to fully create the about dialog')
|
logging.exception('failed to fully create the about dialog')
|
||||||
|
|
|
@ -60,11 +60,11 @@ def make_toggle(name, label, function, stock_id=None, *args):
|
||||||
#
|
#
|
||||||
|
|
||||||
# def _toggle_notifications(action):
|
# def _toggle_notifications(action):
|
||||||
# if action.get_active():
|
# if action.get_active():
|
||||||
# notify.init('Solaar')
|
# notify.init('Solaar')
|
||||||
# else:
|
# else:
|
||||||
# notify.uninit()
|
# notify.uninit()
|
||||||
# action.set_sensitive(notify.available)
|
# action.set_sensitive(notify.available)
|
||||||
# toggle_notifications = make_toggle('notifications', 'Notifications', _toggle_notifications)
|
# toggle_notifications = make_toggle('notifications', 'Notifications', _toggle_notifications)
|
||||||
|
|
||||||
about = make('help-about',
|
about = make('help-about',
|
||||||
|
@ -109,6 +109,6 @@ def unpair(window, device):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
del receiver[device_number]
|
del receiver[device_number]
|
||||||
except:
|
except Exception:
|
||||||
# _log.exception("unpairing %s", device)
|
# _log.exception("unpairing %s", device)
|
||||||
error_dialog('unpair', device)
|
error_dialog('unpair', device)
|
||||||
|
|
|
@ -75,7 +75,7 @@ def _write_async_key_value(setting, key, value, sbox):
|
||||||
def _create_toggle_control(setting):
|
def _create_toggle_control(setting):
|
||||||
def _switch_notify(switch, _ignore, s):
|
def _switch_notify(switch, _ignore, s):
|
||||||
if switch.get_sensitive():
|
if switch.get_sensitive():
|
||||||
_write_async(s, switch.get_active() == True, switch.get_parent())
|
_write_async(s, switch.get_active() is True, switch.get_parent())
|
||||||
|
|
||||||
c = Gtk.Switch()
|
c = Gtk.Switch()
|
||||||
c.connect('notify::active', _switch_notify, setting)
|
c.connect('notify::active', _switch_notify, setting)
|
||||||
|
@ -312,8 +312,8 @@ def update(device, is_online=None):
|
||||||
|
|
||||||
def clean(device):
|
def clean(device):
|
||||||
"""Remove the controls for a given device serial.
|
"""Remove the controls for a given device serial.
|
||||||
Needed after the device has been unpaired.
|
Needed after the device has been unpaired.
|
||||||
"""
|
"""
|
||||||
assert _box is not None
|
assert _box is not None
|
||||||
device_id = (device.receiver.path, device.number)
|
device_id = (device.receiver.path, device.number)
|
||||||
for k in list(_items.keys()):
|
for k in list(_items.keys()):
|
||||||
|
|
|
@ -219,7 +219,7 @@ def icon_file(name, size=_LARGE_SIZE):
|
||||||
if theme_icon:
|
if theme_icon:
|
||||||
file_name = theme_icon.get_filename()
|
file_name = theme_icon.get_filename()
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("icon %s(%d) => %s", name, size, file_name)
|
# _log.debug("icon %s(%d) => %s", name, size, file_name)
|
||||||
return file_name
|
return file_name
|
||||||
|
|
||||||
_log.warn('icon %s(%d) not found in current theme', name, size)
|
_log.warn('icon %s(%d) not found in current theme', name, size)
|
||||||
|
|
|
@ -60,7 +60,7 @@ if available:
|
||||||
_log.info('starting desktop notifications')
|
_log.info('starting desktop notifications')
|
||||||
try:
|
try:
|
||||||
return Notify.init(NAME)
|
return Notify.init(NAME)
|
||||||
except:
|
except Exception:
|
||||||
_log.exception('initializing desktop notifications')
|
_log.exception('initializing desktop notifications')
|
||||||
available = False
|
available = False
|
||||||
return available and Notify.is_initted()
|
return available and Notify.is_initted()
|
||||||
|
@ -73,12 +73,12 @@ if available:
|
||||||
Notify.uninit()
|
Notify.uninit()
|
||||||
|
|
||||||
# def toggle(action):
|
# def toggle(action):
|
||||||
# if action.get_active():
|
# if action.get_active():
|
||||||
# init()
|
# init()
|
||||||
# else:
|
# else:
|
||||||
# uninit()
|
# uninit()
|
||||||
# action.set_sensitive(available)
|
# action.set_sensitive(available)
|
||||||
# return action.get_active()
|
# return action.get_active()
|
||||||
|
|
||||||
def alert(reason, icon=None):
|
def alert(reason, icon=None):
|
||||||
assert reason
|
assert reason
|
||||||
|
@ -90,8 +90,7 @@ if available:
|
||||||
|
|
||||||
# we need to use the filename here because the notifications daemon
|
# we need to use the filename here because the notifications daemon
|
||||||
# is an external application that does not know about our icon sets
|
# is an external application that does not know about our icon sets
|
||||||
icon_file = _icons.icon_file(NAME.lower()) if icon is None \
|
icon_file = _icons.icon_file(NAME.lower()) if icon is None else _icons.icon_file(icon)
|
||||||
else _icons.icon_file(icon)
|
|
||||||
|
|
||||||
n.update(NAME, reason, icon_file)
|
n.update(NAME, reason, icon_file)
|
||||||
n.set_urgency(Notify.Urgency.NORMAL)
|
n.set_urgency(Notify.Urgency.NORMAL)
|
||||||
|
@ -99,7 +98,7 @@ if available:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("showing %s", n)
|
# _log.debug("showing %s", n)
|
||||||
n.show()
|
n.show()
|
||||||
except Exception:
|
except Exception:
|
||||||
_log.exception('showing %s', n)
|
_log.exception('showing %s', n)
|
||||||
|
@ -125,8 +124,7 @@ if available:
|
||||||
|
|
||||||
# we need to use the filename here because the notifications daemon
|
# we need to use the filename here because the notifications daemon
|
||||||
# is an external application that does not know about our icon sets
|
# is an external application that does not know about our icon sets
|
||||||
icon_file = _icons.device_icon_file(dev.name, dev.kind) if icon is None \
|
icon_file = _icons.device_icon_file(dev.name, dev.kind) if icon is None else _icons.icon_file(icon)
|
||||||
else _icons.icon_file(icon)
|
|
||||||
|
|
||||||
n.update(summary, message, icon_file)
|
n.update(summary, message, icon_file)
|
||||||
urgency = Notify.Urgency.LOW if dev.status else Notify.Urgency.NORMAL
|
urgency = Notify.Urgency.LOW if dev.status else Notify.Urgency.NORMAL
|
||||||
|
@ -135,7 +133,7 @@ if available:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("showing %s", n)
|
# _log.debug("showing %s", n)
|
||||||
n.show()
|
n.show()
|
||||||
except Exception:
|
except Exception:
|
||||||
_log.exception('showing %s', n)
|
_log.exception('showing %s', n)
|
||||||
|
|
|
@ -184,14 +184,9 @@ def _pairing_succeeded(assistant, receiver, device):
|
||||||
|
|
||||||
def _check_encrypted(dev):
|
def _check_encrypted(dev):
|
||||||
if assistant.is_drawable():
|
if assistant.is_drawable():
|
||||||
if device.status.get(_K.LINK_ENCRYPTED) == False:
|
if device.status.get(_K.LINK_ENCRYPTED) is False:
|
||||||
hbox.pack_start(
|
hbox.pack_start(Gtk.Image.new_from_icon_name('security-low', Gtk.IconSize.MENU), False, False, 0)
|
||||||
Gtk.Image.new_from_icon_name('security-low',
|
hbox.pack_start(Gtk.Label(_('The wireless link is not encrypted') + '!'), False, False, 0)
|
||||||
Gtk.IconSize.MENU), False,
|
|
||||||
False, 0)
|
|
||||||
hbox.pack_start(
|
|
||||||
Gtk.Label(_('The wireless link is not encrypted') + '!'),
|
|
||||||
False, False, 0)
|
|
||||||
hbox.show_all()
|
hbox.show_all()
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -42,7 +42,7 @@ del getLogger
|
||||||
# constants
|
# constants
|
||||||
#
|
#
|
||||||
|
|
||||||
_TRAY_ICON_SIZE = 32 # pixels
|
_TRAY_ICON_SIZE = 32 # pixels
|
||||||
_MENU_ICON_SIZE = Gtk.IconSize.LARGE_TOOLBAR
|
_MENU_ICON_SIZE = Gtk.IconSize.LARGE_TOOLBAR
|
||||||
_RECEIVER_SEPARATOR = ('~', None, None, None)
|
_RECEIVER_SEPARATOR = ('~', None, None, None)
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ def _scroll(tray_icon, event, direction=None):
|
||||||
_last_scroll = now
|
_last_scroll = now
|
||||||
|
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("scroll direction %s", direction)
|
# _log.debug("scroll direction %s", direction)
|
||||||
|
|
||||||
global _picked_device
|
global _picked_device
|
||||||
candidate = None
|
candidate = None
|
||||||
|
|
|
@ -415,7 +415,7 @@ def _device_selected(selection):
|
||||||
model, item = selection.get_selected()
|
model, item = selection.get_selected()
|
||||||
device = model.get_value(item, _COLUMN.DEVICE) if item else None
|
device = model.get_value(item, _COLUMN.DEVICE) if item else None
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("window tree selected device %s", device)
|
# _log.debug("window tree selected device %s", device)
|
||||||
if device:
|
if device:
|
||||||
_update_info_panel(device, full=True)
|
_update_info_panel(device, full=True)
|
||||||
else:
|
else:
|
||||||
|
@ -666,8 +666,8 @@ def _update_receiver_panel(receiver, panel, buttons, full=False):
|
||||||
# b._insecure.set_visible(False)
|
# b._insecure.set_visible(False)
|
||||||
buttons._unpair.set_visible(False)
|
buttons._unpair.set_visible(False)
|
||||||
|
|
||||||
if ( receiver.may_unpair or receiver.re_pairs ) and not is_pairing and \
|
if (receiver.may_unpair or receiver.re_pairs) and not is_pairing and \
|
||||||
( receiver.remaining_pairings() is None or receiver.remaining_pairings() != 0 ):
|
(receiver.remaining_pairings() is None or receiver.remaining_pairings() != 0):
|
||||||
if not receiver.re_pairs and devices_count >= receiver.max_devices:
|
if not receiver.re_pairs and devices_count >= receiver.max_devices:
|
||||||
paired_devices = tuple(n
|
paired_devices = tuple(n
|
||||||
for n in range(1, receiver.max_devices + 1)
|
for n in range(1, receiver.max_devices + 1)
|
||||||
|
@ -727,7 +727,7 @@ def _update_device_panel(device, panel, buttons, full=False):
|
||||||
panel._battery._text.set_markup(text)
|
panel._battery._text.set_markup(text)
|
||||||
|
|
||||||
if is_online:
|
if is_online:
|
||||||
not_secure = device.status.get(_K.LINK_ENCRYPTED) == False
|
not_secure = device.status.get(_K.LINK_ENCRYPTED) is False
|
||||||
if not_secure:
|
if not_secure:
|
||||||
panel._secure._text.set_text(_('not encrypted'))
|
panel._secure._text.set_text(_('not encrypted'))
|
||||||
panel._secure._icon.set_from_icon_name('security-low',
|
panel._secure._icon.set_from_icon_name('security-low',
|
||||||
|
|
|
@ -55,7 +55,7 @@ def _suspend_or_resume(suspend):
|
||||||
|
|
||||||
def watch(on_resume_callback=None, on_suspend_callback=None):
|
def watch(on_resume_callback=None, on_suspend_callback=None):
|
||||||
"""Register callback for suspend/resume events.
|
"""Register callback for suspend/resume events.
|
||||||
They are called only if the system DBus is running, and the UPower daemon is available."""
|
They are called only if the system DBus is running, and the UPower daemon is available."""
|
||||||
global _resume_callback, _suspend_callback
|
global _resume_callback, _suspend_callback
|
||||||
_suspend_callback = on_suspend_callback
|
_suspend_callback = on_suspend_callback
|
||||||
_resume_callback = on_resume_callback
|
_resume_callback = on_resume_callback
|
||||||
|
@ -95,7 +95,7 @@ try:
|
||||||
_log.info(
|
_log.info(
|
||||||
'connected to system dbus, watching for suspend/resume events')
|
'connected to system dbus, watching for suspend/resume events')
|
||||||
|
|
||||||
except:
|
except Exception:
|
||||||
# Either:
|
# Either:
|
||||||
# - the dbus library is not available
|
# - the dbus library is not available
|
||||||
# - the system dbus is not running
|
# - the system dbus is not running
|
||||||
|
|
Loading…
Reference in New Issue