diff --git a/app/watcher.py b/app/watcher.py index 4faee6cc..2a23d413 100644 --- a/app/watcher.py +++ b/app/watcher.py @@ -195,7 +195,7 @@ class Watcher(Thread): updated = True self._device_status_changed(devstatus, C.STATUS.UNAVAILABLE) elif code == 0x11: - status = devices.process_event(devstatus, self.listener, data) + status = devices.process_event(devstatus, data) updated |= self._device_status_changed(devstatus, status) else: _l.warn("unknown event code %02x", code) diff --git a/lib/cli/__init__.py b/lib/cli/__init__.py deleted file mode 100644 index cfe18a79..00000000 --- a/lib/cli/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# pass diff --git a/lib/hidapi/__init__.py b/lib/hidapi/__init__.py index e0a2b217..92f1b29f 100644 --- a/lib/hidapi/__init__.py +++ b/lib/hidapi/__init__.py @@ -5,7 +5,7 @@ __license__ = "GPL" __version__ = "0.3" # -# In case a future pure-Python implementation is feasible. +# This package exists in case a future pure-Python implementation is feasible. # from .native import * diff --git a/lib/cli/hidconsole.py b/lib/hidapi/hidconsole.py similarity index 99% rename from lib/cli/hidconsole.py rename to lib/hidapi/hidconsole.py index 86ea255e..46aaaf9f 100644 --- a/lib/cli/hidconsole.py +++ b/lib/hidapi/hidconsole.py @@ -50,7 +50,7 @@ if __name__ == '__main__': help='linux device to connect to') args = arg_parser.parse_args() - import hidapi + from . import hidapi print (".. Opening device %s" % args.device) handle = hidapi.open_path(args.device.encode('utf-8')) if handle: diff --git a/lib/logitech/devices/__init__.py b/lib/logitech/devices/__init__.py index 7a3783c0..09f72357 100644 --- a/lib/logitech/devices/__init__.py +++ b/lib/logitech/devices/__init__.py @@ -17,13 +17,13 @@ def ping(devinfo, listener): def default_request_status(devinfo, listener): if _api.C.FEATURE.BATTERY in devinfo.features: - reply = listener.request(_api.get_device_battery_level, devinfo.number, features_array=devinfo.features) + reply = listener.request(_api.get_device_battery_level, devinfo.number, features=devinfo.features) if reply: discharge, dischargeNext, status = reply return C.STATUS.CONNECTED, {C.PROPS.BATTERY_LEVEL: discharge} -def default_process_event(devinfo, listener, data): +def default_process_event(devinfo, data): feature_index = ord(data[0:1]) feature = devinfo.features[feature_index] feature_function = ord(data[1:2]) & 0xF0 @@ -53,6 +53,12 @@ _REQUEST_STATUS_FUNCTIONS = { } def request_status(devinfo, listener): + """Trigger a status request for a device. + + :param devinfo: the device info tuple. + :param listener: the EventsListener that will be used to send the request, + and which will receive the status events from the device. + """ if listener: if devinfo.name in _REQUEST_STATUS_FUNCTIONS: return _REQUEST_STATUS_FUNCTIONS[devinfo.name](devinfo, listener) @@ -63,11 +69,18 @@ _PROCESS_EVENT_FUNCTIONS = { k750.NAME: k750.process_event } -def process_event(devinfo, listener, data): - if listener: - default_result = default_process_event(devinfo, listener, data) - if default_result is not None: - return default_result +def process_event(devinfo, data): + """Process an event received for a device. - if devinfo.name in _PROCESS_EVENT_FUNCTIONS: - return _PROCESS_EVENT_FUNCTIONS[devinfo.name](devinfo, listener, data) + When using an EventsListener, it is assumed this event was received through + its callback, where you may call LUR APIs directly. + + :param devinfo: the device info tuple. + :param data: the event data (event packet sans the first two bytes: reply code and device number) + """ + default_result = default_process_event(devinfo, data) + if default_result is not None: + return default_result + + if devinfo.name in _PROCESS_EVENT_FUNCTIONS: + return _PROCESS_EVENT_FUNCTIONS[devinfo.name](devinfo, data) diff --git a/lib/logitech/devices/k750.py b/lib/logitech/devices/k750.py index a6c35b55..eb6bc934 100644 --- a/lib/logitech/devices/k750.py +++ b/lib/logitech/devices/k750.py @@ -21,7 +21,7 @@ NAME = 'Wireless Solar Keyboard K750' def _trigger_solar_charge_events(handle, devinfo): return _api.request(handle, devinfo.number, feature=_api.C.FEATURE.SOLAR_CHARGE, function=b'\x03', params=b'\x78\x01', - features_array=devinfo.features) + features=devinfo.features) def _charge_status(data, hasLux=False): @@ -54,7 +54,7 @@ def request_status(devinfo, listener): return C.STATUS.UNAVAILABLE -def process_event(devinfo, listener, data): +def process_event(devinfo, data): if data[:2] == b'\x09\x00' and data[7:11] == b'GOOD': # usually sent after the keyboard is turned on return _charge_status(data) @@ -65,7 +65,7 @@ def process_event(devinfo, listener, data): if data[:2] == b'\x09\x20' and data[7:11] == b'GOOD': logging.debug("Solar key pressed") - if listener and _trigger_solar_charge_events(listener.receiver, devinfo) is None: + if _trigger_solar_charge_events(devinfo.handle, devinfo) is None: return C.STATUS.UNAVAILABLE return _charge_status(data) diff --git a/lib/logitech/unifying_receiver/api.py b/lib/logitech/unifying_receiver/api.py index f4eaa55d..b16c0873 100644 --- a/lib/logitech/unifying_receiver/api.py +++ b/lib/logitech/unifying_receiver/api.py @@ -33,7 +33,7 @@ open = _base.open close = _base.close -def request(handle, devnumber, feature, function=b'\x00', params=b'', features_array=None): +def request(handle, devnumber, feature, function=b'\x00', params=b'', features=None): """Makes a feature call to the device, and returns the reply data. Basically a write() followed by (possibly multiple) reads, until a reply @@ -43,7 +43,13 @@ def request(handle, devnumber, feature, function=b'\x00', params=b'', features_a Incoming data packets not matching the feature and function will be delivered to the unhandled hook (if any), and ignored. - The optional ``features_array`` parameter is a cached result of the + :param function: the function to call on that feature, may be an byte value + or a bytes string of length 1. + :param params: optional bytes string to send as function parameters to the + feature; may also be an integer if the function only takes a single byte as + parameter. + + The optional ``features`` parameter is a cached result of the get_device_features function for this device, necessary to find the feature index. If the ``features_arrary`` is not provided, one will be obtained by manually calling get_device_features before making the request call proper. @@ -55,18 +61,23 @@ def request(handle, devnumber, feature, function=b'\x00', params=b'', features_a if feature == C.FEATURE.ROOT: feature_index = b'\x00' else: - if features_array is None: - features_array = get_device_features(handle, devnumber) - if features_array is None: + if features is None: + features = get_device_features(handle, devnumber) + if features is None: _l.log(_LOG_LEVEL, "(%d) no features array available", devnumber) return None - if feature in features_array: - feature_index = _pack('!B', features_array.index(feature)) + if feature in features: + feature_index = _pack('!B', features.index(feature)) if feature_index is None: _l.warn("(%d) feature <%s:%s> not supported", devnumber, _hexlify(feature), C.FEATURE_NAME[feature]) raise E.FeatureNotSupported(devnumber, feature) + if type(function) == int: + function = _pack('!B', function) + if type(params) == int: + params = _pack('!B', params) + return _base.request(handle, devnumber, feature_index + function, params) @@ -86,19 +97,19 @@ def get_device_protocol(handle, devnumber): return 'HID %d.%d' % (ord(reply[0:1]), ord(reply[1:2])) -def find_device_by_name(handle, device_name): +def find_device_by_name(handle, name): """Searches for an attached device by name. :returns: an AttachedDeviceInfo tuple, or ``None``. """ - _l.log(_LOG_LEVEL, "searching for device '%s'", device_name) + _l.log(_LOG_LEVEL, "searching for device '%s'", name) for devnumber in range(1, 1 + C.MAX_ATTACHED_DEVICES): - features_array = get_device_features(handle, devnumber) - if features_array: - d_name = get_device_name(handle, devnumber, features_array) - if d_name == device_name: - return get_device_info(handle, devnumber, device_name=d_name, features_array=features_array) + features = get_device_features(handle, devnumber) + if features: + d_name = get_device_name(handle, devnumber, features) + if d_name == name: + return get_device_info(handle, devnumber, name=d_name, features=features) def list_devices(handle): @@ -111,27 +122,27 @@ def list_devices(handle): devices = [] for device in range(1, 1 + C.MAX_ATTACHED_DEVICES): - features_array = get_device_features(handle, device) - if features_array: - devices.append(get_device_info(handle, device, features_array=features_array)) + features = get_device_features(handle, device) + if features: + devices.append(get_device_info(handle, device, features=features)) return devices -def get_device_info(handle, devnumber, device_name=None, features_array=None): - """Gets the complete info for a device (type, name, firmwares, and features_array). +def get_device_info(handle, devnumber, name=None, features=None): + """Gets the complete info for a device (type, name, firmware versions, features). :returns: an AttachedDeviceInfo tuple, or ``None``. """ - if features_array is None: - features_array = get_device_features(handle, devnumber) - if features_array is None: + if features is None: + features = get_device_features(handle, devnumber) + if features is None: return None - d_type = get_device_type(handle, devnumber, features_array) - d_name = get_device_name(handle, devnumber, features_array) if device_name is None else device_name - d_firmware = get_device_firmware(handle, devnumber, features_array) - devinfo = AttachedDeviceInfo(handle, devnumber, d_type, d_name, d_firmware, features_array) + d_type = get_device_type(handle, devnumber, features) + d_name = get_device_name(handle, devnumber, features) if name is None else name + d_firmware = get_device_firmware(handle, devnumber, features) + devinfo = AttachedDeviceInfo(handle, devnumber, d_type, d_name, d_firmware, features) _l.log(_LOG_LEVEL, "(%d) found device %s", devnumber, devinfo) return devinfo @@ -222,7 +233,7 @@ def get_device_features(handle, devnumber): return features -def get_device_firmware(handle, devnumber, features_array=None): +def get_device_firmware(handle, devnumber, features=None): """Reads a device's firmware info. :returns: a list of FirmwareInfo tuples, ordered by firmware layer. @@ -230,14 +241,13 @@ def get_device_firmware(handle, devnumber, features_array=None): def _makeFirmwareInfo(level, type, name=None, version=None, build=None, extras=None): return FirmwareInfo(level, type, name, version, build, extras) - fw_count = request(handle, devnumber, C.FEATURE.FIRMWARE, features_array=features_array) + fw_count = request(handle, devnumber, C.FEATURE.FIRMWARE, features=features) if fw_count: fw_count = ord(fw_count[:1]) fw = [] for index in range(0, fw_count): - index = _pack('!B', index) - fw_info = request(handle, devnumber, C.FEATURE.FIRMWARE, function=b'\x10', params=index, features_array=features_array) + fw_info = request(handle, devnumber, C.FEATURE.FIRMWARE, function=b'\x10', params=index, features=features) if fw_info: fw_level = ord(fw_info[:1]) & 0x0F if fw_level == 0 or fw_level == 1: @@ -262,34 +272,33 @@ def get_device_firmware(handle, devnumber, features_array=None): return fw -def get_device_type(handle, devnumber, features_array=None): +def get_device_type(handle, devnumber, features=None): """Reads a device's type. :see DEVICE_TYPE: :returns: a string describing the device type, or ``None`` if the device is not available or does not support the ``NAME`` feature. """ - d_type = request(handle, devnumber, C.FEATURE.NAME, function=b'\x20', features_array=features_array) + d_type = request(handle, devnumber, C.FEATURE.NAME, function=b'\x20', features=features) if d_type: d_type = ord(d_type[:1]) _l.log(_LOG_LEVEL, "(%d) device type %d = %s", devnumber, d_type, C.DEVICE_TYPE[d_type]) return C.DEVICE_TYPE[d_type] -def get_device_name(handle, devnumber, features_array=None): +def get_device_name(handle, devnumber, features=None): """Reads a device's name. :returns: a string with the device name, or ``None`` if the device is not available or does not support the ``NAME`` feature. """ - name_length = request(handle, devnumber, C.FEATURE.NAME, features_array=features_array) + name_length = request(handle, devnumber, C.FEATURE.NAME, features=features) if name_length: name_length = ord(name_length[:1]) d_name = b'' while len(d_name) < name_length: - name_index = _pack('!B', len(d_name)) - name_fragment = request(handle, devnumber, C.FEATURE.NAME, function=b'\x10', params=name_index, features_array=features_array) + name_fragment = request(handle, devnumber, C.FEATURE.NAME, function=b'\x10', params=len(d_name), features=features) if name_fragment: name_fragment = name_fragment[:name_length - len(d_name)] d_name += name_fragment @@ -301,12 +310,12 @@ def get_device_name(handle, devnumber, features_array=None): return d_name -def get_device_battery_level(handle, devnumber, features_array=None): +def get_device_battery_level(handle, devnumber, features=None): """Reads a device's battery level. :raises FeatureNotSupported: if the device does not support this feature. """ - battery = request(handle, devnumber, C.FEATURE.BATTERY, features_array=features_array) + battery = request(handle, devnumber, C.FEATURE.BATTERY, features=features) if battery: discharge, dischargeNext, status = _unpack('!BBB', battery[:3]) _l.log(_LOG_LEVEL, "(%d) battery %d%% charged, next level %d%% charge, status %d = %s", @@ -314,15 +323,14 @@ def get_device_battery_level(handle, devnumber, features_array=None): return (discharge, dischargeNext, C.BATTERY_STATUS[status]) -def get_device_keys(handle, devnumber, features_array=None): - count = request(handle, devnumber, C.FEATURE.REPROGRAMMABLE_KEYS, features_array=features_array) +def get_device_keys(handle, devnumber, features=None): + count = request(handle, devnumber, C.FEATURE.REPROGRAMMABLE_KEYS, features=features) if count: keys = [] count = ord(count[:1]) for index in range(0, count): - keyindex = _pack('!B', index) - keydata = request(handle, devnumber, C.FEATURE.REPROGRAMMABLE_KEYS, function=b'\x10', params=keyindex, features_array=features_array) + keydata = request(handle, devnumber, C.FEATURE.REPROGRAMMABLE_KEYS, function=b'\x10', params=index, features=features) if keydata: key, key_task, flags = _unpack('!HHB', keydata[:5]) keys.append(ReprogrammableKeyInfo(index, key, C.KEY_NAME[key], key_task, C.KEY_NAME[key_task], flags)) diff --git a/lib/logitech/unifying_receiver/base.py b/lib/logitech/unifying_receiver/base.py index e29c135a..8e777078 100644 --- a/lib/logitech/unifying_receiver/base.py +++ b/lib/logitech/unifying_receiver/base.py @@ -216,7 +216,7 @@ def read(handle, timeout=DEFAULT_TIMEOUT): # _l.log(_LOG_LEVEL, "(-) => r[]", handle) -def request(handle, devnumber, feature_index_function, params=b'', features_array=None): +def request(handle, devnumber, feature_index_function, params=b'', features=None): """Makes a feature call to a device and waits for a matching reply. This function will skip all incoming messages and events not related to the @@ -227,8 +227,8 @@ def request(handle, devnumber, feature_index_function, params=b'', features_arra :param devnumber: attached device number. :param feature_index_function: a two-byte string of (feature_index, feature_function). :param params: parameters for the feature call, 3 to 16 bytes. - :param features_array: optional features array for the device, only used to - fill the FeatureCallError exception if one occurs. + :param features: optional features array for the device, only used to fill + the FeatureCallError exception if one occurs. :returns: the reply data packet, or ``None`` if the device is no longer available. :raisees FeatureCallError: if the feature call replied with an error. @@ -272,12 +272,12 @@ def request(handle, devnumber, feature_index_function, params=b'', features_arra return None if reply_code == 0x11 and reply_data[0] == b'\xFF' and reply_data[1:3] == feature_index_function: - # an error returned from the device + # the feature call returned with an error error_code = ord(reply_data[3]) _l.warn("(%d) request feature call error %d = %s: %s", devnumber, error_code, C.ERROR_NAME[error_code], _hexlify(reply_data)) feature_index = ord(feature_index_function[:1]) feature_function = feature_index_function[1:2] - feature = None if features_array is None else features_array[feature_index] + feature = None if features is None else features[feature_index] if feature_index < len(features) else None raise E.FeatureCallError(devnumber, feature, feature_index, feature_function, error_code, reply_data) if reply_code == 0x11 and reply_data[:2] == feature_index_function: @@ -285,6 +285,11 @@ def request(handle, devnumber, feature_index_function, params=b'', features_arra # _l.log(_LOG_LEVEL, "(%d) matched reply with feature-index-function [%s]", devnumber, _hexlify(reply_data[2:])) return reply_data[2:] + if reply_code == 0x10 and devnumber == 0xFF and reply_data[:2] == feature_index_function: + # direct calls to the receiver (device 0xFF) may also return successfully with reply code 0x10 + # _l.log(_LOG_LEVEL, "(%d) matched reply with feature-index-function [%s]", devnumber, _hexlify(reply_data[2:])) + return reply_data[2:] + _l.log(_LOG_LEVEL, "(%d) unmatched reply {%s} (expected {%s})", devnumber, _hexlify(reply_data[:2]), _hexlify(feature_index_function)) if unhandled_hook: unhandled_hook(reply_code, reply_devnumber, reply_data) diff --git a/lib/logitech/unifying_receiver/tests/test_50_api.py b/lib/logitech/unifying_receiver/tests/test_50_api.py index 24eb61fc..390606ab 100644 --- a/lib/logitech/unifying_receiver/tests/test_50_api.py +++ b/lib/logitech/unifying_receiver/tests/test_50_api.py @@ -15,7 +15,7 @@ class Test_UR_API(unittest.TestCase): def setUpClass(cls): cls.handle = None cls.device = None - cls.features_array = None + cls.features = None cls.device_info = None @classmethod @@ -23,25 +23,30 @@ class Test_UR_API(unittest.TestCase): if cls.handle: api.close(cls.handle) cls.device = None - cls.features_array = None + cls.features = None cls.device_info = None + def _check(self, check_device=True, check_features=False): + if self.handle is None: + self.fail("No receiver found") + if check_device and self.device is None: + self.fail("Found no devices attached.") + if check_device and check_features and self.features is None: + self.fail("no feature set available") + def test_00_open_receiver(self): Test_UR_API.handle = api.open() - if self.handle is None: - self.fail("No receiver found") + self._check(check_device=False) def test_05_ping_device_zero(self): - if self.handle is None: - self.fail("No receiver found") + self._check(check_device=False) ok = api.ping(self.handle, 0) self.assertIsNotNone(ok, "invalid ping reply") self.assertFalse(ok, "device zero replied") def test_10_ping_all_devices(self): - if self.handle is None: - self.fail("No receiver found") + self._check(check_device=False) devices = [] @@ -55,106 +60,71 @@ class Test_UR_API(unittest.TestCase): Test_UR_API.device = devices[0] def test_30_get_feature_index(self): - if self.handle is None: - self.fail("No receiver found") - if self.device is None: - self.fail("Found no devices attached.") + self._check() fs_index = api.get_feature_index(self.handle, self.device, FEATURE.FEATURE_SET) self.assertIsNotNone(fs_index, "feature FEATURE_SET not available") self.assertGreater(fs_index, 0, "invalid FEATURE_SET index: " + str(fs_index)) def test_31_bad_feature(self): - if self.handle is None: - self.fail("No receiver found") - if self.device is None: - self.fail("Found no devices attached.") + self._check() reply = api.request(self.handle, self.device, FEATURE.ROOT, params=b'\xFF\xFF') self.assertIsNotNone(reply, "invalid reply") self.assertEqual(reply[:5], b'\x00' * 5, "invalid reply") def test_40_get_device_features(self): - if self.handle is None: - self.fail("No receiver found") - if self.device is None: - self.fail("Found no devices attached.") + self._check() features = api.get_device_features(self.handle, self.device) self.assertIsNotNone(features, "failed to read features array") self.assertIn(FEATURE.FEATURE_SET, features, "feature FEATURE_SET not available") # cache this to simplify next tests - Test_UR_API.features_array = features + Test_UR_API.features = features def test_50_get_device_firmware(self): - if self.handle is None: - self.fail("No receiver found") - if self.device is None: - self.fail("Found no devices attached.") - if self.features_array is None: - self.fail("no feature set available") + self._check(check_features=True) - d_firmware = api.get_device_firmware(self.handle, self.device, self.features_array) + d_firmware = api.get_device_firmware(self.handle, self.device, self.features) self.assertIsNotNone(d_firmware, "failed to get device firmware") self.assertGreater(len(d_firmware), 0, "device reported no firmware") for fw in d_firmware: self.assertIsInstance(fw, FirmwareInfo) def test_52_get_device_type(self): - if self.handle is None: - self.fail("No receiver found") - if self.device is None: - self.fail("Found no devices attached.") - if self.features_array is None: - self.fail("no feature set available") + self._check(check_features=True) - d_type = api.get_device_type(self.handle, self.device, self.features_array) + d_type = api.get_device_type(self.handle, self.device, self.features) self.assertIsNotNone(d_type, "failed to get device type") self.assertGreater(len(d_type), 0, "empty device type") def test_55_get_device_name(self): - if self.handle is None: - self.fail("No receiver found") - if self.device is None: - self.fail("Found no devices attached.") - if self.features_array is None: - self.fail("no feature set available") + self._check(check_features=True) - d_name = api.get_device_name(self.handle, self.device, self.features_array) + d_name = api.get_device_name(self.handle, self.device, self.features) self.assertIsNotNone(d_name, "failed to read device name") self.assertGreater(len(d_name), 0, "empty device name") def test_59_get_device_info(self): - if self.handle is None: - self.fail("No receiver found") - if self.device is None: - self.fail("Found no devices attached.") - if self.features_array is None: - self.fail("no feature set available") + self._check(check_features=True) - device_info = api.get_device_info(self.handle, self.device, features_array=self.features_array) + device_info = api.get_device_info(self.handle, self.device, features=self.features) self.assertIsNotNone(device_info, "failed to read full device info") self.assertIsInstance(device_info, AttachedDeviceInfo) Test_UR_API.device_info = device_info def test_60_get_battery_level(self): - if self.handle is None: - self.fail("No receiver found") - if self.device is None: - self.fail("Found no devices attached.") - if self.features_array is None: - self.fail("no feature set available") + self._check(check_features=True) - if FEATURE.BATTERY in self.features_array: - battery = api.get_device_battery_level(self.handle, self.device, self.features_array) + if FEATURE.BATTERY in self.features: + battery = api.get_device_battery_level(self.handle, self.device, self.features) self.assertIsNotNone(battery, "failed to read battery level") self.assertIsInstance(battery, tuple, "result not a tuple") else: warnings.warn("BATTERY feature not supported by device %d" % self.device) def test_70_list_devices(self): - if self.handle is None: - self.fail("No receiver found") + self._check(check_device=False) all_devices = api.list_devices(self.handle) if all_devices: @@ -165,10 +135,7 @@ class Test_UR_API(unittest.TestCase): self.assertIsNone(self.device) def test_70_find_device_by_name(self): - if self.handle is None: - self.fail("No receiver found") - if self.device is None: - self.fail("Found no devices attached.") + self._check() all_devices = api.list_devices(self.handle) for device_info in all_devices: diff --git a/lib/cli/ur_scanner.py b/lib/logitech/ur_scanner.py similarity index 63% rename from lib/cli/ur_scanner.py rename to lib/logitech/ur_scanner.py index 100e4b16..49f79f0c 100644 --- a/lib/cli/ur_scanner.py +++ b/lib/logitech/ur_scanner.py @@ -6,11 +6,40 @@ logging.basicConfig(level=logging.DEBUG) from binascii import hexlify -from logitech.unifying_receiver import api -from logitech.unifying_receiver.constants import * +from .unifying_receiver import (api, base) +from .unifying_receiver.constants import * + + +def print_receiver(receiver): + print ("Unifying Receiver") + + reply = base.request(receiver, 0xff, b'\x83\xB5', b'\x03') + if reply and reply[0:1] == b'\x03': + print (" Serial: %s" % hexlify(reply[1:5])) + + reply = base.request(receiver, 0xff, b'\x81\xF1', b'\x01') + if reply and reply[0:1] == b'\x01': + fw_version = hexlify(reply[1:3]) + firmware = fw_version[0:2] + '.' + fw_version[2:4] + else: + firmware = '??.??' + + reply = base.request(receiver, 0xff, b'\x81\xF1', b'\x02') + if reply and reply[0:1] == b'\x02': + firmware += '.B' + hexlify(reply[1:3]) + print (" Firmware version: %s" % firmware) + + reply = base.request(receiver, 0xff, b'\x81\xF1', b'\x04') + if reply and reply[0:1] == b'\x04': + bl_version = hexlify(reply[1:3]) + print (" Bootloader: %s.%s" % (bl_version[0:2], bl_version[2:4])) + + print ("--------") def scan_devices(receiver): + print_receiver(receiver) + devices = api.list_devices(receiver) if not devices: print ("!! No attached devices found.") @@ -21,7 +50,7 @@ def scan_devices(receiver): # print " Protocol %s" % devinfo.protocol for fw in devinfo.firmware: - print (" %s firmware: %s version %s build %d" % (fw.type, fw.name, fw.version, fw.build)) + print (" %s firmware: %s version %s build %d" % (fw.type, fw.name, fw.version, fw.build)) for index in range(0, len(devinfo.features)): feature = devinfo.features[index] @@ -29,11 +58,11 @@ def scan_devices(receiver): print (" ~ Feature %s (%s) at index %d" % (FEATURE_NAME[feature], hexlify(feature), index)) if FEATURE.BATTERY in devinfo.features: - discharge, dischargeNext, status = api.get_device_battery_level(receiver, devinfo.number, features_array=devinfo.features) + discharge, dischargeNext, status = api.get_device_battery_level(receiver, devinfo.number, features=devinfo.features) print (" Battery %d charged (next level %d%), status %s" % (discharge, dischargeNext, status)) if FEATURE.REPROGRAMMABLE_KEYS in devinfo.features: - keys = api.get_device_keys(receiver, devinfo.number, features_array=devinfo.features) + keys = api.get_device_keys(receiver, devinfo.number, features=devinfo.features) if keys is not None and keys: print (" %d reprogrammable keys found" % len(keys)) for k in keys: diff --git a/tools/hidconsole b/tools/hidconsole index c1d9f4fa..0100c11b 100755 --- a/tools/hidconsole +++ b/tools/hidconsole @@ -1,10 +1,10 @@ #!/bin/sh -cd -P `dirname "$0"`/.. +cd -P `dirname "$0"`/.. >/dev/null 2>&1 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/lib/native/`uname -m` export PYTHONPATH=$PWD/lib -cd - +cd - >/dev/null 2>&1 -exec python -OO -u -m cli.hidconsole "$@" +exec python -OO -u -m hidapi.hidconsole "$@" diff --git a/tools/ur_scanner b/tools/ur_scanner index 9f32102e..fad07a21 100755 --- a/tools/ur_scanner +++ b/tools/ur_scanner @@ -1,10 +1,10 @@ #!/bin/sh -cd -P `dirname "$0"`/.. +cd -P `dirname "$0"`/.. >/dev/null 2>&1 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/lib/native/`uname -m` export PYTHONPATH=$PWD/lib -cd - +cd - >/dev/null 2>&1 -exec python -OO -m cli.ur_scanner "$@" +exec python -OO -m logitech.ur_scanner "$@"