cli: nicer output of settings in solaar show and solaar config
This commit is contained in:
parent
fed9a26cb6
commit
f938d3430e
|
@ -63,6 +63,10 @@ class Validator:
|
||||||
def build(cls, setting_class, device, **kwargs):
|
def build(cls, setting_class, device, **kwargs):
|
||||||
return cls(**kwargs)
|
return cls(**kwargs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def to_string(cls, value):
|
||||||
|
return (str(value))
|
||||||
|
|
||||||
|
|
||||||
class BooleanValidator(Validator):
|
class BooleanValidator(Validator):
|
||||||
__slots__ = ('true_value', 'false_value', 'read_skip_byte_count', 'write_prefix_bytes', 'mask', 'needs_current_value')
|
__slots__ = ('true_value', 'false_value', 'read_skip_byte_count', 'write_prefix_bytes', 'mask', 'needs_current_value')
|
||||||
|
@ -222,6 +226,9 @@ class Setting:
|
||||||
assert cls.kind is None or cls.kind & validator.kind != 0
|
assert cls.kind is None or cls.kind & validator.kind != 0
|
||||||
return cls(device, rw, validator)
|
return cls(device, rw, validator)
|
||||||
|
|
||||||
|
def val_to_string(self, value):
|
||||||
|
return self._validator.to_string(value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def choices(self):
|
def choices(self):
|
||||||
assert hasattr(self, '_value')
|
assert hasattr(self, '_value')
|
||||||
|
@ -745,6 +752,13 @@ class BitFieldValidator(Validator):
|
||||||
assert (isinstance(byte_count, int) and byte_count >= self.byte_count)
|
assert (isinstance(byte_count, int) and byte_count >= self.byte_count)
|
||||||
self.byte_count = byte_count
|
self.byte_count = byte_count
|
||||||
|
|
||||||
|
def to_string(self, value):
|
||||||
|
def element_to_string(key, val):
|
||||||
|
k = next((k for k in self.options if int(key) == k), None)
|
||||||
|
return str(k) + ':' + str(val) if k is not None else '?'
|
||||||
|
|
||||||
|
return '{' + ', '.join([element_to_string(k, value[k]) for k in value]) + '}'
|
||||||
|
|
||||||
def validate_read(self, reply_bytes):
|
def validate_read(self, reply_bytes):
|
||||||
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}
|
||||||
|
@ -894,6 +908,9 @@ class ChoicesValidator(Validator):
|
||||||
assert self._byte_count + self._read_skip_byte_count <= 14
|
assert self._byte_count + self._read_skip_byte_count <= 14
|
||||||
assert self._byte_count + len(self._write_prefix_bytes) <= 14
|
assert self._byte_count + len(self._write_prefix_bytes) <= 14
|
||||||
|
|
||||||
|
def to_string(self, value):
|
||||||
|
return str(self.choices[value]) if isinstance(value, int) else str(value)
|
||||||
|
|
||||||
def validate_read(self, reply_bytes):
|
def validate_read(self, reply_bytes):
|
||||||
reply_value = _bytes2int(reply_bytes[self._read_skip_byte_count:self._read_skip_byte_count + self._byte_count])
|
reply_value = _bytes2int(reply_bytes[self._read_skip_byte_count:self._read_skip_byte_count + self._byte_count])
|
||||||
valid_value = self.choices[reply_value]
|
valid_value = self.choices[reply_value]
|
||||||
|
@ -974,6 +991,13 @@ class ChoicesMapValidator(ChoicesValidator):
|
||||||
assert self._byte_count + self._read_skip_byte_count + self._key_byte_count <= 14
|
assert self._byte_count + self._read_skip_byte_count + self._key_byte_count <= 14
|
||||||
assert self._byte_count + len(self._write_prefix_bytes) + self._key_byte_count <= 14
|
assert self._byte_count + len(self._write_prefix_bytes) + self._key_byte_count <= 14
|
||||||
|
|
||||||
|
def to_string(self, value):
|
||||||
|
def element_to_string(key, val):
|
||||||
|
k, c = next(((k, c) for k, c in self.choices.items() if int(key) == k), (None, None))
|
||||||
|
return str(k) + ':' + str(c[val]) if k is not None else '?'
|
||||||
|
|
||||||
|
return '{' + ', '.join([element_to_string(k, value[k]) for k in sorted(value)]) + '}'
|
||||||
|
|
||||||
def validate_read(self, reply_bytes, key):
|
def validate_read(self, reply_bytes, key):
|
||||||
start = self._key_byte_count + self._read_skip_byte_count
|
start = self._key_byte_count + self._read_skip_byte_count
|
||||||
end = start + self._byte_count
|
end = start + self._byte_count
|
||||||
|
|
|
@ -41,7 +41,36 @@ def _print_setting(s, verbose=True):
|
||||||
if value is None:
|
if value is None:
|
||||||
print(s.name, '= ? (failed to read from device)')
|
print(s.name, '= ? (failed to read from device)')
|
||||||
else:
|
else:
|
||||||
print(s.name, '= %s' % value)
|
print(s.name, '=', s.val_to_string(value))
|
||||||
|
|
||||||
|
|
||||||
|
def _print_setting_keyed(s, key, verbose=True):
|
||||||
|
print('#', s.label)
|
||||||
|
if verbose:
|
||||||
|
if s.description:
|
||||||
|
print('#', s.description.replace('\n', ' '))
|
||||||
|
if s.kind == _settings.KIND.multiple_toggle:
|
||||||
|
k = next((k for k in s._labels if key == k), None)
|
||||||
|
if k is None:
|
||||||
|
print(s.name, '=? (key not found)')
|
||||||
|
else:
|
||||||
|
print('# possible values: on/true/t/yes/y/1 or off/false/f/no/n/0 or Toggle/~')
|
||||||
|
value = s.read(cached=False)
|
||||||
|
if value is None:
|
||||||
|
print(s.name, '= ? (failed to read from device)')
|
||||||
|
else:
|
||||||
|
print(s.name, s.val_to_string({k: value[str(int(k))]}))
|
||||||
|
elif s.kind == _settings.KIND.map_choice:
|
||||||
|
k = next((k for k in s.choices.keys() if key == k), None)
|
||||||
|
if k is None:
|
||||||
|
print(s.name, '=? (key not found)')
|
||||||
|
else:
|
||||||
|
print('# possible values: one of [', ', '.join(str(v) for v in s.choices[k]), ']')
|
||||||
|
value = s.read(cached=False)
|
||||||
|
if value is None:
|
||||||
|
print(s.name, '= ? (failed to read from device)')
|
||||||
|
else:
|
||||||
|
print(s.name, s.val_to_string({k: value[str(int(k))]}))
|
||||||
|
|
||||||
|
|
||||||
def to_int(s):
|
def to_int(s):
|
||||||
|
@ -66,7 +95,7 @@ def select_choice(value, choices, setting, key):
|
||||||
elif lvalue in ('higher', 'lower'):
|
elif lvalue in ('higher', 'lower'):
|
||||||
old_value = setting.read() if key is None else setting.read_key(key)
|
old_value = setting.read() if key is None else setting.read_key(key)
|
||||||
if old_value is None:
|
if old_value is None:
|
||||||
raise Exception("%s: could not read current value'" % setting.name)
|
raise Exception('%s: could not read current value' % setting.name)
|
||||||
if lvalue == 'lower':
|
if lvalue == 'lower':
|
||||||
lower_values = choices[:old_value]
|
lower_values = choices[:old_value]
|
||||||
value = lower_values[-1] if lower_values else choices[:][0]
|
value = lower_values[-1] if lower_values else choices[:][0]
|
||||||
|
@ -82,9 +111,9 @@ def select_choice(value, choices, setting, key):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def select_toggle(value, setting):
|
def select_toggle(value, setting, key=None):
|
||||||
if value.lower() in ('toggle', '~'):
|
if value.lower() in ('toggle', '~'):
|
||||||
value = not setting.read()
|
value = not (setting.read() if key is None else setting.read()[str(int(key))])
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
value = bool(int(value))
|
value = bool(int(value))
|
||||||
|
@ -147,9 +176,10 @@ def run(receivers, args, find_receiver, find_device):
|
||||||
return
|
return
|
||||||
|
|
||||||
result, message, value = set(dev, setting, args)
|
result, message, value = set(dev, setting, args)
|
||||||
print(message)
|
if message is not None:
|
||||||
if result is None:
|
print(message)
|
||||||
raise Exception("%s: failed to set value '%s' [%r]" % (setting.name, str(value), value))
|
if result is None:
|
||||||
|
raise Exception("%s: failed to set value '%s' [%r]" % (setting.name, str(value), value))
|
||||||
|
|
||||||
|
|
||||||
def set(dev, setting, args):
|
def set(dev, setting, args):
|
||||||
|
@ -169,6 +199,9 @@ def set(dev, setting, args):
|
||||||
result = setting.write(value)
|
result = setting.write(value)
|
||||||
|
|
||||||
elif setting.kind == _settings.KIND.map_choice:
|
elif setting.kind == _settings.KIND.map_choice:
|
||||||
|
if args.extra_subkey is None:
|
||||||
|
_print_setting_keyed(setting, args.value_key)
|
||||||
|
return (None, None, None)
|
||||||
key = args.value_key
|
key = args.value_key
|
||||||
ikey = to_int(key)
|
ikey = to_int(key)
|
||||||
k = next((k for k in setting.choices.keys() if key == k), None)
|
k = next((k for k in setting.choices.keys() if key == k), None)
|
||||||
|
@ -182,6 +215,9 @@ def set(dev, setting, args):
|
||||||
result = setting.write_key_value(int(k), value)
|
result = setting.write_key_value(int(k), value)
|
||||||
|
|
||||||
elif setting.kind == _settings.KIND.multiple_toggle:
|
elif setting.kind == _settings.KIND.multiple_toggle:
|
||||||
|
if args.extra_subkey is None:
|
||||||
|
_print_setting_keyed(setting, args.value_key)
|
||||||
|
return (None, None, None)
|
||||||
key = args.value_key
|
key = args.value_key
|
||||||
all_keys = getattr(setting, 'choices_universe', None)
|
all_keys = getattr(setting, 'choices_universe', None)
|
||||||
ikey = all_keys[int(key) if key.isdigit() else key] if isinstance(all_keys, _NamedInts) else to_int(key)
|
ikey = all_keys[int(key) if key.isdigit() else key] if isinstance(all_keys, _NamedInts) else to_int(key)
|
||||||
|
@ -189,18 +225,18 @@ def set(dev, setting, args):
|
||||||
if k is None and ikey is not None:
|
if k is None and ikey is not None:
|
||||||
k = next((k for k in setting._labels if ikey == k), None)
|
k = next((k for k in setting._labels if ikey == k), None)
|
||||||
if k is not None:
|
if k is not None:
|
||||||
value = select_toggle(args.extra_subkey, setting)
|
value = select_toggle(args.extra_subkey, setting, key=k)
|
||||||
else:
|
else:
|
||||||
raise Exception("%s: key '%s' not in setting" % (setting.name, key))
|
raise Exception("%s: key '%s' not in setting" % (setting.name, key))
|
||||||
message = 'Setting %s key %r to %r' % (setting.name, k, value)
|
message = 'Setting %s key %r to %r' % (setting.name, k, value)
|
||||||
result = setting.write_key_value(int(k), value)
|
result = setting.write_key_value(str(int(k)), value)
|
||||||
|
|
||||||
elif setting.kind == _settings.KIND.multiple_range:
|
elif setting.kind == _settings.KIND.multiple_range:
|
||||||
|
if args.extra_subkey is None:
|
||||||
|
raise Exception('%s: setting needs both key and value to set' % (setting.name))
|
||||||
key = args.value_key
|
key = args.value_key
|
||||||
all_keys = getattr(setting, 'choices_universe', None)
|
all_keys = getattr(setting, 'choices_universe', None)
|
||||||
ikey = all_keys[int(key) if key.isdigit() else key] if isinstance(all_keys, _NamedInts) else to_int(key)
|
ikey = all_keys[int(key) if key.isdigit() else key] if isinstance(all_keys, _NamedInts) else to_int(key)
|
||||||
if args.extra_subkey is None:
|
|
||||||
raise Exception('%s: setting needs a subkey' % (setting.name))
|
|
||||||
if args.extra2 is None or to_int(args.extra2) is None:
|
if args.extra2 is None or to_int(args.extra2) is None:
|
||||||
raise Exception('%s: setting needs an integer value, not %s' % (setting.name, args.extra2))
|
raise Exception('%s: setting needs an integer value, not %s' % (setting.name, args.extra2))
|
||||||
if not setting._value: # ensure that there are values to look through
|
if not setting._value: # ensure that there are values to look through
|
||||||
|
|
|
@ -171,7 +171,7 @@ def _print_device(dev, num=None):
|
||||||
print(' Provide vertical tuning, trackball')
|
print(' Provide vertical tuning, trackball')
|
||||||
else:
|
else:
|
||||||
print(' No vertical tuning, standard mice')
|
print(' No vertical tuning, standard mice')
|
||||||
if feature == _hidpp20.FEATURE.VERTICAL_SCROLLING:
|
elif feature == _hidpp20.FEATURE.VERTICAL_SCROLLING:
|
||||||
vertical_scrolling_info = _hidpp20.get_vertical_scrolling_info(dev)
|
vertical_scrolling_info = _hidpp20.get_vertical_scrolling_info(dev)
|
||||||
if vertical_scrolling_info:
|
if vertical_scrolling_info:
|
||||||
print(' Roller type: %s' % vertical_scrolling_info['roller'])
|
print(' Roller type: %s' % vertical_scrolling_info['roller'])
|
||||||
|
@ -231,10 +231,11 @@ def _print_device(dev, num=None):
|
||||||
_battery_line(dev)
|
_battery_line(dev)
|
||||||
for setting in dev_settings:
|
for setting in dev_settings:
|
||||||
if setting.feature == feature:
|
if setting.feature == feature:
|
||||||
if setting._device and getattr(setting._device, 'persister',
|
if setting._device and getattr(setting._device, 'persister', None) and \
|
||||||
None) and setting._device.persister.get(setting.name) is not None:
|
setting._device.persister.get(setting.name) is not None:
|
||||||
print(' %s (saved): %s' % (setting.label, setting._device.persister.get(setting.name)))
|
v = setting.val_to_string(setting._device.persister.get(setting.name))
|
||||||
v = setting.read(False)
|
print(' %s (saved): %s' % (setting.label, v))
|
||||||
|
v = setting.val_to_string(setting.read(False))
|
||||||
print(' %s : %s' % (setting.label, v))
|
print(' %s : %s' % (setting.label, v))
|
||||||
|
|
||||||
if dev.online and dev.keys:
|
if dev.online and dev.keys:
|
||||||
|
|
Loading…
Reference in New Issue