From f27e7bb7e20fe5e073c27c70ed6ec486fc9a5efa Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Sat, 9 Dec 2023 17:12:20 +0100 Subject: [PATCH] [#340] Better state management for alarms. - Added `AlarmEnabledEvent` and `AlarmDisabledEvent`. - Added `snooze_interval` configurable both at plugin level and alarm level. --- platypush/message/event/alarm.py | 23 +++++++++++++++++------ platypush/plugins/alarm/__init__.py | 27 +++++++++++++++++++++++++-- platypush/plugins/alarm/_model.py | 11 ++++++++++- platypush/plugins/alarm/manifest.yaml | 2 ++ 4 files changed, 54 insertions(+), 9 deletions(-) diff --git a/platypush/message/event/alarm.py b/platypush/message/event/alarm.py index 9a70230c..2273238d 100644 --- a/platypush/message/event/alarm.py +++ b/platypush/message/event/alarm.py @@ -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: diff --git a/platypush/plugins/alarm/__init__.py b/platypush/plugins/alarm/__init__.py index 0ede4105..90c65d88 100644 --- a/platypush/plugins/alarm/__init__.py +++ b/platypush/plugins/alarm/__init__.py @@ -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. diff --git a/platypush/plugins/alarm/_model.py b/platypush/plugins/alarm/_model.py index 5fa46237..c126dfe8 100644 --- a/platypush/plugins/alarm/_model.py +++ b/platypush/plugins/alarm/_model.py @@ -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 diff --git a/platypush/plugins/alarm/manifest.yaml b/platypush/plugins/alarm/manifest.yaml index 16c912a2..aa58d402 100644 --- a/platypush/plugins/alarm/manifest.yaml +++ b/platypush/plugins/alarm/manifest.yaml @@ -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