forked from platypush/platypush
[#340] Ironed out some bugs in the alarm
integration.
- The alarm ID should be randomly generated - auto-increment IDs are subject to race conditions when alarms are created in separate processes. - Clean up alarms that are not static and have been removed from the db. - Better alarm shut down detection logic.
This commit is contained in:
parent
ca57d3d7b3
commit
42574d054a
2 changed files with 24 additions and 14 deletions
|
@ -167,6 +167,13 @@ class AlarmPlugin(RunnablePlugin, EntityManager):
|
|||
media_plugin=alarm.media_plugin or self.media_plugin,
|
||||
)
|
||||
|
||||
# Stop and remove alarms that are not statically configured no longer
|
||||
# present in the db
|
||||
for name, alarm in self.alarms.copy().items():
|
||||
if not alarm.static and name not in alarms:
|
||||
del self.alarms[name]
|
||||
alarm.stop()
|
||||
|
||||
def _sync_alarms(self):
|
||||
with self._get_session() as session:
|
||||
db_alarms = {
|
||||
|
@ -184,15 +191,16 @@ class AlarmPlugin(RunnablePlugin, EntityManager):
|
|||
self._synced = True
|
||||
|
||||
def _clear_alarm(self, alarm: DbAlarm, session: Session):
|
||||
self.alarms.pop(str(alarm.name), None)
|
||||
alarm_obj = self.alarms.pop(str(alarm.name), None)
|
||||
if alarm_obj:
|
||||
alarm_obj.stop()
|
||||
|
||||
session.delete(alarm)
|
||||
self._bus.post(EntityDeleteEvent(entity=alarm))
|
||||
|
||||
def _clear_expired_alarms(self, session: Session):
|
||||
expired_alarms = [
|
||||
alarm
|
||||
for alarm in self.alarms.values()
|
||||
if alarm.is_expired() and alarm.is_shut_down()
|
||||
alarm for alarm in self.alarms.values() if alarm.should_stop()
|
||||
]
|
||||
|
||||
if not expired_alarms:
|
||||
|
@ -220,7 +228,7 @@ class AlarmPlugin(RunnablePlugin, EntityManager):
|
|||
iter(
|
||||
alarm
|
||||
for alarm in self.alarms.values()
|
||||
if alarm.state == AlarmState.RUNNING
|
||||
if alarm.state in {AlarmState.RUNNING, AlarmState.SNOOZED}
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
@ -233,6 +241,9 @@ class AlarmPlugin(RunnablePlugin, EntityManager):
|
|||
|
||||
def _on_alarm_update(self, alarm: Alarm):
|
||||
with self._db_lock:
|
||||
if alarm.should_stop():
|
||||
return
|
||||
|
||||
self.publish_entities([alarm])
|
||||
|
||||
def _add(
|
||||
|
|
|
@ -3,6 +3,7 @@ import enum
|
|||
import os
|
||||
import time
|
||||
import threading
|
||||
from random import randint
|
||||
from typing import Callable, Optional, Union
|
||||
|
||||
import croniter
|
||||
|
@ -40,9 +41,6 @@ class Alarm:
|
|||
Alarm model and controller.
|
||||
"""
|
||||
|
||||
_alarms_count = 0
|
||||
_id_lock = threading.RLock()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
when: Union[str, int, float],
|
||||
|
@ -59,10 +57,7 @@ class Alarm:
|
|||
on_change: Optional[Callable[['Alarm'], None]] = None,
|
||||
**_,
|
||||
):
|
||||
with self._id_lock:
|
||||
self._alarms_count += 1
|
||||
self.id = self._alarms_count
|
||||
|
||||
self.id = randint(0, 65535)
|
||||
self.when = when
|
||||
self.name = name or f'Alarm_{self.id}'
|
||||
self.media = self._get_media_resource(media)
|
||||
|
@ -240,10 +235,10 @@ class Alarm:
|
|||
sleep_time = self._runtime_snooze_interval
|
||||
else:
|
||||
self.state = AlarmState.WAITING
|
||||
self._on_change()
|
||||
|
||||
break
|
||||
|
||||
self._on_change()
|
||||
self.wait_stop(self.poll_interval)
|
||||
|
||||
if self.state == AlarmState.SNOOZED:
|
||||
|
@ -266,7 +261,11 @@ class Alarm:
|
|||
self.stop_event.wait(timeout)
|
||||
|
||||
def should_stop(self):
|
||||
return self.stop_event.is_set() or (self.is_expired() and self.is_shut_down())
|
||||
return (
|
||||
self.stop_event.is_set()
|
||||
or (self.is_expired() and self.state == AlarmState.DISMISSED)
|
||||
or self.state == AlarmState.SHUTDOWN
|
||||
)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
|
|
Loading…
Reference in a new issue