Skip to content

Commit

Permalink
stateengine plugin: improve and extend web interface and visu
Browse files Browse the repository at this point in the history
  • Loading branch information
onkelandy committed Sep 15, 2023
1 parent 3af85b5 commit e2d17c1
Show file tree
Hide file tree
Showing 12 changed files with 110 additions and 43 deletions.
64 changes: 47 additions & 17 deletions stateengine/StateEngineAction.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ def __init__(self, abitem, name: str):
self._function = None
self.__template = None
self._action_status = {}
self._retrigger_issue = None
self._suspend_issue = None
self.__queue = abitem.queue

def update_delay(self, value):
Expand Down Expand Up @@ -260,7 +262,6 @@ def check_getitem_fromeval(self, check_item, check_value=None, check_mindelta=No
check_item, _issue = self._abitem.return_item(item)
_issue = {
self._name: {'issue': _issue, 'issueorigin': [{'state': 'unknown', 'action': self._function}]}}
# self._action_status = _issue
if check_value:
check_value.set_cast(check_item.cast)
if check_mindelta:
Expand All @@ -273,14 +274,12 @@ def check_getitem_fromeval(self, check_item, check_value=None, check_mindelta=No
self._log_develop("Got no item from eval on {} with initial item {}", self._function, self.__item)
except Exception as ex:
_issue = {self._name: {'issue': ex, 'issueorigin': [{'state': 'unknown', 'action': self._function}]}}
# self._action_status = _issue
# raise Exception("Problem evaluating item '{}' from eval: {}".format(check_item, ex))
self._log_error("Problem evaluating item '{}' from eval: {}", check_item, ex)
check_item = None
if item is None:
_issue = {self._name: {'issue': ['Item {} from eval not existing'.format(check_item)],
'issueorigin': [{'state': 'unknown', 'action': self._function}]}}
# self._action_status = _issue
# raise Exception("Problem evaluating item '{}' from eval. It does not exist.".format(check_item))
self._log_error("Problem evaluating item '{}' from eval. It does not exist", check_item)
check_item = None
Expand Down Expand Up @@ -359,7 +358,6 @@ def check_complete(self, item_state, check_item, check_status, check_mindelta, c
if self._abitem.id == check_item.property.path:
self._caller += '_self'
if _issue[self._name].get('issue') not in [[], [None], None]:
# self._action_status = _issue
self._log_develop("Issue with {} action {}", action_type, _issue)
else:
_issue = {self._name: {'issue': None,
Expand Down Expand Up @@ -440,7 +438,6 @@ def _update_repeat_webif(value: bool):
_validitem = True
except Exception as ex:
_validitem = False
#self._log_develop("action issues {}", self._action_status)
self._log_decrease_indent()
if not self._can_execute(state):
self._log_decrease_indent()
Expand Down Expand Up @@ -561,6 +558,7 @@ def _waitforexecute(self, state, actionname: str, namevar: str = "", repeat_text
else:
value = None
self._abitem.add_scheduler_entry(self._scheduler_name)
self.update_webif_actionstatus(state, self._name, 'Scheduled')
self._se_plugin.scheduler_add(self._scheduler_name, self._delayed_execute,
value={'actionname': actionname, 'namevar': self._name,
'repeat_text': repeat_text, 'value': value,
Expand Down Expand Up @@ -791,6 +789,7 @@ def real_execute(self, state, actionname: str, namevar: str = "", repeat_text: s
if returnvalue:
return value
self._log_info("{0}: Setting values by attribute '{1}'.{2}", actionname, self.__byattr, repeat_text)
self.update_webif_actionstatus(state, self._name, 'True')
source = self.set_source(current_condition, previous_condition, previousstate_condition)
for item in self.itemsApi.find_items(self.__byattr):
self._log_info("\t{0} = {1}", item.property.path, item.conf[self.__byattr])
Expand Down Expand Up @@ -855,6 +854,7 @@ def real_execute(self, state, actionname: str, namevar: str = "", repeat_text: s

if returnvalue:
return value
self.update_webif_actionstatus(state, self._name, 'True')
self._log_info("{0}: Triggering logic '{1}' using value '{2}'.{3}", actionname, self.__logic, value, repeat_text)
add_logics = 'logics.{}'.format(self.__logic) if not self.__logic.startswith('logics.') else self.__logic
self._sh.trigger(add_logics, by=self._caller, source=self._name, value=value)
Expand Down Expand Up @@ -937,10 +937,12 @@ def real_execute(self, state, actionname: str, namevar: str = "", repeat_text: s
if previousstate_condition:
self._log_debug("Running eval {0} based on previous state's conditionset {1}", self.__eval, previousstate_condition)
eval(self.__eval)
self.update_webif_actionstatus(state, self._name, 'True')
self._log_decrease_indent()
except Exception as ex:
self._log_decrease_indent()
text = "{0}: Problem evaluating '{1}': {2}."
self.update_webif_actionstatus(state, self._name, 'False', 'Problem evaluating: {}'.format(ex))
self._log_error(text.format(actionname, StateEngineTools.get_eval_name(self.__eval), ex))
else:
try:
Expand All @@ -954,9 +956,11 @@ def real_execute(self, state, actionname: str, namevar: str = "", repeat_text: s
if previousstate_condition:
self._log_debug("Running eval {0} based on previous state's conditionset {1}", self.__eval, previousstate_condition)
self.__eval()
self.update_webif_actionstatus(state, self._name, 'True')
self._log_decrease_indent()
except Exception as ex:
self._log_decrease_indent()
self.update_webif_actionstatus(state, self._name, 'False', 'Problem calling: {}'.format(ex))
text = "{0}: Problem calling '{0}': {1}."
self._log_error(text.format(actionname, StateEngineTools.get_eval_name(self.__eval), ex))

Expand Down Expand Up @@ -1057,6 +1061,7 @@ def real_execute(self, state, actionname: str, namevar: str = "", repeat_text: s

if value is None:
self._log_debug("{0}: Value is None", actionname)
self.update_webif_actionstatus(state, self._name, 'False', 'Value is None')
return

if returnvalue:
Expand All @@ -1068,6 +1073,7 @@ def real_execute(self, state, actionname: str, namevar: str = "", repeat_text: s
# noinspection PyCallingNonCallable
delta = float(abs(self.__item() - value))
if delta < mindelta:
self.update_webif_actionstatus(state, self._name, 'False')
text = "{0}: Not setting '{1}' to '{2}' because delta '{3:.2}' is lower than mindelta '{4}'"
self._log_debug(text, actionname, self.__item.property.path, value, delta, mindelta)
return
Expand Down Expand Up @@ -1097,6 +1103,7 @@ def real_execute(self, state, actionname: str, namevar: str = "", repeat_text: s
self._log_debug("{0}: New value differs from old value, no force required.", actionname)
self._log_decrease_indent()
self._log_debug("{0}: Set '{1}' to '{2}'.{3}", actionname, self.__item.property.path, value, repeat_text)
self.update_webif_actionstatus(state, self._name, 'True')
# noinspection PyCallingNonCallable
self.__item(value, caller=self._caller, source=source)

Expand Down Expand Up @@ -1190,61 +1197,84 @@ def real_execute(self, state, actionname: str, namevar: str = "", repeat_text: s
self._log_increase_indent()
if self.__special == "suspend":
self.suspend_execute(state, current_condition, previous_condition, previousstate_condition)
if self._suspend_issue in ["", [], None, [None]]:
self.update_webif_actionstatus(state, self._name, 'True')
else:
self.update_webif_actionstatus(state, self._name, 'False', self._suspend_issue)
self._log_decrease_indent()
elif self.__special == "retrigger":
if self._retrigger_issue in ["", [], None, [None]]:
self.update_webif_actionstatus(state, self._name, 'True')
else:
self.update_webif_actionstatus(state, self._name, 'False', self._retrigger_issue)
# noinspection PyCallingNonCallable
self._abitem.update_state(self.__value, self._caller)
#self.__value(True, caller=self._caller)
self._log_decrease_indent()
else:
self._log_decrease_indent()
self.update_webif_actionstatus(state, self._name, 'False', 'Unknown special value {}'.format(self.__special))
raise ValueError("{0}: Unknown special value '{1}'!".format(actionname, self.__special))
self._log_debug("Special action {0}: done", self.__special)

def suspend_get_value(self, value):
_issue = {self._name: {'issue': None, 'issueorigin': [{'state': 'suspend', 'action': 'suspend'}]}}
if value is None:
_issue = {self._name: {'issue': 'Special action suspend requires arguments', 'issueorigin': [{'state': 'suspend', 'action': 'suspend'}]}}
text = 'Special action suspend requires arguments'
_issue = {self._name: {'issue': text, 'issueorigin': [{'state': 'suspend', 'action': 'suspend'}]}}
self._action_status = _issue
raise ValueError("Action {0}: Special action 'suspend' requires arguments!".format(self._name))
self._suspend_issue = text
raise ValueError("Action {0}: {1}".format(self._name, text))

suspend, manual = StateEngineTools.partition_strip(value, ",")
if suspend is None or manual is None:
_issue = {self._name: {'issue': 'Special action suspend requires two arguments', 'issueorigin': [{'state': 'suspend', 'action': 'suspend'}]}}
text = "Special action 'suspend' requires two arguments (separated by a comma)!"
_issue = {self._name: {'issue': text, 'issueorigin': [{'state': 'suspend', 'action': 'suspend'}]}}
self._action_status = _issue
raise ValueError("Action {0}: Special action 'suspend' requires two arguments (separated by a comma)!".format(self._name))
self._suspend_issue = text
raise ValueError("Action {0}: {1}".format(self._name, text))

suspend_item, _issue = self._abitem.return_item(suspend)
_issue = {self._name: {'issue': _issue, 'issueorigin': [{'state': 'suspend', 'action': 'suspend'}]}}
if suspend_item is None:
_issue = {self._name: {'issue': 'Suspend item not found', 'issueorigin': [{'state': 'suspend', 'action': 'suspend'}]}}
text = "Suspend item '{}' not found!".format(suspend)
_issue = {self._name: {'issue': text, 'issueorigin': [{'state': 'suspend', 'action': 'suspend'}]}}
self._action_status = _issue
raise ValueError("Action {0}: Suspend item '{1}' not found!".format(self._name, suspend))
self._suspend_issue = text
raise ValueError("Action {0}: {1}".format(self._name, text))

manual_item, _issue = self._abitem.return_item(manual)
self._suspend_issue = _issue
_issue = {self._name: {'issue': _issue, 'issueorigin': [{'state': 'suspend', 'action': 'suspend'}]}}
if manual_item is None:
_issue = {self._name: {'issue': 'Manual item {} not found'.format(manual), 'issueorigin': [{'state': 'suspend', 'action': 'suspend'}]}}
text = 'Manual item {} not found'.format(manual)
_issue = {self._name: {'issue': text, 'issueorigin': [{'state': 'suspend', 'action': 'suspend'}]}}
self._action_status = _issue
raise ValueError("Action {0}: Manual item '{1}' not found!".format(self._name, manual))
self._suspend_issue = text
raise ValueError("Action {0}: {1}".format(self._name, text))
self._action_status = _issue
return [suspend_item, manual_item.property.path]

def retrigger_get_value(self, value):
if value is None:
_issue = {self._name: {'issue': 'Special action retrigger requires item', 'issueorigin': [{'state': 'retrigger', 'action': 'retrigger'}]}}
text = 'Special action retrigger requires item'
_issue = {self._name: {'issue': text, 'issueorigin': [{'state': 'retrigger', 'action': 'retrigger'}]}}
self._action_status = _issue
raise ValueError("Action {0}: Special action 'retrigger' requires item".format(self._name))
self._retrigger_issue = text
raise ValueError("Action {0}: {1}".format(self._name, text))

se_item, __ = StateEngineTools.partition_strip(value, ",")

se_item, _issue = self._abitem.return_item(se_item)
self._retrigger_issue = _issue
_issue = {self._name: {'issue': _issue, 'issueorigin': [{'state': 'retrigger', 'action': 'retrigger'}]}}
self._action_status = _issue
if se_item is None:
_issue = {self._name: {'issue': 'Retrigger item {} not found'.format(se_item), 'issueorigin': [{'state': 'retrigger', 'action': 'retrigger'}]}}
text = 'Retrigger item {} not found'.format(se_item)
_issue = {self._name: {'issue': text, 'issueorigin': [{'state': 'retrigger', 'action': 'retrigger'}]}}
self._action_status = _issue
raise ValueError("Action {0}: Retrigger item '{1}' not found!".format(self._name, se_item))
self._retrigger_issue = text
raise ValueError("Action {0}: {1}".format(self._name, text))
return se_item

def suspend_execute(self, state=None, current_condition=None, previous_condition=None, previousstate_condition=None):
Expand Down
10 changes: 10 additions & 0 deletions stateengine/StateEngineItem.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ def laststate(self):
_returnvalue = None if self.__laststate_item_id is None else self.__laststate_item_id.property.value
return _returnvalue

@property
def laststate_releasedby(self):
_returnvalue = None if self.__laststate_item_id is None \
else self.__release_info.get(self.__laststate_item_id.property.value)
return _returnvalue

@property
def previousstate(self):
_returnvalue = None if self.__previousstate_item_id is None else self.__previousstate_item_id.property.value
Expand Down Expand Up @@ -181,6 +187,7 @@ def __init__(self, smarthome, item, se_plugin):
self.__shtime = Shtime.get_instance()
self.__se_plugin = se_plugin
self.__active_schedulers = []
self.__release_info = {}
self.__default_instant_leaveaction = StateEngineValue.SeValue(self, "Default Instant Leave Action", False, "bool")
self.__instant_leaveaction = StateEngineValue.SeValue(self, "Instant Leave Action", False, "num")
try:
Expand Down Expand Up @@ -892,6 +899,9 @@ def update_can_release_list():
self.__logger.debug("Inserted copy of state {}", relevant_state.id)
_checkedentries.append(entry)
self.__logger.info("State {} can currently get released by: {}", new_state.id, _can_release_list)
self.__release_info = {new_state.id: _can_release_list}
_key_releasedby = ['{}'.format(new_state.id), 'releasedby']
self.update_webif(_key_releasedby, _can_release_list)

self.__logger.info("".ljust(80, "_"))
return all_released_by
Expand Down
9 changes: 8 additions & 1 deletion stateengine/StateEngineState.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ def is_copy_for(self):

@is_copy_for.setter
def is_copy_for(self, value):
if value:
webif_id = value.id
else:
webif_id = None
_key_copy = ['{}'.format(self.id), 'is_copy_for']
self._abitem.update_webif(_key_copy, webif_id)
self.__is_copy_for.set(value, "", True, None, False)

# Constructor
Expand Down Expand Up @@ -199,7 +205,8 @@ def write_to_log(self):
'actions_enter_or_stay': {},
'actions_stay': {},
'actions_leave': {},
'leave': False, 'enter': False, 'stay': False})
'leave': False, 'enter': False, 'stay': False,
'is_copy_for': None, 'releasedby': None})
self._log_decrease_indent()
self._log_info("Finished Web Interface Update")

Expand Down
Loading

0 comments on commit e2d17c1

Please sign in to comment.