diff --git a/stateengine/StateEngineAction.py b/stateengine/StateEngineAction.py index 36497db3e..d115c289c 100755 --- a/stateengine/StateEngineAction.py +++ b/stateengine/StateEngineAction.py @@ -594,6 +594,7 @@ class SeActionSetItem(SeActionBase): def __init__(self, abitem, name: str): super().__init__(abitem, name) self.__item = None + self.__eval_item = None self.__status = None self.__delta = 0 self.__value = StateEngineValue.SeValue(self._abitem, "value") @@ -606,6 +607,7 @@ def __repr__(self): def _getitem_fromeval(self): if self.__item is None: return + self.__eval_item = self.__item self.__item, self.__value, self.__mindelta, _issue = self.check_getitem_fromeval(self.__item, self.__value, self.__mindelta) if self.__item is None: @@ -643,8 +645,6 @@ def write_to_logger(self): self._log_debug("item: {0}", self.__item.property.path) else: self._log_debug("item is not defined! Check log file.") - if self.__status is not None: - self._log_debug("status: {0}", self.__status.property.path) self.__mindelta.write_to_logger() self.__value.write_to_logger() @@ -710,6 +710,7 @@ def _execute_set_add_remove(self, state, actionname, namevar, repeat_text, item, self.update_webif_actionstatus(state, re.findall(pat, actionname)[0], 'True') # noinspection PyCallingNonCallable item(value, caller=self._caller, source=source) + self.__item = self.__eval_item def get(self): orig_item = self.__item @@ -733,6 +734,7 @@ def get(self): value = None except Exception: value = None + self.__item = orig_item mindelta = self.__mindelta.get() if mindelta is None: result = {'function': str(self._function), 'item': item, 'item_from_eval': item_from_eval, @@ -979,6 +981,7 @@ class SeActionForceItem(SeActionBase): def __init__(self, abitem, name: str): super().__init__(abitem, name) self.__item = None + self.__eval_item = None self.__status = None self.__value = StateEngineValue.SeValue(self._abitem, "value") self.__mindelta = StateEngineValue.SeValue(self._abitem, "mindelta") @@ -1045,6 +1048,7 @@ def _can_execute(self, state): def _getitem_fromeval(self): if self.__item is None: return + self.__eval_item = self.__item self.__item, self.__value, self.__mindelta, _issue = self.check_getitem_fromeval(self.__item, self.__value, self.__mindelta) if self.__item is None: @@ -1103,6 +1107,7 @@ def real_execute(self, state, actionname: str, namevar: str = "", repeat_text: s self.update_webif_actionstatus(state, self._name, 'True') # noinspection PyCallingNonCallable self.__item(value, caller=self._caller, source=source) + self.__item = self.__eval_item def get(self): orig_item = self.__item @@ -1126,6 +1131,7 @@ def get(self): value = None except Exception: value = None + self.__item = orig_item result = {'function': str(self._function), 'item': item, 'item_from_eval': item_from_eval, 'value': value, 'conditionset': str(self.conditionset.get()), 'previousconditionset': str(self.previousconditionset.get()), 'previousstate_conditionset': str(self.previousstate_conditionset.get()), 'actionstatus': {}} diff --git a/stateengine/StateEngineCondition.py b/stateengine/StateEngineCondition.py index ce3dd0e75..2e1993b55 100755 --- a/stateengine/StateEngineCondition.py +++ b/stateengine/StateEngineCondition.py @@ -68,12 +68,16 @@ def __repr__(self): def check_items(self, check, value=None, item_state=None): item_issue, status_issue, eval_issue, status_eval_issue = None, None, None, None item_value, status_value, eval_value, status_eval_value = None, None, None, None + if check == "attribute": + _orig_value = value + else: + _orig_value = None if check == "se_item" or (check == "attribute" and self.__item is None and self.__eval is None): if value is None: value = StateEngineTools.find_attribute(self._sh, item_state, "se_item_" + self.__name) - if value is not None: + if isinstance(value, str): match = re.match(r'^(.*):', value) - if isinstance(value, str) and value.startswith("eval:"): + if value.startswith("eval:"): _, _, value = value.partition(":") self.__eval = value self.__item = None @@ -87,9 +91,11 @@ def check_items(self, check, value=None, item_state=None): self.__item = value item_value = value if check == "se_status" or (check == "attribute" and self.__status is None and self.__status_eval is None): + if check == "attribute": + value = _orig_value if value is None: value = StateEngineTools.find_attribute(self._sh, item_state, "se_status_" + self.__name) - if value is not None: + if isinstance(value, str): match = re.match(r'^(.*):', value) if isinstance(value, str) and value.startswith("eval:"): _, _, value = value.partition(":") @@ -108,7 +114,7 @@ def check_items(self, check, value=None, item_state=None): if check == "se_eval" or (check == "attribute" and self.__eval is None): if value is None: value = StateEngineTools.find_attribute(self._sh, item_state, "se_eval_" + self.__name) - if value is not None: + if isinstance(value, str): match = re.match(r'^(.*):', value) if value.startswith("eval:"): _, _, value = value.partition("eval:") @@ -120,9 +126,11 @@ def check_items(self, check, value=None, item_state=None): self.__eval = value eval_value = value if check == "se_status_eval" or (check == "attribute" and self.__status_eval is None): + if check == "attribute": + value = _orig_value if value is None: value = StateEngineTools.find_attribute(self._sh, item_state, "se_status_eval_" + self.__name) - if value is not None: + if isinstance(value, str): match = re.match(r'^(.*):', value) if value.startswith("eval:"): _, _, value = value.partition("eval:") @@ -454,7 +462,7 @@ def __convert(convert_value, convert_current): else: self.__value.set_cast(StateEngineTools.cast_str) convert_value = StateEngineTools.cast_str(convert_value) - convert_current = StateEngineTools.cast_str(convert_value) + convert_current = StateEngineTools.cast_str(convert_current) if not type(_oldvalue) == type(convert_value): self._log_debug("Value {} was type {} and therefore not the same" " type as item value {}. It got converted to {}.", @@ -469,7 +477,6 @@ def __convert(convert_value, convert_current): self.__updatedbynegate if valuetype == "updatedby" else\ self.__triggeredbynegate if valuetype == "triggeredby" else\ self.__negate - if isinstance(value, list): text = "Condition '{0}': {1}={2} negate={3} current={4}" _key = ['{}'.format(state.id), 'conditionsets', '{}'.format(self._abitem.get_variable('current.conditionset_name')), '{}'.format(self.__name), 'current', '{}'.format(valuetype)] @@ -850,26 +857,26 @@ def check_eval(eval_or_status_eval): if self.__status is not None: # noinspection PyUnusedLocal - self._log_debug("Trying to get {} of status item {}", eval_type, self.__status) + self._log_debug("Trying to get {} of status item {}", eval_type, self.__status.property.path) 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.__status_eval is not None: + self._log_debug("Trying to get {} of statuseval {}", eval_type, self.__status_eval) + return_value = check_eval(self.__status_eval) + return return_value elif self.__item is not None: # noinspection PyUnusedLocal - self._log_debug("Trying to get {} of item {}", eval_type, self.__item) + self._log_debug("Trying to get {} of item {}", eval_type, self.__item.property.path) 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.__status_eval is not None: - self._log_debug("Trying to get {} of statuseval {}", eval_type, self.__status_eval) - return_value = check_eval(self.__status_eval) - return return_value elif self.__eval is not None: - self._log_debug("Trying to get {} of statuseval {}", eval_type, self.__eval) + self._log_debug("Trying to get {} of eval {}", eval_type, self.__eval) return_value = check_eval(self.__eval) return return_value diff --git a/stateengine/StateEngineConditionSet.py b/stateengine/StateEngineConditionSet.py index 8b9d42c7f..fa9f46837 100755 --- a/stateengine/StateEngineConditionSet.py +++ b/stateengine/StateEngineConditionSet.py @@ -89,6 +89,9 @@ def update(self, item, grandparent_item): if item is not None: for attribute in item.conf: func, name = StateEngineTools.partition_strip(attribute, "_") + if name.startswith("eval_"): + _, name = StateEngineTools.partition_strip(name, "_") + func = f"{func}_eval" if name == "": continue try: @@ -97,6 +100,7 @@ def update(self, item, grandparent_item): self.__conditions[name] = StateEngineCondition.SeCondition(self._abitem, name) issue = self.__conditions[name].set(func, item.conf[attribute]) self.__conditions.move_to_end(name, last=True) + if issue not in [[], None, [None]]: self.__unused_attributes.update({name: {'attribute': attribute, 'issue': issue}}) elif name not in self.__used_attributes.keys(): @@ -110,6 +114,9 @@ def update(self, item, grandparent_item): for attribute in grandparent_item.conf: func, name = StateEngineTools.partition_strip(attribute, "_") + if name.startswith("eval_"): + _, name = StateEngineTools.partition_strip(name, "_") + func = f"{func}_eval" if name == "": continue cond1 = name not in self.__used_attributes.keys() diff --git a/stateengine/StateEngineTools.py b/stateengine/StateEngineTools.py index f47861f2a..65e5cc5c0 100755 --- a/stateengine/StateEngineTools.py +++ b/stateengine/StateEngineTools.py @@ -262,7 +262,10 @@ def cast_time(value): # attribute: name of attribute to find def find_attribute(smarthome, base_item, attribute, recursion_depth=0): # 1: parent of given item could have attribute - parent_item = base_item.return_parent() + try: + parent_item = base_item.return_parent() + except Exception: + return None try: _parent_conf = parent_item.conf if parent_item is not None and attribute in _parent_conf: