diff --git a/app/solaar.py b/app/solaar.py index e84ed458..d11718dd 100644 --- a/app/solaar.py +++ b/app/solaar.py @@ -21,23 +21,18 @@ def _require(module, os_package): def _parse_arguments(): import argparse arg_parser = argparse.ArgumentParser(prog=NAME.lower()) - arg_parser.add_argument('-S', '--no-systray', - action='store_false', dest='systray', + arg_parser.add_argument('-S', '--no-systray', action='store_false', dest='systray', help='don\'t embed the application window into the systray') - arg_parser.add_argument('-N', '--no-notifications', - action='store_false', dest='notifications', + arg_parser.add_argument('-N', '--no-notifications', action='store_false', dest='notifications', help='disable desktop notifications (shown only when in systray)') - arg_parser.add_argument('-v', '--verbose', - action='count', default=0, - help='increase the logger verbosity (may be repeated)') - arg_parser.add_argument('-V', '--version', - action='version', - version='%(prog)s ' + __version__) + arg_parser.add_argument('-d', '--debug', action='count', default=0, + help='print logging messages, for debugging purposes (may be repeated for extra verbosity)') + arg_parser.add_argument('-V', '--version', action='version', version='%(prog)s ' + __version__) args = arg_parser.parse_args() import logging - if args.verbose > 0: - log_level = logging.WARNING - 10 * args.verbose + if args.debug > 0: + log_level = logging.WARNING - 10 * args.debug log_format='%(asctime)s %(levelname)8s [%(threadName)s] %(name)s: %(message)s' logging.basicConfig(level=max(log_level, logging.DEBUG), format=log_format) else: diff --git a/app/solaar_cli.py b/app/solaar_cli.py index 6839d546..48c9a80f 100644 --- a/app/solaar_cli.py +++ b/app/solaar_cli.py @@ -23,6 +23,9 @@ def _require(module, os_package): except ImportError: _fail("missing required package '%s'" % os_package) +# +# +# def _receiver(): from logitech.unifying_receiver import Receiver @@ -49,14 +52,15 @@ def _find_device(receiver, name): return dev if len(name) < 3: - _fail("need at least 3 characters to match the device") + _fail("need at least 3 characters to match a device") - if name in 'receiver': + name = name.lower() + if 'receiver'.startswith(name) or name == receiver.serial: return receiver dev = None for d in receiver: - if name in d.name.lower() or name in d.codename.lower(): + if name == d.serial or name in d.name.lower() or name in d.codename.lower(): if dev is None: dev = d else: @@ -67,9 +71,9 @@ def _find_device(receiver, name): return dev -def _print_receiver(receiver, short=True): - if short: - print ("-: Unifying Receiver [%s:%s]" % (receiver.path, receiver.serial)) +def _print_receiver(receiver, verbose=False): + if not verbose: + print ("-: Unifying Receiver [%s:%s] with %d devices" % (receiver.path, receiver.serial, receiver.count())) return print ("-: Unifying Receiver") @@ -78,6 +82,8 @@ def _print_receiver(receiver, short=True): for f in receiver.firmware: print (" %-11s: %s" % (f.kind, f.version)) + print (" Has %d paired device(s)." % receiver.count()) + notifications = receiver.request(0x8100) if notifications: notifications = ord(notifications[0:1]) << 16 | ord(notifications[1:2]) << 8 @@ -87,96 +93,100 @@ def _print_receiver(receiver, short=True): else: print (" All notifications disabled.") - print (" Reported %d paired device(s)." % receiver.count()) activity = receiver.request(0x83B3) if activity: activity = [(d, ord(activity[d - 1:d])) for d in range(1, receiver.max_devices)] print(" Device activity counters: %s" % ', '.join(('%d=%d' % (d, a)) for d, a in activity if a > 0)) -def _print_device(dev, short=True): +def _print_device(dev, verbose=False): p = dev.protocol state = '' if p > 0 else ' inactive' - if short: + if not verbose: print ("%d: %s [%s:%s]%s" % (dev.number, dev.name, dev.codename, dev.serial, state)) return print ("%d: %s" % (dev.number, dev.name)) print (" Codename : %s" % dev.codename) print (" Kind : %s" % dev.kind) - print (" Serial number: %s" % dev.serial) - print (" Wireless PID : %s" % dev.wpid) - if p == 0: print (" Protocol : unknown (device is inactive)") else: print (" Protocol : HID++ %1.1f" % p) - + print (" Polling rate : %d ms" % dev.polling_rate) + print (" Wireless PID : %s" % dev.wpid) + print (" Serial number: %s" % dev.serial) for fw in dev.firmware: - print (" %-11s: %s %s" % (fw.kind, fw.name, fw.version)) + print (" %-11s: %s" % (fw.kind, (fw.name + ' ' + fw.version).strip())) if dev.power_switch_location: print (" The power switch is located on the %s" % dev.power_switch_location) - if p == 0: - return from logitech.unifying_receiver import hidpp10, hidpp20 + if p > 0: - if dev.features: - print (" Supports %d HID++ 2.0 features:" % len(dev.features)) - for index, feature in enumerate(dev.features): - feature = dev.features[index] - flags = dev.request(0x0000, feature.bytes(2)) - flags = 0 if flags is None else ord(flags[1:2]) - flags = hidpp20.FEATURE_FLAG.flag_names(flags) - print (" %2d: %-20s {%04X} %s" % (index, feature, feature, flags)) + if dev.features: + print (" Supports %d HID++ 2.0 features:" % len(dev.features)) + for index, feature in enumerate(dev.features): + feature = dev.features[index] + flags = dev.request(0x0000, feature.bytes(2)) + flags = 0 if flags is None else ord(flags[1:2]) + flags = hidpp20.FEATURE_FLAG.flag_names(flags) + print (" %2d: %-20s {%04X} %s" % (index, feature, feature, flags)) - if dev.keys: - print (" Has %d reprogrammable keys:" % len(dev.keys)) - for k in dev.keys: - flags = hidpp20.KEY_FLAG.flag_names(k.flags) - print (" %2d: %-20s => %-20s %s" % (k.index, hidpp20.KEY[k.key], hidpp20.KEY[k.task], flags)) + if dev.keys: + print (" Has %d reprogrammable keys:" % len(dev.keys)) + for k in dev.keys: + flags = hidpp20.KEY_FLAG.flag_names(k.flags) + print (" %2d: %-20s => %-20s %s" % (k.index, k.key, k.task, flags)) - battery = hidpp10.get_battery(dev) or hidpp20.get_battery(dev) - if battery: - charge, status = battery - print (" Battery: %d%% charged, %s" % (charge, status)) + if p > 0: + battery = hidpp20.get_battery(dev) + if battery is None: + battery = hidpp10.get_battery(dev) + if battery: + charge, status = battery + print (" Battery is %d%% charged, %s" % (charge, status)) + else: + print (" Battery status unavailable.") else: - print (" Battery report not supported.") + print (" Battery status is unknown (device is inactive).") +# +# +# -def list_devices(receiver, args): - _print_receiver(receiver, args.short) - for dev in receiver: - if not args.short: - print ("") - _print_device(dev, args.short) - - -def show_device(receiver, args): - dev = _find_device(receiver, args.device) - if dev is receiver: - _print_receiver(receiver, False) +def show_devices(receiver, args): + if args.device == 'all': + _print_receiver(receiver, args.verbose) + for dev in receiver: + if args.verbose: + print ("") + _print_device(dev, args.verbose) else: - _print_device(dev, False) + dev = _find_device(receiver, args.device) + if dev is receiver: + _print_receiver(receiver, args.verbose) + else: + _print_device(dev, args.verbose) def pair_device(receiver, args): # get all current devices known_devices = [dev.number for dev in receiver] - from threading import Event - done = Event() - from logitech.unifying_receiver import status r_status = status.ReceiverStatus(receiver, lambda *args, **kwargs: None) + done = False + def _events_handler(event): + global done if event.devnumber == 0xFF: r_status.process_event(event) if not r_status.lock_open: - done.set() + done = True elif event.sub_id == 0x41 and event.address == 0x04: if event.devnumber not in known_devices: r_status.new_device = receiver[event.devnumber] @@ -193,7 +203,7 @@ def pair_device(receiver, args): receiver.set_lock(False, timeout=20) print ("Pairing: turn your new device on (timing out in 20 seconds).") - while not done.is_set(): + while not done: event = base.read(receiver.handle, 2000) if event: event = base.make_event(*event) @@ -214,9 +224,9 @@ def pair_device(receiver, args): def unpair_device(receiver, args): dev = _find_device(receiver, args.device) if dev is receiver: - _fail("cannot unpair the receiver") + _fail("cannot unpair the receiver from itself!") - # query these + # query these now, it's last chance to get them number, name, codename, serial = dev.number, dev.name, dev.codename, dev.serial try: del receiver[number] @@ -228,41 +238,37 @@ def unpair_device(receiver, args): def _parse_arguments(): import argparse arg_parser = argparse.ArgumentParser(prog=NAME.lower()) - arg_parser.add_argument('-v', '--verbose', - action='count', default=0, - help='increase the logger verbosity (may be repeated)') - arg_parser.add_argument('-V', '--version', - action='version', - version='%(prog)s ' + __version__) - subparsers = arg_parser.add_subparsers(title='sub-commands') + arg_parser.add_argument('-d', '--debug', action='count', default=0, + help='print logging messages, for debugging purposes (may be repeated for extra verbosity)') + arg_parser.add_argument('-V', '--version', action='version', version='%(prog)s ' + __version__) - list_p = subparsers.add_parser('list', help='list paired devices') - list_p.add_argument('--full', action='store_false', dest='short', - help='print full info about each device') - list_p.set_defaults(cmd=list_devices) + subparsers = arg_parser.add_subparsers(title='commands') - show_p = subparsers.add_parser('show', help='show info about a single device', - epilog='The argument may be a device number (1..6),' - ' at least 3 characters of a device\'s name,' - ' or "receiver".') - show_p.add_argument('device', help='device to show information about') - show_p.set_defaults(cmd=show_device) + sp = subparsers.add_parser('show', help='show information about paired devices') + sp.add_argument('device', nargs='?', default='all', + help='device to show information about; may be a device number (1..6), a device serial, ' + 'at least 3 characters of a device\'s name, "receiver", or "all" (the default)') + sp.add_argument('-v', '--verbose', action='store_true', + help='print all available information about the inspected device(s)') + sp.set_defaults(cmd=show_devices) - pair_p = subparsers.add_parser('pair', help='pair a new device') - pair_p.set_defaults(cmd=pair_device) - unpair_p = subparsers.add_parser('unpair', help='unpair a device', - epilog='The argument may be a device number (1..6),' - ' or at least 3 characters of a device\'s name.') - unpair_p.add_argument('device', help='device to unpair') - unpair_p.set_defaults(cmd=unpair_device) + sp = subparsers.add_parser('pair', help='pair a new device', + epilog='The Logitech Unifying Receiver supports up to 6 paired devices at the same time.') + sp.set_defaults(cmd=pair_device) + + sp = subparsers.add_parser('unpair', help='unpair a device') + sp.add_argument('device', + help='device to unpair; may be a device number (1..6), a device serial, ' + 'or at least 3 characters of a device\'s name.') + sp.set_defaults(cmd=unpair_device) args = arg_parser.parse_args() import logging - if args.verbose > 0: - log_level = logging.WARNING - 10 * args.verbose - log_format='%(asctime)s %(levelname)8s [%(threadName)s] %(name)s: %(message)s' + if args.debug > 0: + log_level = logging.WARNING - 10 * args.debug + log_format='%(asctime)s %(levelname)8s %(name)s: %(message)s' logging.basicConfig(level=max(log_level, logging.DEBUG), format=log_format) else: logging.root.addHandler(logging.NullHandler()) diff --git a/lib/hidapi/hidconsole.py b/lib/hidapi/hidconsole.py index 7496b082..c3d51e37 100644 --- a/lib/hidapi/hidconsole.py +++ b/lib/hidapi/hidconsole.py @@ -101,7 +101,7 @@ if __name__ == '__main__': else: # the devices might reply quite slow rlist, wlist, xlist = _select([handle], [], [], 1) - time.sleep(0.050) + time.sleep(1) except EOFError: pass except Exception as e: