[#340] Better state management for alarms.

- Added `AlarmEnabledEvent` and `AlarmDisabledEvent`.
- Added `snooze_interval` configurable both at plugin level and alarm
  level.
This commit is contained in:
Fabio Manganiello 2023-12-09 17:12:20 +01:00
parent 34e2a59285
commit f27e7bb7e2
Signed by: blacklight
GPG key ID: D90FBA7F76362774
4 changed files with 54 additions and 9 deletions

View file

@ -4,43 +4,54 @@ from platypush.message.event import Event
class AlarmEvent(Event):
def __init__(self, name: Optional[str] = None, *args, **kwargs):
"""
Base class for alarm events.
"""
def __init__(self, *args, name: Optional[str] = None, **kwargs):
super().__init__(*args, name=name, **kwargs)
class AlarmEnabledEvent(AlarmEvent):
"""
Triggered when an alarm is enabled.
"""
class AlarmDisabledEvent(AlarmEvent):
"""
Triggered when an alarm is disabled.
"""
class AlarmStartedEvent(AlarmEvent):
"""
Triggered when an alarm starts.
"""
pass
class AlarmEndedEvent(AlarmEvent):
"""
Triggered when an alarm stops.
"""
pass
class AlarmDismissedEvent(AlarmEndedEvent):
"""
Triggered when an alarm is dismissed.
"""
pass
class AlarmSnoozedEvent(AlarmEvent):
"""
Triggered when an alarm is snoozed.
"""
pass
class AlarmTimeoutEvent(AlarmEndedEvent):
"""
Triggered when an alarm times out.
"""
pass
# vim:sw=4:ts=4:et:

View file

@ -79,6 +79,7 @@ class AlarmPlugin(RunnablePlugin, EntityManager):
alarms: Optional[Union[list, Dict[str, Any]]] = None,
media_plugin: Optional[str] = None,
poll_interval: Optional[float] = 5.0,
snooze_interval: float = 300.0,
**kwargs,
):
"""
@ -90,8 +91,11 @@ class AlarmPlugin(RunnablePlugin, EntityManager):
``media.gstreamer`` etc. If not specified, the first available
configured local media plugin will be used. This only applies to
alarms that are configured to play an audio resource.
:param poll_interval: Poll interval in seconds (default: 5).
:param snooze_interval: Default snooze interval in seconds (default: 300).
"""
super().__init__(poll_interval=poll_interval, **kwargs)
self.snooze_interval = snooze_interval
self._db_lock = RLock()
alarms = alarms or []
if isinstance(alarms, dict):
@ -160,7 +164,7 @@ class AlarmPlugin(RunnablePlugin, EntityManager):
self.alarms[name] = Alarm.from_db(
alarm,
stop_event=self._should_stop,
media_plugin=self.media_plugin,
media_plugin=alarm.media_plugin or self.media_plugin,
)
def _sync_alarms(self):
@ -240,6 +244,7 @@ class AlarmPlugin(RunnablePlugin, EntityManager):
audio_file: Optional[str] = None,
audio_volume: Optional[Union[int, float]] = None,
enabled: bool = True,
snooze_interval: Optional[float] = None,
) -> Alarm:
alarm = Alarm(
when=when,
@ -249,6 +254,7 @@ class AlarmPlugin(RunnablePlugin, EntityManager):
media=media or audio_file,
media_plugin=self.media_plugin,
audio_volume=audio_volume,
snooze_interval=snooze_interval or self.snooze_interval,
stop_event=self._should_stop,
on_change=self._on_alarm_update,
)
@ -281,6 +287,7 @@ class AlarmPlugin(RunnablePlugin, EntityManager):
self.logger.info('No alarm is running')
return
interval = interval or alarm.snooze_interval or self.snooze_interval
alarm.snooze(interval=interval)
@action
@ -293,6 +300,7 @@ class AlarmPlugin(RunnablePlugin, EntityManager):
audio_file: Optional[str] = None,
audio_volume: Optional[Union[int, float]] = None,
enabled: bool = True,
snooze_interval: Optional[float] = None,
) -> dict:
"""
Add a new alarm.
@ -307,6 +315,7 @@ class AlarmPlugin(RunnablePlugin, EntityManager):
:param media: Path of the audio file to be played.
:param audio_volume: Volume of the audio.
:param enabled: Whether the new alarm should be enabled (default: True).
:param snooze_interval: Snooze seconds before playing the alarm again.
:return: The newly created alarm.
"""
if audio_file:
@ -322,6 +331,7 @@ class AlarmPlugin(RunnablePlugin, EntityManager):
name=name,
enabled=enabled,
audio_volume=audio_volume,
snooze_interval=snooze_interval,
).to_dict()
@action
@ -343,6 +353,19 @@ class AlarmPlugin(RunnablePlugin, EntityManager):
"""
self._disable(name)
@action
def set_enabled(self, name: str, enabled: bool):
"""
Enable/disable an alarm.
:param name: Alarm name.
:param enabled: Whether the alarm should be enabled.
"""
if enabled:
self._enable(name)
else:
self._disable(name)
@action
def dismiss(self):
"""
@ -351,7 +374,7 @@ class AlarmPlugin(RunnablePlugin, EntityManager):
self._dismiss()
@action
def snooze(self, interval: Optional[float] = 300.0):
def snooze(self, interval: Optional[float] = None):
"""
Snooze the alarm that is currently running for the specified number of seconds.
The alarm will stop and resume again later.

View file

@ -12,9 +12,11 @@ from platypush.context import get_bus, get_plugin
from platypush.entities.alarm import Alarm as AlarmDb
from platypush.message.request import Request
from platypush.message.event.alarm import (
AlarmStartedEvent,
AlarmDisabledEvent,
AlarmDismissedEvent,
AlarmEnabledEvent,
AlarmSnoozedEvent,
AlarmStartedEvent,
)
from platypush.plugins.media import MediaPlugin, PlayerState
from platypush.procedure import Procedure
@ -145,7 +147,13 @@ class Alarm:
self.set_enabled(True)
def set_enabled(self, enabled: bool):
if enabled == self._enabled:
return
self._enabled = enabled
evt_type = AlarmEnabledEvent if enabled else AlarmDisabledEvent
get_bus().post(evt_type(name=self.name))
self._on_change()
def dismiss(self):
self.state = AlarmState.DISMISSED
@ -232,6 +240,7 @@ class Alarm:
sleep_time = self._runtime_snooze_interval
else:
self.state = AlarmState.WAITING
self._on_change()
break

View file

@ -1,6 +1,8 @@
manifest:
events:
- platypush.message.event.alarm.AlarmDisabledEvent
- platypush.message.event.alarm.AlarmDismissedEvent
- platypush.message.event.alarm.AlarmEnabledEvent
- platypush.message.event.alarm.AlarmSnoozedEvent
- platypush.message.event.alarm.AlarmStartedEvent
- platypush.message.event.alarm.AlarmTimeoutEvent