rules: add rule condition for checking device settings

This commit is contained in:
Peter F. Patel-Schneider 2022-02-26 12:56:26 -05:00
parent eedf4bfffb
commit 00176a1df8
2 changed files with 84 additions and 2 deletions

View File

@ -412,6 +412,43 @@ class Report(Condition):
return {'Report': self.report} return {'Report': self.report}
# Setting(device, setting, [key], value...)
class Setting(Condition):
def __init__(self, args):
if not (isinstance(args, list) and len(args) > 2):
_log.warn('rule Setting argument not list with minimum length 3: %s', args)
self.args = []
else:
self.args = args
def __str__(self):
return 'Setting: ' + ' '.join([str(a) for a in self.args])
def evaluate(self, report, notification, device, status, last_result):
import solaar.ui.window as _window
if len(self.args) < 3:
return None
dev = _window.find_device(self.args[0]) if self.args[0] is not None else device
if dev is None:
_log.warn('Setting condition: device %s is not known', self.args[0])
return False
setting = next((s for s in dev.settings if s.name == self.args[1]), None)
if setting is None:
_log.warn('Setting condition: setting %s is not the name of a setting for %s', self.args[1], dev.name)
return None
# should the value argument be checked to be sure it is acceptable?? needs to be careful about boolean toggle
# TODO add compare methods for more validators
try:
result = setting.compare(self.args[2:], setting.read())
except Exception as e:
_log.warn('Setting condition: error when checking setting %s: %s', self.args, e)
result = False
return result
def data(self):
return {'Setting': self.args[:]}
MODIFIERS = { MODIFIERS = {
'Shift': int(Gdk.ModifierType.SHIFT_MASK), 'Shift': int(Gdk.ModifierType.SHIFT_MASK),
'Control': int(Gdk.ModifierType.CONTROL_MASK), 'Control': int(Gdk.ModifierType.CONTROL_MASK),
@ -635,7 +672,7 @@ class KeyPress(Action):
def evaluate(self, feature, notification, device, status, last_result): def evaluate(self, feature, notification, device, status, last_result):
current = gkeymap.get_modifier_state() current = gkeymap.get_modifier_state()
if _log.isEnabledFor(_INFO): if _log.isEnabledFor(_INFO):
_log.info('KeyPress action: %s, modifiers %s %s', self.key_symbols, current, [hex(k) for k in self.keys]) _log.info('KeyPress action: %s, modifiers %s %s', self.key_symbols, current, [hex(k) for k in self.key_symbols])
self.keyDown(self.key_symbols, current) self.keyDown(self.key_symbols, current)
self.keyUp(reversed(self.key_symbols), current) self.keyUp(reversed(self.key_symbols), current)
if x11: if x11:
@ -819,6 +856,7 @@ COMPONENTS = {
'MouseProcess': MouseProcess, 'MouseProcess': MouseProcess,
'Feature': Feature, 'Feature': Feature,
'Report': Report, 'Report': Report,
'Setting': Setting,
'Modifiers': Modifiers, 'Modifiers': Modifiers,
'Key': Key, 'Key': Key,
'Test': Test, 'Test': Test,

View File

@ -67,6 +67,11 @@ class Validator:
def to_string(cls, value): def to_string(cls, value):
return (str(value)) return (str(value))
def compare(self, args, current):
if len(args) != 1:
return False
return args[0] == current
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')
@ -316,6 +321,9 @@ class Setting:
def acceptable(self, args, current): def acceptable(self, args, current):
return self._validator.acceptable(args, current) if self._validator else None return self._validator.acceptable(args, current) if self._validator else None
def compare(self, args, current):
return self._validator.compare(args, current) if self._validator else None
def apply(self): def apply(self):
assert hasattr(self, '_value') assert hasattr(self, '_value')
assert hasattr(self, '_device') assert hasattr(self, '_device')
@ -789,6 +797,14 @@ class BitFieldValidator(Validator):
val = bool_or_toggle(current[str(int(key))], args[1]) val = bool_or_toggle(current[str(int(key))], args[1])
return None if val is None else [str(int(key)), val] return None if val is None else [str(int(key)), val]
def compare(self, args, current):
if len(args) != 2:
return False
key = next((key for key in self.options if key == args[0]), None)
if key is None:
return False
return args[1] == current[str(int(key))]
class BitFieldWithOffsetAndMaskValidator(Validator): class BitFieldWithOffsetAndMaskValidator(Validator):
__slots__ = ('byte_count', 'options', '_option_from_key', '_mask_from_offset', '_option_from_offset_mask') __slots__ = ('byte_count', 'options', '_option_from_key', '_mask_from_offset', '_option_from_offset_mask')
@ -878,7 +894,15 @@ class BitFieldWithOffsetAndMaskValidator(Validator):
if key is None: if key is None:
return None return None
val = bool_or_toggle(current[str(int(key))], args[1]) val = bool_or_toggle(current[str(int(key))], args[1])
return None if val is None else [str(key), val] return None if val is None else [str(int(key)), val]
def compare(self, args, current):
if len(args) != 2:
return False
key = next((option.id for option in self.options if option.as_int() == args[0]), None)
if key is None:
return False
return args[1] == current[str(int(key))]
class ChoicesValidator(Validator): class ChoicesValidator(Validator):
@ -1026,6 +1050,14 @@ class ChoicesMapValidator(ChoicesValidator):
choice = next((item for item in choices if item == args[1]), None) choice = next((item for item in choices if item == args[1]), None)
return [str(int(key)), int(choice)] if choice is not None else None return [str(int(key)), int(choice)] if choice is not None else None
def compare(self, args, current):
if len(args) != 2:
return False
key = next((key for key in self.choices if key == int(args[0])), None)
if key is None:
return False
return args[1] == current[str(int(key))]
class RangeValidator(Validator): class RangeValidator(Validator):
kind = KIND.range kind = KIND.range
@ -1071,6 +1103,14 @@ class RangeValidator(Validator):
# None if len(args) != 1 or type(arg) != int or arg < self.min_value or arg > self.max_value else args) # None if len(args) != 1 or type(arg) != int or arg < self.min_value or arg > self.max_value else args)
return None if len(args) != 1 or type(arg) != int or arg < self.min_value or arg > self.max_value else args return None if len(args) != 1 or type(arg) != int or arg < self.min_value or arg > self.max_value else args
def compare(self, args, current):
if len(args) == 1:
return args[0] == current
elif len(args) == 2:
return args[0] <= current and current <= args[1]
else:
return False
class MultipleRangeValidator(Validator): class MultipleRangeValidator(Validator):
@ -1157,6 +1197,10 @@ class MultipleRangeValidator(Validator):
return None return None
return [str(int(item)), {**args[1]}] return [str(int(item)), {**args[1]}]
def commpare(self, args, current):
_log.warn('compare not implemented for multiple range settings')
return False
class ActionSettingRW: class ActionSettingRW:
"""Special RW class for settings that turn on and off special processing when a key or button is depressed""" """Special RW class for settings that turn on and off special processing when a key or button is depressed"""