From 3cb11cc0473c4bd721da4007faf28337fefe1c85 Mon Sep 17 00:00:00 2001 From: Onkel Andy Date: Tue, 14 May 2024 15:55:06 +0200 Subject: [PATCH 1/9] stateengine plugin: update logging --- stateengine/StateEngineCondition.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stateengine/StateEngineCondition.py b/stateengine/StateEngineCondition.py index ce3dd0e75..a4f0551b2 100755 --- a/stateengine/StateEngineCondition.py +++ b/stateengine/StateEngineCondition.py @@ -850,7 +850,7 @@ 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\ @@ -858,7 +858,7 @@ def check_eval(eval_or_status_eval): self.__status.property.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\ From 31f5fa196e4207576d056580c9102b174bd20d84 Mon Sep 17 00:00:00 2001 From: Onkel Andy Date: Fri, 5 Jul 2024 01:37:10 +0200 Subject: [PATCH 2/9] stateengine plugin: remove status from action log, not needed --- stateengine/StateEngineAction.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/stateengine/StateEngineAction.py b/stateengine/StateEngineAction.py index 36497db3e..53668598a 100755 --- a/stateengine/StateEngineAction.py +++ b/stateengine/StateEngineAction.py @@ -643,8 +643,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() From 3a699822474bdd6bfe56c6b908c40a86d923fc98 Mon Sep 17 00:00:00 2001 From: Onkel Andy Date: Fri, 5 Jul 2024 01:39:13 +0200 Subject: [PATCH 3/9] stateengine plugin: correct eval_status evaluation order --- stateengine/StateEngineCondition.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stateengine/StateEngineCondition.py b/stateengine/StateEngineCondition.py index ce3dd0e75..cb5ccb756 100755 --- a/stateengine/StateEngineCondition.py +++ b/stateengine/StateEngineCondition.py @@ -856,6 +856,10 @@ def check_eval(eval_or_status_eval): 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) @@ -864,12 +868,8 @@ def check_eval(eval_or_status_eval): 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 From f8d8c14668153781126086b78384f8c1e91a0951 Mon Sep 17 00:00:00 2001 From: Onkel Andy Date: Fri, 5 Jul 2024 01:40:13 +0200 Subject: [PATCH 4/9] stateengine plugin: fix issues finding attributes when using se_use --- stateengine/StateEngineCondition.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/stateengine/StateEngineCondition.py b/stateengine/StateEngineCondition.py index cb5ccb756..832d00a6d 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:") @@ -122,7 +128,7 @@ def check_items(self, check, value=None, item_state=None): if check == "se_status_eval" or (check == "attribute" and self.__status_eval is None): 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:") From 78c8358f3e0b1cd706dc21ee57119c79428610f0 Mon Sep 17 00:00:00 2001 From: Onkel Andy Date: Fri, 5 Jul 2024 01:41:02 +0200 Subject: [PATCH 5/9] stateengine plugin: fix status_eval --- stateengine/StateEngineConditionSet.py | 7 +++++++ 1 file changed, 7 insertions(+) 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() From 4dff89a6a2f345adb33083732c07908f92bbc794 Mon Sep 17 00:00:00 2001 From: Onkel Andy Date: Fri, 5 Jul 2024 01:41:57 +0200 Subject: [PATCH 6/9] stateengine plugin: try/except finding parent item as this is not always possible (esp. when using structs) --- stateengine/StateEngineTools.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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: From eb6e994e63c23e0301de77111f51473dbe71bdbb Mon Sep 17 00:00:00 2001 From: Onkel Andy Date: Fri, 5 Jul 2024 21:04:00 +0200 Subject: [PATCH 7/9] stateengine plugin: get correct current item for action when using se_eval --- stateengine/StateEngineAction.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/stateengine/StateEngineAction.py b/stateengine/StateEngineAction.py index 53668598a..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: @@ -708,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 @@ -731,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, @@ -977,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") @@ -1043,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: @@ -1101,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 @@ -1124,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': {}} From 601ca5b72388023ea66917ab85cea7cafe7d600e Mon Sep 17 00:00:00 2001 From: Onkel Andy Date: Fri, 5 Jul 2024 21:12:16 +0200 Subject: [PATCH 8/9] stateengine plugin: fix value conversion for strings! --- stateengine/StateEngineCondition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stateengine/StateEngineCondition.py b/stateengine/StateEngineCondition.py index 7b8545acc..c6d624713 100755 --- a/stateengine/StateEngineCondition.py +++ b/stateengine/StateEngineCondition.py @@ -460,7 +460,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 {}.", From b27753f886c47374cd05724c90781861f4437c08 Mon Sep 17 00:00:00 2001 From: Onkel Andy Date: Fri, 5 Jul 2024 21:13:07 +0200 Subject: [PATCH 9/9] stateengine plugin: fix se_status_eval when using structs (e.g. in se_use) --- stateengine/StateEngineCondition.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stateengine/StateEngineCondition.py b/stateengine/StateEngineCondition.py index c6d624713..2e1993b55 100755 --- a/stateengine/StateEngineCondition.py +++ b/stateengine/StateEngineCondition.py @@ -126,6 +126,8 @@ 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 isinstance(value, str): @@ -475,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)]