diff --git a/docs/devices.md b/docs/devices.md index c8b6fe96..7c6b4cfc 100644 --- a/docs/devices.md +++ b/docs/devices.md @@ -82,10 +82,10 @@ Mice (Unifying): | M525 | 2.0 | yes | - | | | M600 Touch | 2.0 | yes | | | | M705 Marathon | 1.0 | yes | - | smooth scrolling | -| T400 Zone Touch | | | | | +| T400 Zone Touch | 2.0 | yes | | | | T620 Touch | 2.0 | yes | | | | Performance MX | 1.0 | yes | R/W | | -| Anywhere MX | 1.0 | yes | - | | +| Anywhere MX | 1.0 | yes | R/W | smooth scrolling | | Cube | 2.0 | yes | | | diff --git a/docs/devices/anywhere-mx.txt b/docs/devices/anywhere-mx.txt new file mode 100644 index 00000000..bd50383f --- /dev/null +++ b/docs/devices/anywhere-mx.txt @@ -0,0 +1,88 @@ +Receiver +LZ301AR-DJ +M/N:C-U0007 +(ltunify) +Serial number: D1759614 +Firmware version: 012.001.00019 +Bootloader version: BL.002.014 + +Supported notification flags: 00 09 00 +- 01: Wireless Notifications +- 08: Software Present + +Mouse +(ltunify) +HID++ version: 1.0 +Device index 1 +Mouse +Name: Anywhere MX +Wireless Product ID: 1017 +Serial number: 13865F99 +Firmware version: 016.001.00040 +Bootloader version: BL.002.010 +(solaar) +Unifying Receiver + Device path : /dev/hidraw2 + USB id : 046d:c52b + Serial : D1759614 + Firmware : 12.01.B0019 + Bootloader : 02.14 + Has 1 paired device(s) out of a maximum of 6. + Notifications: (none) + Device activity counters: 1=19 + + 1: Anywhere Mouse MX + Codename : Anywhere MX + Kind : mouse + Wireless PID : 1017 + Protocol : HID++ 1.0 + Polling rate : 8 ms (125Hz) + Serial number: 13865F99 + Firmware: 16.01.B0040 + Bootloader: 02.10 + Other: 00.06 + The power switch is located on the base. + Notifications: (none). + Battery: 100%, discharging. + +(scan-registers) +# Old notification flags: 000000 +# 00 - Enabled Notifications, supported flags: Battery Status (10) +>> ( 0.792) [10 01 8100 100000] b'\x10\x01\x81\x00\x10\x00\x00' + +# 01 - scrolling settings? +# Flags: +# 0x40 - Enable Smooth Scrolling +# 0x02 - "confuse KDE", see https://github.com/pwr/Solaar/issues/115 +<< ( 0.011) [10 01 8101 000000] b'\x10\x01\x81\x01\x00\x00\x00' +>> ( 1.710) [10 01 8101 020000] b'\x10\x01\x81\x01\x02\x00\x00' + +# 0D - battery level. first byte is remaining charge in percent; second is +# (guessed) maximum?; third is charge status (30=discharging) +# "10 ix 0D 64 64 30 00" is a battery notification (when enabled) +<< ( 9.789) [10 01 810D 000000] b'\x10\x01\x81\r\x00\x00\x00' +>> ( 9.816) [10 01 810D 646430] b'\x10\x01\x81\rdd0' + +# 63 - DPI (range 0x80-0x8e (inclusive)) +<< ( 75.521) [10 01 8163 000000] b'\x10\x01\x81c\x00\x00\x00' +>> ( 75.550) [10 01 8163 890000] b'\x10\x01\x81c\x89\x00\x00' + +# D0 - ? +<< ( 163.118) [10 01 81D0 000000] b'\x10\x01\x81\xd0\x00\x00\x00' +>> ( 163.148) [10 01 81D0 000000] b'\x10\x01\x81\xd0\x00\x00\x00' + +# D4 - ? +<< ( 166.034) [10 01 81D4 000000] b'\x10\x01\x81\xd4\x00\x00\x00' +>> ( 166.063) [10 01 81D4 000008] b'\x10\x01\x81\xd4\x00\x00\x08' + +# F1 - firmware/bootloader version +<< ( 187.172) [10 01 81F1 000000] b'\x10\x01\x81\xf1\x00\x00\x00' +>> ( 187.199) [10 01 8F81 F10300] b'\x10\x01\x8f\x81\xf1\x03\x00' + +# F3 - ? +<< ( 188.629) [10 01 81F3 000000] b'\x10\x01\x81\xf3\x00\x00\x00' +>> ( 188.661) [10 01 81F3 000000] b'\x10\x01\x81\xf3\x00\x00\x00' + +# FD - ? +<< ( 195.715) [10 01 83FD 000000] b'\x10\x01\x83\xfd\x00\x00\x00' +>> ( 195.746) [11 01 83FD 00000000000000000000000000000000] b'\x11\x01\x83\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' diff --git a/docs/devices/k750.txt b/docs/devices/k750.txt new file mode 100644 index 00000000..8e7720bf --- /dev/null +++ b/docs/devices/k750.txt @@ -0,0 +1,39 @@ +(from Julien Danjou) + +(solaar) + 2: Wireless Solar Keyboard K750 + Codename : K750 + Kind : keyboard + Wireless PID : 4002 + Protocol : HID++ 2.0 + Polling rate : 20 ms (50Hz) + Serial number: 5692B2EC + Firmware: RQK 33.00.B0015 + Bootloader: DFU 00.02.B0003 + The power switch is located on the edge of top right corner. + Supports 11 HID++ 2.0 features: + 0: ROOT {0000} + 1: FEATURE SET {0001} + 2: DEVICE FW VERSION {0003} + 3: DEVICE NAME {0005} + 4: REPROG CONTROLS {1B00} + 5: WIRELESS DEVICE STATUS {1D4B} + 6: unknown:1DF3 {1DF3} hidden + 7: FN INVERSION {40A0} + 8: ENCRYPTION {4100} + 9: SOLAR DASHBOARD {4301} + 10: KEYBOARD LAYOUT {4520} + Has 12 reprogrammable keys: + 0: MY HOME => HomePage FN sensitive, is FN, reprogrammable + 1: Mail => Email FN sensitive, is FN, reprogrammable + 2: SEARCH => Search FN sensitive, is FN, reprogrammable + 3: Calculator => Calculator FN sensitive, is FN, reprogrammable + 4: MEDIA PLAYER => Music FN sensitive, is FN, reprogrammable + 5: Previous => Previous FN sensitive, is FN + 6: Play/Pause => Play/Pause FN sensitive, is FN + 7: Next => Next FN sensitive, is FN + 8: Mute => Mute FN sensitive, is FN + 9: Volume Down => Volume Down FN sensitive, is FN + 10: Volume Up => Volume Up FN sensitive, is FN + 11: SLEEP => Sleep FN sensitive, is FN, reprogrammable + Battery status unavailable. diff --git a/docs/devices/m525.txt b/docs/devices/m525.txt index 375ce3c4..43648f23 100644 --- a/docs/devices/m525.txt +++ b/docs/devices/m525.txt @@ -1,2 +1,56 @@ -No non-error messages received for GET_REG and GET_REG_LONG. Perhaps because -this is a HID++ 2.0 device? +Mouse +(ltunify) +HID++ version: 2.0 +Device index 1 +Mouse +Name: M525 +Wireless Product ID: 4013 +Serial number: DAFA335E +Device was unavailable, version information not available. +Total number of HID++ 2.0 features: 12 + 0: [0000] IRoot + 1: [0001] IFeatureSet + 2: [0003] IFirmwareInfo + 3: [0005] GetDeviceNameType + 4: [1000] batteryLevelStatus + 5: [1D4B] WirelessDeviceStatus + 6: [1DF3] H unknown + 7: [1B00] SpecialKeysMSEButtons + 8: [1DF0] H unknown + 9: [1F03] H unknown + 10: [2100] VerticalScrolling + 11: [2120] HiResScrolling + 12: [2200] MousePointer +(O = obsolete feature; H = SW hidden feature; + I = reserved for internal use) +(solaar) + 1: Wireless Mouse M525 + Codename : M525 + Kind : mouse + Wireless PID : 4013 + Protocol : HID++ 2.0 + Polling rate : 8 ms (125Hz) + Serial number: DAFA335E + Firmware: RQM 27.02.B0028 + The power switch is located on the base. + Supports 13 HID++ 2.0 features: + 0: ROOT {0000} + 1: FEATURE SET {0001} + 2: DEVICE FW VERSION {0003} + 3: DEVICE NAME {0005} + 4: BATTERY STATUS {1000} + 5: WIRELESS DEVICE STATUS {1D4B} + 6: unknown:1DF3 {1DF3} hidden + 7: REPROG CONTROLS {1B00} + 8: unknown:1DF0 {1DF0} hidden + 9: unknown:1F03 {1F03} hidden + 10: VERTICAL SCROLLING {2100} + 11: HI RES SCROLLING {2120} + 12: MOUSE POINTER {2200} + Has 5 reprogrammable keys: + 0: LEFT CLICK => LeftClick mse, reprogrammable + 1: RIGHT CLICK => RightClick mse, reprogrammable + 2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable + 3: BACK AS BUTTON 4 => Back mse, reprogrammable + 4: FORWARD AS BUTTON 5 => BrowserForward mse, reprogrammable + Battery: 90%, discharging. diff --git a/docs/devices/t400.txt b/docs/devices/t400.txt new file mode 100644 index 00000000..76752b24 --- /dev/null +++ b/docs/devices/t400.txt @@ -0,0 +1,109 @@ +Receiver +LZ2388S-DJ +M/N:C-U0007 +(ltunify) +Serial number: E6B794F8 +Firmware version: 012.001.00019 +Bootloader version: BL.002.014 + +Mouse +(ltunify) +HID++ version: 2.0 +Device index 1 +Mouse +Name: T400 +Wireless Product ID: 4026 +Serial number: 131A3093 +Device was unavailable, version information not available. +Total number of HID++ 2.0 features: 27 + 0: [0000] IRoot + 1: [0001] IFeatureSet + 2: [0002] unknown + 3: [0003] IFirmwareInfo + 4: [0005] GetDeviceNameType + 5: [00C0] DFUControl + 6: [1000] batteryLevelStatus + 7: [1802] HI unknown + 8: [1810] HI unknown + 9: [1830] HI unknown + 10: [1850] HI unknown + 11: [1860] HI unknown + 12: [1890] HI unknown + 13: [18A0] HI unknown + 14: [18E3] HI unknown + 15: [1B00] SpecialKeysMSEButtons + 16: [1D4B] WirelessDeviceStatus + 17: [1DF3] HI unknown + 18: [1E00] H unknown + 19: [1E80] HI unknown + 20: [1F03] HI unknown + 21: [1F04] HI unknown + 22: [2100] VerticalScrolling + 23: [2101] H unknown + 24: [2120] HiResScrolling + 25: [2200] MousePointer + 26: [6110] H TouchmouseRawPoints + 27: [1B03] ReprogControlsV3 +(O = obsolete feature; H = SW hidden feature; + I = reserved for internal use) +(solaar) +Unifying Receiver + Device path : /dev/hidraw2 + USB id : 046d:c52b + Serial : E6B794F8 + Firmware : 12.01.B0019 + Bootloader : 02.14 + Has 1 paired device(s) out of a maximum of 6. + Notifications: (none) + Device activity counters: 1=134 + + 1: Zone Touch Mouse T400 + Codename : T400 + Kind : mouse + Wireless PID : 4026 + Protocol : HID++ 2.0 + Polling rate : 8 ms (125Hz) + Serial number: 131A3093 + Firmware: RQM 39.00.B0029 + Bootloader: BL 03.00 + Hardware: 72 + Other: + The power switch is located on the base. + Supports 28 HID++ 2.0 features: + 0: ROOT {0000} + 1: FEATURE SET {0001} + 2: FEATURE INFO {0002} + 3: DEVICE FW VERSION {0003} + 4: DEVICE NAME {0005} + 5: DFUCONTROL {00C0} + 6: BATTERY STATUS {1000} + 7: unknown:1802 {1802} internal, hidden + 8: unknown:1810 {1810} internal, hidden + 9: unknown:1830 {1830} internal, hidden + 10: unknown:1850 {1850} internal, hidden + 11: unknown:1860 {1860} internal, hidden + 12: unknown:1890 {1890} internal, hidden + 13: unknown:18A0 {18A0} internal, hidden + 14: unknown:18E3 {18E3} internal, hidden + 15: REPROG CONTROLS {1B00} + 16: WIRELESS DEVICE STATUS {1D4B} + 17: unknown:1DF3 {1DF3} internal, hidden + 18: unknown:1E00 {1E00} hidden + 19: unknown:1E80 {1E80} internal, hidden + 20: unknown:1F03 {1F03} internal, hidden + 21: unknown:1F04 {1F04} internal, hidden + 22: VERTICAL SCROLLING {2100} + 23: unknown:2101 {2101} hidden + 24: HI RES SCROLLING {2120} + 25: MOUSE POINTER {2200} + 26: TOUCHMOUSE RAW POINTS {6110} hidden + 27: REPROG CONTROLS V3 {1B03} + Has 7 reprogrammable keys: + 0: LEFT CLICK => LeftClick mse, reprogrammable + 1: RIGHT CLICK => RightClick mse, reprogrammable + 2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable + 3: METRO START SCREEN => MetroStartScreen mse, reprogrammable + 4: ZOOMIN => Do Nothing mse, reprogrammable + 5: ZOOMOUT => Do Nothing mse, reprogrammable + 6: BACK HSCROLL => TouchBackForwardHorzScroll + Battery: 100%, discharging. diff --git a/docs/devices/t650.txt b/docs/devices/t650.txt new file mode 100644 index 00000000..98ade9e5 --- /dev/null +++ b/docs/devices/t650.txt @@ -0,0 +1,94 @@ +Receiver +LZ2458D-DJ +M/N:C-U0008 +(ltunify) +Serial number: 28E69A3E +Firmware version: 024.000.00018 +Bootloader version: BL.000.006 + +Touchpad +(ltunify) +HID++ version: 2.0 +Device index 1 +Touchpad +Name: T650 +Wireless Product ID: 4101 +Serial number: 22205A4D +Device was unavailable, version information not available. +Total number of HID++ 2.0 features: 22 + 0: [0000] IRoot + 1: [0001] IFeatureSet + 2: [0002] unknown + 3: [0003] IFirmwareInfo + 4: [0005] GetDeviceNameType + 5: [1000] batteryLevelStatus + 6: [1D4B] WirelessDeviceStatus + 7: [1DF3] HI unknown + 8: [1B00] SpecialKeysMSEButtons + 9: [1F03] HI unknown + 10: [2100] VerticalScrolling + 11: [2120] HiResScrolling + 12: [2200] MousePointer + 13: [00C0] DFUControl + 14: [1E80] HI unknown + 15: [6100] TouchpadRawXy + 16: [1860] HI unknown + 17: [1E00] H unknown + 18: [1B01] ReprogControlsV2 + 19: [1890] HI unknown + 20: [18E5] HI unknown + 21: [18A0] HI unknown + 22: [1830] HI unknown +(O = obsolete feature; H = SW hidden feature; + I = reserved for internal use) +(solaar) +Unifying Receiver + Device path : /dev/hidraw2 + USB id : 046d:c52b + Serial : 28E69A3E + Firmware : 24.00.B0018 + Bootloader : 00.06 + Has 1 paired device(s) out of a maximum of 6. + Notifications: (none) + Device activity counters: 1=221 + + 1: Wireless Rechargeable Touchpad T650 + Codename : T650 + Kind : touchpad + Wireless PID : 4101 + Protocol : HID++ 2.0 + Polling rate : 8 ms (125Hz) + Serial number: 22205A4D + Firmware: RQM 41.01.B0037 + Bootloader: BL 03.00 + Hardware: 72 + Other: + The power switch is located on the base. + Supports 23 HID++ 2.0 features: + 0: ROOT {0000} + 1: FEATURE SET {0001} + 2: FEATURE INFO {0002} + 3: DEVICE FW VERSION {0003} + 4: DEVICE NAME {0005} + 5: BATTERY STATUS {1000} + 6: WIRELESS DEVICE STATUS {1D4B} + 7: unknown:1DF3 {1DF3} internal, hidden + 8: REPROG CONTROLS {1B00} + 9: unknown:1F03 {1F03} internal, hidden + 10: VERTICAL SCROLLING {2100} + 11: HI RES SCROLLING {2120} + 12: MOUSE POINTER {2200} + 13: DFUCONTROL {00C0} + 14: unknown:1E80 {1E80} internal, hidden + 15: TOUCHPAD RAW XY {6100} + 16: unknown:1860 {1860} internal, hidden + 17: unknown:1E00 {1E00} hidden + 18: REPROG CONTROLS V2 {1B01} + 19: unknown:1890 {1890} internal, hidden + 20: unknown:18E5 {18E5} internal, hidden + 21: unknown:18A0 {18A0} internal, hidden + 22: unknown:1830 {1830} internal, hidden + Has 2 reprogrammable keys: + 0: LEFT CLICK => LeftClick mse, reprogrammable + 1: RIGHT CLICK => RightClick mse, reprogrammable + Battery: 50%, discharging. diff --git a/docs/logitech/Unifying_receiver_DJ_collection_specification_draft.pdf b/docs/logitech/Unifying_receiver_DJ_collection_specification_draft.pdf new file mode 100644 index 00000000..862ae947 Binary files /dev/null and b/docs/logitech/Unifying_receiver_DJ_collection_specification_draft.pdf differ diff --git a/lib/logitech_receiver/hidpp20.py b/lib/logitech_receiver/hidpp20.py index fd03da2e..5ab67008 100644 --- a/lib/logitech_receiver/hidpp20.py +++ b/lib/logitech_receiver/hidpp20.py @@ -210,9 +210,10 @@ class FeaturesArray(object): indices = index.indices(len(self.features)) return [self.__getitem__(i) for i in range(*indices)] - def __contains__(self, value): + def __contains__(self, featureId): + """Tests whether the list contains given Feature ID""" if self._check(): - ivalue = int(value) + ivalue = int(featureId) may_have = False for f in self.features: @@ -220,8 +221,6 @@ class FeaturesArray(object): may_have = True elif ivalue == int(f): return True - elif ivalue < int(f): - break if may_have: reply = self.device.request(0x0000, _pack('!H', ivalue)) @@ -231,17 +230,16 @@ class FeaturesArray(object): self.features[index] = FEATURE[ivalue] return True - def index(self, value): + def index(self, featureId): + """Gets the Feature Index for a given Feature ID""" if self._check(): may_have = False - ivalue = int(value) + ivalue = int(featureId) for index, f in enumerate(self.features): if f is None: may_have = True elif ivalue == int(f): return index - elif ivalue < int(f): - raise ValueError("%r not in list" % value) if may_have: reply = self.device.request(0x0000, _pack('!H', ivalue)) @@ -250,7 +248,7 @@ class FeaturesArray(object): self.features[index] = FEATURE[ivalue] return index - raise ValueError("%r not in list" % value) + raise ValueError("%r not in list" % featureId) def __iter__(self): if self._check(): diff --git a/lib/logitech_receiver/notifications.py b/lib/logitech_receiver/notifications.py index 93bb3482..7a0d2664 100644 --- a/lib/logitech_receiver/notifications.py +++ b/lib/logitech_receiver/notifications.py @@ -127,7 +127,7 @@ def _process_hidpp10_custom_notification(device, status, n): status.set_battery_info(charge, status_text) return True - if n.sub_id == _R.illumination: + if n.sub_id == _R.keyboard_illumination: # message layout: 10 ix 17("address") # TODO anything we can do with this? if _log.isEnabledFor(_INFO): @@ -189,7 +189,7 @@ def _process_hidpp10_notification(device, status, n): if n.address == 0x01: if _log.isEnabledFor(_DEBUG): _log.debug("%s: device powered on", device) - reason = str(status) or _("powered on") + reason = status.to_string() or _("powered on") status.changed(active=True, alert=_ALERT.NOTIFICATION, reason=reason) else: _log.warn("%s: unknown %s", device, n) diff --git a/lib/logitech_receiver/status.py b/lib/logitech_receiver/status.py index fc68c71d..4fb59113 100644 --- a/lib/logitech_receiver/status.py +++ b/lib/logitech_receiver/status.py @@ -139,7 +139,7 @@ class DeviceStatus(dict): # timestamp of when this status object was last updated self.updated = 0 - def __str__(self): + def to_string(self): def _items(): comma = False @@ -163,8 +163,6 @@ class DeviceStatus(dict): return ''.join(i for i in _items()) - __unicode__ = __str__ - def __repr__(self): return '{' + ', '.join('\'%s\': %r' % (k, v) for k, v in self.items()) + '}' @@ -202,9 +200,9 @@ class DeviceStatus(dict): # only show the notification once alert = ALERT.NOTIFICATION | ALERT.ATTENTION if isinstance(level, _NamedInt): - reason = 'battery: %s (%s)' % (level, status) + reason = '%s: %s (%s)' % (_("Battery"), _(str(level)), _(str(status))) else: - reason = 'battery: %d%% (%s)' % (level, status) + reason = '%s: %d%% (%s)' % (_("Battery"), level, _(str(status))) if changed or reason: # update the leds on the device, if any diff --git a/lib/solaar/cli/__init__.py b/lib/solaar/cli/__init__.py index a339acf3..b94029a7 100644 --- a/lib/solaar/cli/__init__.py +++ b/lib/solaar/cli/__init__.py @@ -73,12 +73,15 @@ def _create_parser(): _cli_parser, actions = _create_parser() +print_help = _cli_parser.print_help -def _receivers(): +def _receivers(dev_path=None): from logitech_receiver import Receiver from logitech_receiver.base import receivers for dev_info in receivers(): + if dev_path is not None and dev_path != dev_info.path: + continue try: r = Receiver.open(dev_info) if _log.isEnabledFor(_DEBUG): @@ -129,21 +132,23 @@ def _find_device(receivers, name): raise Exception("no device found matching '%s'" % name) -def run(cli_args=None): - if cli_args == 'help': - _cli_parser.print_help() - return - +def run(cli_args=None, hidraw_path=None): if cli_args: action = cli_args[0] args = _cli_parser.parse_args(cli_args) else: args = _cli_parser.parse_args() + # Python 3 has an undocumented 'feature' that breaks parsing empty args + # http://bugs.python.org/issue16308 + if not 'cmd' in args: + _cli_parser.print_usage(_sys.stderr) + _sys.stderr.write('%s: error: too few arguments\n' % NAME.lower()) + _sys.exit(2) action = args.action assert action in actions try: - c = list(_receivers()) + c = list(_receivers(hidraw_path)) if not c: raise Exception('Logitech receiver not found') diff --git a/lib/solaar/cli/show.py b/lib/solaar/cli/show.py index 906cb40c..29f7ca62 100644 --- a/lib/solaar/cli/show.py +++ b/lib/solaar/cli/show.py @@ -107,10 +107,13 @@ def _print_device(dev): if battery is not None: from logitech_receiver.common import NamedInt as _NamedInt level, status = battery - if isinstance(level, _NamedInt): - text = str(level) + if level is not None: + if isinstance(level, _NamedInt): + text = str(level) + else: + text = '%d%%' % level else: - text = '%d%%' % level + text = 'N/A' print (' Battery: %s, %s.' % (text, status)) else: print (' Battery status unavailable.') @@ -128,12 +131,14 @@ def run(receivers, args, find_receiver, find_device): for r in receivers: _print_receiver(r) count = r.count() - for dev in r: - print ('') - _print_device(dev) - count -= 1 - if count == 0: - break + if count: + for dev in r: + print ('') + _print_device(dev) + count -= 1 + if not count: + break + print ('') return dev = find_receiver(receivers, device_name) diff --git a/lib/solaar/gtk.py b/lib/solaar/gtk.py index aabd35f5..16c8549f 100644 --- a/lib/solaar/gtk.py +++ b/lib/solaar/gtk.py @@ -42,6 +42,10 @@ def _parse_arguments(): arg_parser = argparse.ArgumentParser(prog=NAME.lower()) 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('-D', '--hidraw', action='store', dest='hidraw_path', metavar='PATH', + help='unifying receiver to use; the first detected receiver if unspecified. Example: /dev/hidraw2') + arg_parser.add_argument('--restart-on-wake-up', action='store_true', + help='restart Solaar on sleep wake-up (experimental)') arg_parser.add_argument('-V', '--version', action='version', version='%(prog)s ' + __version__) arg_parser.add_argument('--help-actions', action='store_true', help='print help for the optional actions') @@ -51,7 +55,8 @@ def _parse_arguments(): args = arg_parser.parse_args() if args.help_actions: - return 'help' + _cli.print_help() + return import logging if args.debug > 0: @@ -62,11 +67,11 @@ def _parse_arguments(): logging.root.addHandler(logging.NullHandler()) logging.root.setLevel(logging.ERROR) - if args.action: - return args.action + if not args.action: + if logging.root.isEnabledFor(logging.INFO): + logging.info("language %s (%s), translations path %s", _i18n.language, _i18n.encoding, _i18n.path) - if logging.root.isEnabledFor(logging.INFO): - logging.info("language %s (%s), translations path %s", _i18n.language, _i18n.encoding, _i18n.path) + return args def main(): @@ -76,9 +81,9 @@ def main(): import signal signal.signal(signal.SIGINT, signal.SIG_DFL) - cli_action = _parse_arguments() - if cli_action: - return _cli.run(cli_action) + args = _parse_arguments() + if not args: return + if args.action: return _cli.run(args.action, args.hidraw_path) _require('gi.repository', 'python-gi') _require('gi.repository.Gtk', 'gir1.2-gtk-3.0') @@ -87,6 +92,13 @@ def main(): import solaar.ui as ui import solaar.listener as listener listener.setup_scanner(ui.status_changed, ui.error_dialog) + + import solaar.upower as _upower + if args.restart_on_wake_up: + _upower.watch(listener.start_all, listener.stop_all) + else: + _upower.watch(listener.ping_all) + # main UI event loop ui.run_loop(listener.start_all, listener.stop_all) except Exception as e: diff --git a/lib/solaar/listener.py b/lib/solaar/listener.py index 0c770d33..a7f9040d 100644 --- a/lib/solaar/listener.py +++ b/lib/solaar/listener.py @@ -268,9 +268,16 @@ def stop_all(): l.join() -# stop/start all receiver threads on suspend/resume events, if possible -from . import upower -upower.watch(start_all, stop_all) +def ping_all(): + for l in _all_listeners.values(): + count = l.receiver.count() + if count: + for dev in l.receiver: + dev.ping() + l._status_changed(dev) + count -= 1 + if not count: + break from logitech_receiver import base as _base diff --git a/lib/solaar/ui/config_panel.py b/lib/solaar/ui/config_panel.py index 8adca6e1..99a37120 100644 --- a/lib/solaar/ui/config_panel.py +++ b/lib/solaar/ui/config_panel.py @@ -70,6 +70,7 @@ def _create_choice_control(setting): _write_async(s, cbbox.get_active_id(), cbbox.get_parent()) c = Gtk.ComboBoxText() + # TODO i18n text entries for entry in setting.choices: c.append(str(entry), str(entry)) c.connect('changed', _combo_notify, setting) diff --git a/lib/solaar/ui/notify.py b/lib/solaar/ui/notify.py index 48391a01..00016770 100644 --- a/lib/solaar/ui/notify.py +++ b/lib/solaar/ui/notify.py @@ -117,7 +117,7 @@ try: elif dev.status is None: message = _("unpaired") elif bool(dev.status): - message = dev.status.__str__() or _("connected") + message = dev.status.to_string() or _("connected") else: message = _("offline") diff --git a/lib/solaar/ui/tray.py b/lib/solaar/ui/tray.py index 3dd76f4c..3aec1476 100644 --- a/lib/solaar/ui/tray.py +++ b/lib/solaar/ui/tray.py @@ -281,7 +281,7 @@ def _generate_tooltip_lines(): if number is None: # receiver continue - p = str(status) + p = status.to_string() if p: # does it have any properties to print? yield '%s' % name if status: diff --git a/lib/solaar/ui/window.py b/lib/solaar/ui/window.py index e0401754..b56707a6 100644 --- a/lib/solaar/ui/window.py +++ b/lib/solaar/ui/window.py @@ -530,7 +530,7 @@ def _update_details(button): if read_all: for fw in list(device.firmware): - yield (' ' + str(fw.kind), (fw.name + ' ' + fw.version).strip()) + yield (' ' + _(str(fw.kind)), (fw.name + ' ' + fw.version).strip()) elif device.kind is None or device.online: yield (' %s' % _("Firmware"), '...') diff --git a/lib/solaar/upower.py b/lib/solaar/upower.py index 3570e484..36fd782a 100644 --- a/lib/solaar/upower.py +++ b/lib/solaar/upower.py @@ -43,7 +43,7 @@ def _resume(): _resume_callback() -def watch(on_resume_callback, on_suspend_callback): +def watch(on_resume_callback=None, on_suspend_callback=None): """Register callback for suspend/resume events. They are called only if the system DBus is running, and the UPower daemon is available.""" global _resume_callback, _suspend_callback diff --git a/po/fi.po b/po/fi.po index a47fb6eb..574ab14a 100644 --- a/po/fi.po +++ b/po/fi.po @@ -6,7 +6,7 @@ msgid "" msgstr "Project-Id-Version: solaar 0.9.2\n" "Report-Msgid-Bugs-To: \n" - "POT-Creation-Date: 2013-08-11 16:49+0300\n" + "POT-Creation-Date: 2013-09-29 16:22+0300\n" "PO-Revision-Date: 2013-08-05 18:49+0300\n" "Last-Translator: Tomi Leppänen\n" "Language-Team: none\n" @@ -179,6 +179,7 @@ msgid " paired devices." msgstr " paritettua laitetta" #: lib/logitech_receiver/status.py:149 lib/logitech_receiver/status.py:151 +#: lib/logitech_receiver/status.py:203 lib/logitech_receiver/status.py:205 #: lib/solaar/ui/window.py:143 msgid "Battery" msgstr "Akku" @@ -252,11 +253,11 @@ msgstr "Tietoja" msgid "Unpair" msgstr "Poista paritus" -#: lib/solaar/ui/config_panel.py:97 +#: lib/solaar/ui/config_panel.py:98 msgid "Working" msgstr "Työskentelee" -#: lib/solaar/ui/config_panel.py:100 +#: lib/solaar/ui/config_panel.py:101 msgid "Read/write operation failed." msgstr "Luku/kirjoitus operaatio epäonnistui." @@ -349,11 +350,11 @@ msgid "The wireless link between this device and its receiver is not " "within range." msgstr "Langaton yhteys laitteen ja vastaanottimen välillä ei ole salattu.\n" "\n" - "Osoitinlaitteita (hiiret, ohjainpallot, tasohiiret) käytettäessä se on pieni " - "tietoturvallisuusriski.\n" + "Osoitinlaitteita (hiiret, ohjainpallot, tasohiiret) käytettäessä se " + "on pieni tietoturvallisuusriski.\n" "\n" - "Kuitenkin, näppäimistöjen kanssa se on suuri tietoturvallisuusriski sillä " - "kolmannet osapuolet\n" + "Kuitenkin, näppäimistöjen kanssa se on suuri tietoturvallisuusriski " + "sillä kolmannet osapuolet\n" "voivat lukea huomaamattomasti kirjoitetun tekstin yhteyden kantaman " "sisällä." @@ -420,7 +421,6 @@ msgid "Protocol" msgstr "Yhteyskäytäntö" #: lib/solaar/ui/window.py:524 -#. fuzzy msgid "Polling rate" msgstr "Päivitysnopeus" diff --git a/po/fr.po b/po/fr.po index be7ec53d..646a6ba1 100644 --- a/po/fr.po +++ b/po/fr.po @@ -6,7 +6,7 @@ msgid "" msgstr "Project-Id-Version: solaar 0.9.2\n" "Report-Msgid-Bugs-To: \n" - "POT-Creation-Date: 2013-08-08 21:51+0200\n" + "POT-Creation-Date: 2013-08-25 01:33+0300\n" "PO-Revision-Date: 2013-08-07 14:43+0200\n" "Last-Translator: Damien Lallement \n" "Language-Team: Language: fr\n" @@ -255,11 +255,11 @@ msgstr "À propos de" msgid "Unpair" msgstr "Déconnecter" -#: lib/solaar/ui/config_panel.py:97 +#: lib/solaar/ui/config_panel.py:98 msgid "Working" msgstr "En fonctionnement" -#: lib/solaar/ui/config_panel.py:100 +#: lib/solaar/ui/config_panel.py:101 msgid "Read/write operation failed." msgstr "Les opérations de lecture/écriture ont échoué." diff --git a/po/pl.po b/po/pl.po index febf64d2..26a8a24d 100644 --- a/po/pl.po +++ b/po/pl.po @@ -6,7 +6,7 @@ msgid "" msgstr "Project-Id-Version: solaar 0.9.1\n" "Report-Msgid-Bugs-To: \n" - "POT-Creation-Date: 2013-08-08 21:53+0200\n" + "POT-Creation-Date: 2013-08-25 01:33+0300\n" "PO-Revision-Date: 2013-07-23 10:41+0100\n" "Last-Translator: Adrian Piotrowicz \n" "Language-Team: none\n" @@ -255,11 +255,11 @@ msgstr "O" msgid "Unpair" msgstr "Usuń parowanie" -#: lib/solaar/ui/config_panel.py:97 +#: lib/solaar/ui/config_panel.py:98 msgid "Working" msgstr "Pracuję" -#: lib/solaar/ui/config_panel.py:100 +#: lib/solaar/ui/config_panel.py:101 msgid "Read/write operation failed." msgstr "Operacja odczytu/zapisu nie powiodła się." diff --git a/po/ro.po b/po/ro.po index e6a77808..04fbb3d5 100644 --- a/po/ro.po +++ b/po/ro.po @@ -6,7 +6,7 @@ msgid "" msgstr "Project-Id-Version: solaar 0.9.1\n" "Report-Msgid-Bugs-To: \n" - "POT-Creation-Date: 2013-08-08 21:53+0200\n" + "POT-Creation-Date: 2013-08-25 01:33+0300\n" "PO-Revision-Date: 2013-07-17 20:27+0100\n" "Last-Translator: Daniel Pavel \n" "Language-Team: none\n" @@ -251,11 +251,11 @@ msgstr "Despre" msgid "Unpair" msgstr "Deconectează" -#: lib/solaar/ui/config_panel.py:97 +#: lib/solaar/ui/config_panel.py:98 msgid "Working" msgstr "Prelucrez" -#: lib/solaar/ui/config_panel.py:100 +#: lib/solaar/ui/config_panel.py:101 msgid "Read/write operation failed." msgstr "Operațiunea a eșuat." diff --git a/po/solaar.pot b/po/solaar.pot index 9be103df..cb3f53df 100644 --- a/po/solaar.pot +++ b/po/solaar.pot @@ -7,7 +7,7 @@ msgid "" msgstr "Project-Id-Version: solaar 0.9.2\n" "Report-Msgid-Bugs-To: \n" - "POT-Creation-Date: 2013-08-11 16:49+0300\n" + "POT-Creation-Date: 2013-09-29 16:22+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -173,6 +173,7 @@ msgid " paired devices." msgstr "" #: lib/logitech_receiver/status.py:149 lib/logitech_receiver/status.py:151 +#: lib/logitech_receiver/status.py:203 lib/logitech_receiver/status.py:205 #: lib/solaar/ui/window.py:143 msgid "Battery" msgstr "" @@ -242,11 +243,11 @@ msgstr "" msgid "Unpair" msgstr "" -#: lib/solaar/ui/config_panel.py:97 +#: lib/solaar/ui/config_panel.py:98 msgid "Working" msgstr "" -#: lib/solaar/ui/config_panel.py:100 +#: lib/solaar/ui/config_panel.py:101 msgid "Read/write operation failed." msgstr "" diff --git a/po/sv.po b/po/sv.po index 2c056ae5..81868f6a 100644 --- a/po/sv.po +++ b/po/sv.po @@ -6,8 +6,8 @@ msgid "" msgstr "Project-Id-Version: solaar 0.9.1\n" "Report-Msgid-Bugs-To: \n" - "POT-Creation-Date: 2013-08-08 19:46+0200\n" - "PO-Revision-Date: 2013-07-27 16:28+0100\n" + "POT-Creation-Date: 2013-08-25 01:33+0300\n" + "PO-Revision-Date: 2013-08-20 22:43+0100\n" "Last-Translator: Daniel Zippert & Emelie Snecker \n" "Language-Team: none\n" @@ -98,15 +98,15 @@ msgstr "Annan" #: lib/logitech_receiver/notifications.py:67 msgid "closed" -msgstr "låst" +msgstr "inaktiverat" #: lib/logitech_receiver/notifications.py:67 msgid "open" -msgstr "öppen" +msgstr "aktiverat" #: lib/logitech_receiver/notifications.py:67 msgid "pairing lock is " -msgstr "parkopplingsläge låst" +msgstr "parkopplingsläget är " #: lib/logitech_receiver/notifications.py:150 lib/solaar/ui/notify.py:118 msgid "unpaired" @@ -207,8 +207,8 @@ msgstr "Behörighetsfel" #, python-format msgid "Found a Logitech Receiver (%s), but did not have permission to open " "it." -msgstr "En Logitech mottagare hittades (%s), men behörighet att använda " - "den saknas." +msgstr "En Logitech mottagare hittades (%s), men behörighet att använda den " + "saknas." #: lib/solaar/ui/__init__.py:51 msgid "If you've just installed Solaar, try removing the receiver and " @@ -227,13 +227,13 @@ msgstr "Misslyckades att bryta parkoppling mellan %{device} och %{receiver}." #: lib/solaar/ui/__init__.py:56 msgid "The receiver returned an error, with no further details." -msgstr "Mottagaren rapporterade ett fel, utan specifika detaljer." +msgstr "Mottagaren rapporterade ett fel, utan specifika detaljer." #: lib/solaar/ui/about.py:39 msgid "Shows status of devices connected\n" "through wireless Logitech receivers." msgstr "Visa status för enheter kopplade till\n" - "din trådlösa Logitech mottagare" + "din trådlösa Logitech mottagare" #: lib/solaar/ui/about.py:48 msgid "GUI design" @@ -256,11 +256,11 @@ msgstr "Om" msgid "Unpair" msgstr "Ta bort parkoppling" -#: lib/solaar/ui/config_panel.py:97 +#: lib/solaar/ui/config_panel.py:98 msgid "Working" msgstr "Lyckades" -#: lib/solaar/ui/config_panel.py:100 +#: lib/solaar/ui/config_panel.py:101 msgid "Read/write operation failed." msgstr "Läsning/Skrivning misslyckades." @@ -286,7 +286,7 @@ msgstr "Se till så att enheten inom räckhåll, och har tillräckligt laddat " #: lib/solaar/ui/pair_window.py:137 msgid "A new device was detected, but it is not compatible with this " "receiver." -msgstr "En ny enhet upptäcktes, men är inte kompatibel med mottagaren. " +msgstr "En ny enhet upptäcktes, men är inte kompatibel med mottagaren. " #: lib/solaar/ui/pair_window.py:139 #, python-format @@ -303,7 +303,7 @@ msgstr "Ny enhet har hittats" #: lib/solaar/ui/pair_window.py:180 msgid "The wireless link is not encrypted" -msgstr "Den trådlösa anslutningen är okrypterad" +msgstr "Den trådlösa anslutningen är okrypterad" #: lib/solaar/ui/pair_window.py:197 msgid "pair new device" @@ -337,7 +337,7 @@ msgstr "ingen status" #: lib/solaar/ui/window.py:58 msgid "The wireless link between this device and its receiver is encrypted." -msgstr "Den trådlösa anslutningen är krypterad." +msgstr "Den trådlösa anslutningen är krypterad." #: lib/solaar/ui/window.py:59 msgid "The wireless link between this device and its receiver is not " diff --git a/setup.py b/setup.py index cbd7fcca..40bfa7e8 100755 --- a/setup.py +++ b/setup.py @@ -71,7 +71,7 @@ battery status. requires=['pyudev (>= 0.13)', 'gi.repository.GObject (>= 2.0)', 'gi.repository.Gtk (>= 3.0)'], package_dir={'': 'lib'}, - packages=['hidapi', 'logitech_receiver', 'solaar', 'solaar.ui'], + packages=['hidapi', 'logitech_receiver', 'solaar', 'solaar.ui', 'solaar.cli'], data_files=list(_data_files()), scripts=_glob('bin/*'), )