Skip to content

Commit dc57727

Browse files
committed
stateengine plugin: improve action scheduler
1 parent 56992bd commit dc57727

File tree

2 files changed

+43
-22
lines changed

2 files changed

+43
-22
lines changed

stateengine/StateEngineActionScheduler.py

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,20 @@ def __init__(self, smarthome, se_plugin, logger):
3535
self._next_wakeup = None
3636

3737
# ---------- API für Items ----------
38-
def add(self, abitem, name, action, value, next_run):
38+
def add(self, abitem, name, action, value, next_run, overwrite, callback=None):
3939
self._queue.put((
4040
'add',
4141
abitem,
4242
name,
4343
{
4444
'action': action,
4545
'value': value or {},
46-
'next': next_run
47-
}
46+
'next': next_run,
47+
'overwrite': overwrite
48+
},
49+
callback
4850
))
4951
self._mark_dirty()
50-
self._schedule_wakeup(next_run)
5152

5253
def remove(self, abitem, name, callback=None):
5354
self._queue.put(('remove', abitem, name, callback))
@@ -62,14 +63,6 @@ def _mark_dirty(self):
6263
self._dirty = True
6364
self._se_plugin.scheduler_trigger('actionscheduler')
6465

65-
def _schedule_wakeup(self, next_run):
66-
if next_run is None:
67-
return
68-
69-
if self._next_wakeup is None or next_run < self._next_wakeup:
70-
self._next_wakeup = next_run
71-
self._se_plugin.scheduler_trigger('actionscheduler', dt=next_run)
72-
7366
# ---------- Scheduler Loop ----------
7467
def run(self):
7568
self._dirty = False
@@ -80,9 +73,22 @@ def run(self):
8073
cmd = self._queue.get()
8174

8275
if cmd[0] == 'add':
83-
_, abitem, name, entry = cmd
76+
_, abitem, name, entry, callback = cmd
77+
key = (abitem, name)
8478
with self._lock:
85-
self._scheduled[(abitem, name)] = entry
79+
if key in self._scheduled and entry.get('overwrite', True) is False:
80+
new_next = self._scheduled[key]['next']
81+
added = False
82+
else:
83+
self._scheduled[key] = entry
84+
new_next = entry.get('next')
85+
added = True
86+
87+
if callback:
88+
try:
89+
callback(added, new_next)
90+
except Exception as e:
91+
self.logger.debug(f"Add callback failed for '{name}': {e}")
8692

8793
elif cmd[0] == 'remove':
8894
_, abitem, name, callback = cmd
@@ -92,7 +98,7 @@ def run(self):
9298
try:
9399
callback(removed)
94100
except Exception as e:
95-
self.logger.debug(f"Remove callback failed: {e}")
101+
self.logger.debug(f"Remove callback failed for '{name}': {e}")
96102

97103
elif cmd[0] == 'remove_all':
98104
_, abitem, callback = cmd
@@ -125,6 +131,12 @@ def run(self):
125131
self.logger.debug(f"Scheduled action '{name}' executing")
126132
'''
127133
action.delayed_execute(**vals)
134+
next_times = []
135+
128136
with self._lock:
129137
for entry in self._scheduled.values():
130-
self._schedule_wakeup(entry['next'])
138+
next_times.append(entry['next'])
139+
140+
if next_times:
141+
next_wakeup = min(next_times)
142+
self._se_plugin.scheduler_trigger('actionscheduler', dt=next_wakeup)

stateengine/StateEngineItem.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ def __init__(self, smarthome, item, se_plugin):
215215
self.__cache = {}
216216
self.__last_run = {}
217217
self.__pass_repeat = {}
218+
self._delayedactions_text = []
218219
self.__default_instant_leaveaction = StateEngineValue.SeValue(self, "Default Instant Leave Action", False, "bool")
219220
self.__instant_leaveaction = StateEngineValue.SeValue(self, "Instant Leave Action", False, "num")
220221
try:
@@ -488,9 +489,14 @@ def updatetemplates(self, template, value):
488489
else:
489490
self.__templates[template] = value
490491

491-
def scheduler_add(self, name, action, value=None, next=None):
492-
self.__logger.debug("Scheduling action {} with name {} at {}", action, name, next)
493-
self.__se_plugin._action_scheduler.add(self, name, action, value, next)
492+
def scheduler_add(self, name, action, value=None, next=None, overwrite=True):
493+
def _log_result(added, new_next):
494+
if added:
495+
self._delayedactions_text.append(f"Scheduling action {action} with name '{name}' at {next}.")
496+
else:
497+
self._delayedactions_text.append(f"Scheduled action '{name}' already exists, overwrite is set to {overwrite}. Will run at {new_next}")
498+
499+
self.__se_plugin._action_scheduler.add(self, name, action, value, next, overwrite=overwrite, callback=_log_result)
494500

495501
def scheduler_remove(self, name):
496502
def _log_result(removed):
@@ -767,11 +773,14 @@ def update_current_to_empty(d):
767773
text = "No matching state found, staying at {0} ('{1}') based on conditionset {2} ('{3}')"
768774
self.__logger.info(text, last_state.id, last_state.name, _last_conditionset_id,
769775
_last_conditionset_name)
770-
last_state.run_stay(self.__repeat_actions.get())
771-
if self.update_lock.locked():
772-
self.update_lock.release()
776+
last_state.run_stay(self.__repeat_actions.get())
773777
self.__logger.decrease_indent(50)
774778
self.__logger.debug("State evaluation finished")
779+
for entry in self._delayedactions_text:
780+
self.__logger.debug("{}", entry)
781+
self._delayedactions_text = []
782+
if self.update_lock.locked():
783+
self.update_lock.release()
775784
self.__logger.info("State evaluation queue empty.")
776785
self.__handle_releasedby(new_state, last_state, _instant_leaveaction)
777786

0 commit comments

Comments
 (0)