From 2d83a58503f0af101462e094aa7f44ebffeacebb Mon Sep 17 00:00:00 2001 From: Onkel Andy Date: Fri, 4 Aug 2023 12:23:12 +0200 Subject: [PATCH] stateengine plugin: introduce triggeredby condition functionality (equivalent to updatedby and changedby) --- stateengine/StateEngineCondition.py | 45 +++++++++++++++++++++++-- stateengine/StateEngineWebif.py | 9 +++-- stateengine/user_doc/05_bedingungen.rst | 22 ++++++++++++ 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/stateengine/StateEngineCondition.py b/stateengine/StateEngineCondition.py index 7088a637c..ce92edca3 100755 --- a/stateengine/StateEngineCondition.py +++ b/stateengine/StateEngineCondition.py @@ -54,8 +54,10 @@ def __init__(self, abitem, name: str): self.__agemax = StateEngineValue.SeValue(self._abitem, "agemax") self.__changedby = StateEngineValue.SeValue(self._abitem, "changedby", True) self.__updatedby = StateEngineValue.SeValue(self._abitem, "updatedby", True) + self.__triggeredby = StateEngineValue.SeValue(self._abitem, "triggeredby", True) self.__changedbynegate = None self.__updatedbynegate = None + self.__triggeredbynegate = None self.__agenegate = None self.__error = None self.__itemClass = Item @@ -65,7 +67,7 @@ def __repr__(self): # set a certain function to a given value # func: Function to set ('item', 'eval', 'value', 'min', 'max', 'negate', 'changedby', 'updatedby', - # 'changedbynegate', 'updatedbynegate', 'agemin', 'agemax' or 'agenegate') + # 'triggeredby','changedbynegate', 'updatedbynegate', 'triggeredbynegate','agemin', 'agemax' or 'agenegate') # value: Value for function def set(self, func, value): if func == "se_item": @@ -100,10 +102,14 @@ def set(self, func, value): self.__changedby.set(value, self.__name) elif func == "se_updatedby": self.__updatedby.set(value, self.__name) + elif func == "se_triggeredby": + self.__triggeredby.set(value, self.__name) elif func == "se_changedbynegate": self.__changedbynegate = value elif func == "se_updatedbynegate": self.__updatedbynegate = value + elif func == "se_triggeredbynegate": + self.__triggeredbynegate = value elif func == "se_negate": self.__negate = value elif func == "se_agenegate": @@ -131,6 +137,7 @@ def get(self): 'max': str(self.__max), 'agemin': str(self.__agemin), 'agemax': str(self.__agemax), 'negate': str(self.__negate), 'agenegate': str(self.__agenegate), 'changedby': str(self.__changedby), 'updatedby': str(self.__updatedby), + 'triggeredby': str(self.__triggeredby), 'triggeredbynegate': str(self.__triggeredbynegate), 'changedbynegate': str(self.__changedbynegate), 'updatedbynegate': str(self.__updatedbynegate)} return result @@ -142,7 +149,7 @@ def complete(self, item_state): # check if it is possible to complete this condition if self.__min.is_empty() and self.__max.is_empty() and self.__value.is_empty() \ and self.__agemin.is_empty() and self.__agemax.is_empty() \ - and self.__changedby.is_empty() and self.__updatedby.is_empty(): + and self.__changedby.is_empty() and self.__updatedby.is_empty() and self.__triggeredby.is_empty(): return False # set 'eval' for some known conditions if item and eval are not set, yet @@ -226,6 +233,9 @@ def complete(self, item_state): if (self.__item is not None or self.__status is not None or self.__eval is not None)\ and not self.__updatedby.is_empty() and self.__updatedbynegate is None: self.__updatedbynegate = False + if (self.__item is not None or self.__status is not None or self.__eval is not None)\ + and not self.__triggeredby.is_empty() and self.__triggeredbynegate is None: + self.__triggeredbynegate = False # cast stuff try: @@ -271,6 +281,9 @@ def check(self): if not self.__check_updatedby(): self._log_decrease_indent() return False + if not self.__check_triggeredby(): + self._log_decrease_indent() + return False if not self.__check_changedby(): self._log_decrease_indent() return False @@ -317,6 +330,9 @@ def write_to_logger(self, log_level=StateEngineDefaults.log_level): self.__updatedby.write_to_logger(log_level) if self.__updatedbynegate is not None and not self.__updatedby.is_empty(): self._log_info("updatedby negate: {0}", self.__updatedbynegate) + self.__triggeredby.write_to_logger(log_level) + if self.__updatedbynegate is not None and not self.__triggeredby.is_empty(): + self._log_info("triggeredby negate: {0}", self.__triggeredbynegate) # Cast 'value', 'min' and 'max' using given cast function # cast_func: cast function to use @@ -334,6 +350,9 @@ def __cast_all(self, cast_func): self.__updatedby.set_cast(StateEngineTools.cast_str) if self.__updatedbynegate is not None: self.__updatedbynegate = StateEngineTools.cast_bool(self.__updatedbynegate) + self.__triggeredby.set_cast(StateEngineTools.cast_str) + if self.__triggeredbynegate is not None: + self.__triggeredbynegate = StateEngineTools.cast_bool(self.__triggeredbynegate) if self.__agenegate is not None: self.__agenegate = StateEngineTools.cast_bool(self.__agenegate) @@ -376,9 +395,11 @@ def __convert(convert_value, convert_current): current = self.__get_current(eval_type='changedby') if valuetype == "changedby" else\ self.__get_current(eval_type='updatedby') if valuetype == "updatedby" else\ + self.__get_current(eval_type='triggeredby') if valuetype == "triggeredby" else\ self.__get_current(eval_type='value') negate = self.__changedbynegate if valuetype == "changedby" else\ self.__updatedbynegate if valuetype == "updatedby" else\ + self.__triggeredbynegate if valuetype == "triggeredby" else\ self.__negate if isinstance(value, list): @@ -557,6 +578,23 @@ def __check_changedby(self): finally: self._log_decrease_indent() + # Check if triggeredby conditions match + def __check_triggeredby(self): + try: + if not self.__triggeredby.is_empty(): + # 'updatedby' is given. + triggeredby = self.__triggeredby.get() + triggeredby = StateEngineTools.flatten_list(triggeredby) + return self.__change_update_value(triggeredby, "triggeredby") + else: + self._log_increase_indent() + return True + + except Exception as ex: + self._log_warning("Problem checking triggeredby {}", ex) + finally: + self._log_decrease_indent() + # Check if updatedby conditions match def __check_updatedby(self): try: @@ -663,6 +701,7 @@ def __get_current(self, eval_type='value'): return self.__status.property.last_change_age if eval_type == 'age' else\ self.__status.property.last_change_by if eval_type == 'changedby' else\ self.__status.property.last_update_by if eval_type == 'updatedby' else\ + self.__status.property.last_trigger_by if eval_type == 'triggeredby' else\ self.__status.property.value elif self.__item is not None: # noinspection PyUnusedLocal @@ -670,6 +709,7 @@ def __get_current(self, eval_type='value'): return self.__item.property.last_change_age if eval_type == 'age' else\ self.__item.property.last_change_by if eval_type == 'changedby' else\ self.__item.property.last_update_by if eval_type == 'updatedby' else\ + self.__item.property.last_trigger_by if eval_type == 'triggeredby' else\ self.__item.property.value if self.__eval is not None: # noinspection PyUnusedLocal @@ -686,6 +726,7 @@ def __get_current(self, eval_type='value'): value = eval(self.__eval).property.last_change_age if eval_type == 'age' else \ eval(self.__eval).property.last_change_by if eval_type == 'changedby' else \ eval(self.__eval).property.last_update_by if eval_type == 'updatedby' else \ + eval(self.__eval).property.last_trigger_by if eval_type == 'triggeredby' else \ eval(self.__eval).property.value else: value = eval(self.__eval) diff --git a/stateengine/StateEngineWebif.py b/stateengine/StateEngineWebif.py index 75b6dcd06..7e130658a 100755 --- a/stateengine/StateEngineWebif.py +++ b/stateengine/StateEngineWebif.py @@ -173,6 +173,7 @@ def _conditionlabel(self, state, conditionset): agemax_none = condition_dict.get('agemax') == 'None' changedby_none = condition_dict.get('changedby') == 'None' updatedby_none = condition_dict.get('updatedby') == 'None' + triggeredby_none = condition_dict.get('triggeredby') == 'None' for compare in condition_dict: cond1 = not condition_dict.get(compare) == 'None' @@ -182,8 +183,9 @@ def _conditionlabel(self, state, conditionset): cond5 = not compare == 'agenegate' cond6 = not compare == 'changedbynegate' cond7 = not compare == 'updatedbynegate' - cond8 = not compare == 'status' - if cond1 and cond2 and cond3 and cond4 and cond5 and cond6 and cond7 and cond8: + cond8 = not compare == 'triggeredbynegate' + cond9 = not compare == 'status' + if cond1 and cond2 and cond3 and cond4 and cond5 and cond6 and cond7 and cond8 and cond9: conditionlist += ''.format(compare, condition_dict.get(compare)) if not status_none: textlength = len(str(condition_dict.get('status'))) @@ -223,6 +225,9 @@ def _conditionlabel(self, state, conditionset): else "not updated by" if (not updatedby_none and compare == "updatedby" and condition_dict.get('updatedbynegate') == 'True')\ else "updated by" if not updatedby_none and compare == "updatedby"\ + else "not triggered by" if (not triggeredby_none and compare == "triggeredby" + and condition_dict.get('triggeredbynegate') == 'True')\ + else "triggered by" if not triggeredby_none and compare == "triggeredby"\ else "!=" if (not value_none and compare == "value" and condition_dict.get('negate') == 'True')\ else "==" diff --git a/stateengine/user_doc/05_bedingungen.rst b/stateengine/user_doc/05_bedingungen.rst index 3ac5ac8e0..c72b2e312 100755 --- a/stateengine/user_doc/05_bedingungen.rst +++ b/stateengine/user_doc/05_bedingungen.rst @@ -255,6 +255,28 @@ Die Werte(liste) kann auch durch ``se_changedbynegate_`` negiert se_changedbynegate_: True|False +**Triggerung des Items durch** + +.. code-block:: yaml + + se_triggeredby_: [Wert] + +Die Bedingung ist erfüllt, wenn das Item durch den angegebenen Wert bzw. +einen der angegebenen Werte getriggert wurde. Dies kann relevant werden, +um herauszufinden, wodurch ein Item mit einem eval-Attribut getriggert wurde, +unabhängig davon, ob sich daraus eine Wertänderung ergibt oder nicht. +Hier bietet es sich an, den Wert als Regular Expression mittels +``se_triggeredby_: regex:StateEngine Plugin`` zu definieren. +Die Werte(liste) kann auch durch ``se_triggeredbynegate_`` negiert werden. + +.. code-block:: yaml + + se_triggeredby_: + - [Wert1] + - [Wert2] + - regex:[WertN] + + se_triggeredbynegate_: True|False **Mindestalter**