From 42581b19a8d49aae6278137d7cdd94d67835029e Mon Sep 17 00:00:00 2001 From: Onkel Andy Date: Wed, 23 Aug 2023 01:51:43 +0200 Subject: [PATCH] stateengine plugin: refactor se_instant_leaveaction. Can now be overwritten by a settings item. Further adjust initialization of SE Item --- stateengine/StateEngineDefaults.py | 3 - stateengine/StateEngineItem.py | 113 +++++++++++++++++++++-------- stateengine/__init__.py | 10 ++- stateengine/plugin.yaml | 14 ++-- 4 files changed, 97 insertions(+), 43 deletions(-) diff --git a/stateengine/StateEngineDefaults.py b/stateengine/StateEngineDefaults.py index 3ba4d4c39..5486edeca 100755 --- a/stateengine/StateEngineDefaults.py +++ b/stateengine/StateEngineDefaults.py @@ -30,8 +30,6 @@ lamella_open_value = 0 -instant_leaveaction = False - plugin_identification = "StateEngine Plugin" VERBOSE = logging.DEBUG - 1 @@ -44,4 +42,3 @@ def write_to_log(logger): logger.info("StateEngine default suntracking lamella open value = {0}".format(lamella_open_value)) logger.info("StateEngine default startup delay = {0}".format(startup_delay)) logger.info("StateEngine default suspension time = {0}".format(suspend_time)) - logger.info("StateEngine default instant_leaveaction = {0}".format(instant_leaveaction)) diff --git a/stateengine/StateEngineItem.py b/stateengine/StateEngineItem.py index 5388a6d29..e8c94a554 100755 --- a/stateengine/StateEngineItem.py +++ b/stateengine/StateEngineItem.py @@ -92,6 +92,14 @@ def logger(self): def instant_leaveaction(self): return self.__instant_leaveaction + @property + def default_instant_leaveaction(self): + return self.__default_instant_leaveaction.get() + + @default_instant_leaveaction.setter + def default_instant_leaveaction(self, value): + self.__default_instant_leaveaction = value + @property def laststate(self): _returnvalue = None if self.__laststate_item_id is None else self.__laststate_item_id.property.value @@ -166,6 +174,8 @@ def __init__(self, smarthome, item, se_plugin): self.__se_plugin = se_plugin self.__active_schedulers = [] self.__all_releasedby = {} + self.__default_instant_leaveaction = StateEngineValue.SeValue(self, "Default Instant Leave Action", False, + "bool") #self.__all_torelease = {} try: self.__id = self.__item.property.path @@ -205,6 +215,9 @@ def __init__(self, smarthome, item, se_plugin): if _startup_log_level > 0: base = self.__sh.get_basedir() SeLogger.manage_logdirectory(base, SeLogger.log_directory, True) + self.__log_level = _log_level + self.__instant_leaveaction = StateEngineValue.SeValue(self, "Instant Leave Action", False, "num") + # get startup delay self.__startup_delay = StateEngineValue.SeValue(self, "Startup Delay", False, "num") self.__startup_delay.set_from_attr(self.__item, "se_startup_delay", StateEngineDefaults.startup_delay) @@ -280,9 +293,7 @@ def __init__(self, smarthome, item, se_plugin): self.__action_status = {} self.__state_issues = {} self.__webif_infos = OrderedDict() - self.__instant_leaveaction = StateEngineValue.SeValue(self, "Instant Leave Action", False, "bool") - self.__instant_leaveaction.set_from_attr(self.__item, "se_instant_leaveaction", - StateEngineDefaults.instant_leaveaction) + self.__repeat_actions = StateEngineValue.SeValue(self, "Repeat actions if state is not changed", False, "bool") self.__repeat_actions.set_from_attr(self.__item, "se_repeat_actions", True) @@ -295,6 +306,7 @@ def __init__(self, smarthome, item, se_plugin): self.__update_original_item = None self.__update_original_caller = None self.__update_original_source = None + self.__using_default_instant_leaveaction = False # Check item configuration self.__check_item_config() @@ -303,7 +315,7 @@ def __init__(self, smarthome, item, se_plugin): self.__variables = { "item.suspend_time": self.__suspend_time.get(), "item.suspend_remaining": 0, - "item.instant_leaveaction": self.__instant_leaveaction.get(), + "item.instant_leaveaction": 0, "current.state_id": "", "current.state_name": "", "current.conditionset_id": "", @@ -329,8 +341,11 @@ def __init__(self, smarthome, item, se_plugin): self.__logger.error("Issue finishing states because {}", ex) return - # Write settings to log - self.__write_to_log() + + def __repr__(self): + return self.__id + + def startup(self): try: self.__has_released.pop('initial') except Exception: @@ -338,7 +353,25 @@ def __init__(self, smarthome, item, se_plugin): self.__logger.develop("".ljust(80, "_")) self.__logger.develop("ALL RELEASEDBY: {}", self.__all_releasedby) self.__logger.develop("HAS RELEASED: {}", self.__has_released) + self.__logger.info("".ljust(80, "_")) + # start timer with startup-delay + _startup_delay_param = self.__startup_delay.get() + startup_delay = 1 if self.__startup_delay.is_empty() or _startup_delay_param == 0 else _startup_delay_param + if startup_delay > 0: + first_run = self.__shtime.now() + datetime.timedelta(seconds=startup_delay) + self.__logger.info("Will start stateengine evaluation at {}", first_run) + scheduler_name = self.__id + "-Startup Delay" + value = {"item": self.__item, "caller": "Init"} + self.__se_plugin.scheduler_add(scheduler_name, self.__startup_delay_callback, value=value, next=first_run) + elif startup_delay == -1: + self.__startup_delay_over = True + self.__add_triggers() + else: + self.__startup_delay_callback(self.__item, "Init", None, None) + self.__logger.info("Reset log level to {}", self.__log_level) + self.__logger.log_level = self.__log_level + def show_issues_summary(self): # show issues summary filtered_dict = {key: value for key, value in self.__unused_attributes.items() if key not in self.__used_attributes or 'issue' in value.keys()} @@ -356,26 +389,31 @@ def __init__(self, smarthome, item, se_plugin): if self.__state_issues: self.__log_issues('states') - self.__logger.info("".ljust(80, "_")) - # start timer with startup-delay - _startup_delay_param = self.__startup_delay.get() - startup_delay = 1 if self.__startup_delay.is_empty() or _startup_delay_param == 0 else _startup_delay_param - if startup_delay > 0: - first_run = self.__shtime.now() + datetime.timedelta(seconds=startup_delay) - self.__logger.info("Will start stateengine evaluation at {}", first_run) - scheduler_name = self.__id + "-Startup Delay" - value = {"item": self.__item, "caller": "Init"} - self.__se_plugin.scheduler_add(scheduler_name, self.__startup_delay_callback, value=value, next=first_run) - elif startup_delay == -1: - self.__startup_delay_over = True - self.__add_triggers() + def update_leave_action(self, default_instant_leaveaction): + self.__default_instant_leaveaction = default_instant_leaveaction + + _returnvalue_leave, _returntype_leave, _using_default_leave = self.__instant_leaveaction.set_from_attr( + self.__item, "se_instant_leaveaction", default_instant_leaveaction) + + if len(_returnvalue_leave) > 1: + self.__logger.warning("se_instant_leaveaction for item {} can not be defined as a list" + " ({}). Using default value {}.", self.id, _returnvalue_leave, default_instant_leaveaction) + self.__instant_leaveaction.set(default_instant_leaveaction) + self.__variables.update({"item.instant_leaveaction": default_instant_leaveaction}) + elif len(_returnvalue_leave) == 1 and _returnvalue_leave[0] is None: + self.__instant_leaveaction.set(default_instant_leaveaction) + self.__variables.update({"item.instant_leaveaction": default_instant_leaveaction}) + self.__logger.info("Using default instant_leaveaction {0} " + "as no se_instant_leaveaction is set.".format(default_instant_leaveaction)) + elif _using_default_leave: + self.__variables.update({"item.instant_leaveaction": default_instant_leaveaction}) + self.__logger.info("Using default instant_leaveaction {0} " + "as no se_instant_leaveaction is set.".format(default_instant_leaveaction)) else: - self.__startup_delay_callback(self.__item, "Init", None, None) - self.__logger.info("Reset log level to {}", _log_level) - self.__logger.log_level = _log_level - - def __repr__(self): - return self.__id + self.__variables.update({"item.instant_leaveaction": _returnvalue_leave}) + self.__logger.info("Using instant_leaveaction {0} " + "from attribute se_instant_leaveaction. " + "Default value is {1}".format(_returnvalue_leave, default_instant_leaveaction)) def updatetemplates(self, template, value): if value is None: @@ -407,8 +445,14 @@ def run_queue(self): base = self.__sh.get_basedir() SeLogger.manage_logdirectory(base, SeLogger.log_directory, True) self.__logger.debug("Current log level {}, default {}, currently using default {}", - self.__logger.log_level, _default_log_level, self.__logger.using_default) - self.__logger.debug("Current instant leave action {}", self.__instant_leaveaction) + self.__logger.log_level, _default_log_level, self.__logger.using_default_log_level) + if self.__instant_leaveaction.get() == -1: + self.__using_default_instant_leaveaction = True + else: + self.__using_default_instant_leaveaction = False + self.__logger.debug("Current instant leave action {}, default {}, currently using default {}", + self.__instant_leaveaction, self.__default_instant_leaveaction, + self.__using_default_instant_leaveaction) self.update_lock.acquire(True, 10) while not self.__queue.empty() and self.__ab_alive: job = self.__queue.get() @@ -459,8 +503,8 @@ def run_queue(self): StateEngineCurrent.update() self.__variables["item.suspend_time"] = self.__suspend_time.get() self.__variables["item.suspend_remaining"] = -1 - self.__variables["item.instant_leaveaction"] = self.__instant_leaveaction.get() - + self.__variables["item.instant_leaveaction"] = self.__default_instant_leaveaction.get() \ + if self.__using_default_instant_leaveaction is True else self.__instant_leaveaction.get() # get last state last_state = self.__laststate_get() if last_state is not None: @@ -560,7 +604,14 @@ def run_queue(self): if not _releasedby: # New state is different from last state - if result is False and last_state == state and self.__instant_leaveaction.get() is True: + _instant_leaveaction = self.__instant_leaveaction.get() + if self.__using_default_instant_leaveaction: + _instant_leaveaction = self.__default_instant_leaveaction.get() + if _instant_leaveaction == 1: + _instant_leaveaction = True + elif _instant_leaveaction == 0: + _instant_leaveaction = False + if result is False and last_state == state and _instant_leaveaction is True: self.__logger.info("Leaving {0} ('{1}'). Running actions immediately.", last_state.id, last_state.name) last_state.run_leave(self.__repeat_actions.get()) @@ -1284,7 +1335,7 @@ def __verbose_crons_and_cycles(self): return crons, cycles # log item data - def __write_to_log(self): + def write_to_log(self): # get crons and cycles crons, cycles = self.__verbose_crons_and_cycles() triggers = self.__verbose_triggers() diff --git a/stateengine/__init__.py b/stateengine/__init__.py index f38b94884..143a8313f 100755 --- a/stateengine/__init__.py +++ b/stateengine/__init__.py @@ -87,9 +87,9 @@ def __init__(self, sh): StateEngineDefaults.startup_delay = self.get_parameter_value("startup_delay_default") StateEngineDefaults.suspend_time = self.get_parameter_value("suspend_time_default") default_instant_leaveaction = self.get_parameter_value("instant_leaveaction") - default_instant_leaveaction_value = StateEngineValue.SeValue(self, "Instant Leave Action", False, "bool") - default_instant_leaveaction_value.set(default_instant_leaveaction) - StateEngineDefaults.instant_leaveaction = default_instant_leaveaction_value + self.__default_instant_leaveaction = StateEngineValue.SeValue(self, "Default Instant Leave Action", False, "bool") + self.__default_instant_leaveaction.set(default_instant_leaveaction) + StateEngineDefaults.suntracking_offset = self.get_parameter_value("lamella_offset") StateEngineDefaults.lamella_open_value = self.get_parameter_value("lamella_open_value") StateEngineDefaults.write_to_log(self.logger) @@ -142,6 +142,10 @@ def run(self): try: abitem = StateEngineItem.SeItem(self.get_sh(), item, self) abitem.ab_alive = True + abitem.update_leave_action(self.__default_instant_leaveaction) + abitem.write_to_log() + abitem.show_issues_summary() + abitem.startup() self._items[abitem.id] = abitem except ValueError as ex: self.logger.error("Problem with Item: {0}: {1}".format(item.property.path, ex)) diff --git a/stateengine/plugin.yaml b/stateengine/plugin.yaml index c3d760e22..086192832 100755 --- a/stateengine/plugin.yaml +++ b/stateengine/plugin.yaml @@ -291,17 +291,19 @@ item_attributes: type: foo default: False description: - de: 'Ist dieser Parameter auf True gesetzt, werden "on leave" Aktionen + de: 'Ist dieser Parameter auf True bzw. 1 gesetzt, werden "on leave" Aktionen sofort ausgeführt, wenn der aktuelle Zustand nicht mehr eingenommen wird. Standardmäßig werden die Aktionen erst direkt vor dem Eintreten in einen neuen Zustand getriggert. Kann auch über ein Item zur Laufzeit geändert werden, - beispielsweise durch item:..settings.instant_leaveaction. + beispielsweise durch item:..settings.instant_leaveaction. Der Wert -1 sorgt dafür, + dass der in der plugin.yaml angegebene Standardwert herangezogen wird. ' - en: 'If this parameter is set to True the "on leave" actions are run + en: 'If this parameter is set to True or 1 the "on leave" actions are run immediately after not entering the current state again. By default the actions are triggered directly before entering a new state. Can also be defined by a valid item, - e.g. item:..settings.instant_leaveaction + e.g. item:..settings.instant_leaveaction. By setting the value to -1 + the default value from the plugin.yaml file is used. ' se_plugin: @@ -741,9 +743,9 @@ item_structs: initial_value: -1 instant_leaveaction: - type: bool + type: num cache: True - initial_value: False + initial_value: -1 settings_edited: type: bool