Skip to content

Commit

Permalink
3.0.0 (#142)
Browse files Browse the repository at this point in the history
  • Loading branch information
NeonDaniel authored Apr 2, 2024
2 parents 4e7e120 + 5dccfdd commit f09effb
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 106 deletions.
42 changes: 24 additions & 18 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,54 @@
# Changelog

## [1.5.2a5](https://github.com/NeonGeckoCom/skill-alerts/tree/1.5.2a5) (2023-06-28)
## [2.0.1a5](https://github.com/NeonGeckoCom/skill-alerts/tree/2.0.1a5) (2024-04-02)

[Full Changelog](https://github.com/NeonGeckoCom/skill-alerts/compare/1.5.2a4...1.5.2a5)
[Full Changelog](https://github.com/NeonGeckoCom/skill-alerts/compare/2.0.1a4...2.0.1a5)

**Fixed bugs:**
**Merged pull requests:**

- \[BUG\] No alarm sound on expiration [\#125](https://github.com/NeonGeckoCom/skill-alerts/issues/125)
- Update log and test requirements to prep release [\#141](https://github.com/NeonGeckoCom/skill-alerts/pull/141) ([NeonDaniel](https://github.com/NeonDaniel))

**Merged pull requests:**
## [2.0.1a4](https://github.com/NeonGeckoCom/skill-alerts/tree/2.0.1a4) (2024-02-22)

- Refactor resource resolution to resolve default sound errors [\#126](https://github.com/NeonGeckoCom/skill-alerts/pull/126) ([NeonDaniel](https://github.com/NeonDaniel))
[Full Changelog](https://github.com/NeonGeckoCom/skill-alerts/compare/2.0.1a3...2.0.1a4)

## [1.5.2a4](https://github.com/NeonGeckoCom/skill-alerts/tree/1.5.2a4) (2023-06-26)
**Implemented enhancements:**

[Full Changelog](https://github.com/NeonGeckoCom/skill-alerts/compare/1.5.2a3...1.5.2a4)
- \[FEAT\] Support neon-iris [\#133](https://github.com/NeonGeckoCom/skill-alerts/issues/133)

**Merged pull requests:**

- Update event handling [\#124](https://github.com/NeonGeckoCom/skill-alerts/pull/124) ([NeonDaniel](https://github.com/NeonDaniel))
- Implement support for remote client alerts [\#136](https://github.com/NeonGeckoCom/skill-alerts/pull/136) ([NeonDaniel](https://github.com/NeonDaniel))

## [1.5.2a3](https://github.com/NeonGeckoCom/skill-alerts/tree/1.5.2a3) (2023-06-20)
## [2.0.1a3](https://github.com/NeonGeckoCom/skill-alerts/tree/2.0.1a3) (2024-02-05)

[Full Changelog](https://github.com/NeonGeckoCom/skill-alerts/compare/1.5.2a2...1.5.2a3)
[Full Changelog](https://github.com/NeonGeckoCom/skill-alerts/compare/2.0.1a2...2.0.1a3)

**Merged pull requests:**

- Reafctor skill init/initialize [\#123](https://github.com/NeonGeckoCom/skill-alerts/pull/123) ([NeonDaniel](https://github.com/NeonDaniel))
- Support ovos-utils 0.1 [\#137](https://github.com/NeonGeckoCom/skill-alerts/pull/137) ([NeonDaniel](https://github.com/NeonDaniel))

## [1.5.2a2](https://github.com/NeonGeckoCom/skill-alerts/tree/1.5.2a2) (2023-06-15)
## [2.0.1a2](https://github.com/NeonGeckoCom/skill-alerts/tree/2.0.1a2) (2024-01-29)

[Full Changelog](https://github.com/NeonGeckoCom/skill-alerts/compare/1.5.2a1...1.5.2a2)
[Full Changelog](https://github.com/NeonGeckoCom/skill-alerts/compare/2.0.1a1...2.0.1a2)

**Merged pull requests:**

- Update for best practices [\#121](https://github.com/NeonGeckoCom/skill-alerts/pull/121) ([NeonDaniel](https://github.com/NeonDaniel))
- Update to default escalating volume True for alarms to be audible [\#134](https://github.com/NeonGeckoCom/skill-alerts/pull/134) ([NeonDaniel](https://github.com/NeonDaniel))

## [1.5.2a1](https://github.com/NeonGeckoCom/skill-alerts/tree/1.5.2a1) (2023-06-12)
## [2.0.1a1](https://github.com/NeonGeckoCom/skill-alerts/tree/2.0.1a1) (2024-01-29)

[Full Changelog](https://github.com/NeonGeckoCom/skill-alerts/compare/2.0.0...2.0.1a1)

**Fixed bugs:**

[Full Changelog](https://github.com/NeonGeckoCom/skill-alerts/compare/1.5.1...1.5.2a1)
- transcribed: "Set a timer for five minutes." evokes: Speak: "I'm sorry, I don't understand." [\#132](https://github.com/NeonGeckoCom/skill-alerts/issues/132)
- \[BUG\] No module named 'mycroft\_bus\_client' [\#131](https://github.com/NeonGeckoCom/skill-alerts/issues/131)
- \[BUG\] RuntimeError: dictionary changed size during iteratio [\#130](https://github.com/NeonGeckoCom/skill-alerts/issues/130)

**Merged pull requests:**

- Refactor mycroft-messagebus-client to ovos-bus-client [\#120](https://github.com/NeonGeckoCom/skill-alerts/pull/120) ([NeonDaniel](https://github.com/NeonDaniel))
- Update tests to latest automation/shared classes [\#135](https://github.com/NeonGeckoCom/skill-alerts/pull/135) ([NeonDaniel](https://github.com/NeonDaniel))



Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Summary

A skill to schedule alarms, timers, and reminders
A skill to schedule alarms, timers, and reminders.


## Description
Expand All @@ -14,6 +14,15 @@ was off, or you had quiet hours enabled.
Alarms and reminders may be set to recur daily or weekly. An active alert may be snoozed for a specified amount of time
while it is active. Any alerts that are not acknowledged will be added to a list of missed alerts that may be read and
cleared when requested.

Other modules may integrate with the alerts skill by listening for `neon.alert_expired` events. This event will be
emitted when a scheduled alert expires and will include any context associated with the event creation. If the event
was created with `mq` context, the mq connector module will forward the expired alert for the client module to handle
and the alert will be marked `active` until the client module emits a `neon.acknowledge_alert` Message with the `alert_id`
and `missed` data, i.e.:
```
Message("neon.acknowledge_alert", {"alert_id": <alert_id>, "missed": False}, <context>)
```


## Examples
Expand Down
93 changes: 60 additions & 33 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
from ovos_utils import create_daemon
from ovos_utils.file_utils import resolve_resource_file
from ovos_utils.process_utils import RuntimeRequirements
from ovos_utils.log import LOG
from ovos_utils.log import LOG, log_deprecation
from ovos_utils.sound import play_audio
from adapt.intent import IntentBuilder
from lingua_franca.format import nice_duration, nice_time, nice_date_time
Expand All @@ -47,16 +47,15 @@
from neon_utils.message_utils import request_from_mobile, dig_for_message
from neon_utils.skills.neon_skill import NeonSkill
from neon_utils.user_utils import get_user_prefs, get_message_user
from ovos_workshop.decorators import intent_handler

from mycroft.skills import intent_handler, intent_file_handler

from .util import Weekdays, AlertState, MatchLevel, AlertPriority, WEEKDAYS, WEEKENDS, EVERYDAY
from .util.alert import Alert, AlertType
from .util.alert_manager import AlertManager, get_alert_id
from .util.parse_utils import build_alert_from_intent, spoken_time_remaining, \
from skill_alerts.util import Weekdays, AlertState, MatchLevel, AlertPriority, WEEKDAYS, WEEKENDS, EVERYDAY
from skill_alerts.util.alert import Alert, AlertType
from skill_alerts.util.alert_manager import AlertManager, get_alert_id
from skill_alerts.util.parse_utils import build_alert_from_intent, spoken_time_remaining, \
parse_alert_name_from_message, tokenize_utterance, \
parse_alert_time_from_message
from .util.ui_models import build_timer_data, build_alarm_data
from skill_alerts.util.ui_models import build_timer_data, build_alarm_data


class AlertSkill(NeonSkill):
Expand Down Expand Up @@ -145,7 +144,7 @@ def escalate_volume(self) -> bool:
"""
If true, increase volume while alert expiration is playing
"""
return self.preference_skill().get("escalate_volume", False)
return self.preference_skill().get("escalate_volume", True)

@property
def quiet_hours(self) -> bool:
Expand All @@ -171,6 +170,8 @@ def alert_timeout_seconds(self) -> int:
"""
Return the number of seconds to repeat an alert before marking it missed
"""
# TODO: This should be per-type; a user may want an alarm to go off for
# longer than a timer
timeout_minutes = self.preference_skill().get('timeout_min') or 1
if not isinstance(timeout_minutes, int):
LOG.error(f'Invalid `timeout_min` in settings. '
Expand All @@ -194,6 +195,7 @@ def initialize(self):
self.add_event("mycroft.ready", self.on_ready)

self.add_event("neon.get_events", self._get_events)
self.add_event("neon.acknowledge_alert", self._ack_alert)
self.add_event("alerts.gui.dismiss_notification",
self._gui_dismiss_notification)
self.add_event("ovos.gui.show.active.timers", self._on_display_gui)
Expand Down Expand Up @@ -393,7 +395,7 @@ def handle_list_alerts(self, message):
alerts_string = f"{alerts_string}\n{add_str}"
self.speak(alerts_string, private=True)

@intent_file_handler('list_alerts.intent')
@intent_handler('list_alerts.intent')
def alt_handle_list_alerts(self, message):
"""
Intent handler for "what are my alerts", "are there any alerts", etc.
Expand Down Expand Up @@ -464,7 +466,7 @@ def handle_timer_status(self, message):
to_speak = f"{to_speak}\n{part}"
self.speak(to_speak.lstrip('\n'), private=True)

@intent_file_handler("quiet_hours_start.intent")
@intent_handler("quiet_hours_start.intent")
def handle_start_quiet_hours(self, message):
"""
Handles starting quiet hours.
Expand All @@ -477,7 +479,7 @@ def handle_start_quiet_hours(self, message):
self.speak_dialog("quiet_hours_start", private=True)
self.update_skill_settings({"quiet_hours": True}, message)

@intent_file_handler("quiet_hours_end.intent")
@intent_handler("quiet_hours_end.intent")
def handle_end_quiet_hours(self, message):
"""
Handles ending quiet hours or requests for missed alerts.
Expand Down Expand Up @@ -982,40 +984,43 @@ def _alert_expired(self, alert: Alert):
:param alert: expired Alert object
"""
LOG.info(f'alert expired: {get_alert_id(alert)}')
alert_msg = Message("neon.alert_expired", alert.data, alert.context)
self.bus.emit(alert_msg)
if alert.context.get("mq"):
LOG.info("Alert from remote client; do nothing locally")
return
self.make_active()
self._gui_notify_expired(alert)

if alert.script_filename:
self._run_notify_expired(alert)
self._run_notify_expired(alert, alert_msg)
elif alert.audio_file:
self._play_notify_expired(alert)
self._play_notify_expired(alert, alert_msg)
elif alert.alert_type == AlertType.ALARM and not self.speak_alarm:
self._play_notify_expired(alert)
self._play_notify_expired(alert, alert_msg)
elif alert.alert_type == AlertType.TIMER and not self.speak_timer:
self._play_notify_expired(alert)
self._play_notify_expired(alert, alert_msg)
else:
self._speak_notify_expired(alert)
self._speak_notify_expired(alert, alert_msg)

def _run_notify_expired(self, alert: Alert):
def _run_notify_expired(self, alert: Alert, message: Message):
"""
Handle script file run on alert expiration
:param alert: Alert that has expired
"""
message = Message("neon.run_alert_script",
{"file_to_run": alert.script_filename},
alert.context)
# TODO: This is redundant, listeners should just use `neon.alert_expired`
message = message.forward("neon.run_alert_script",
{"file_to_run": alert.script_filename})
# emit a message telling CustomConversations to run a script
self.bus.emit(message)
# TODO: Validate alert was handled
LOG.info("The script has been executed with CC")
self.alert_manager.dismiss_active_alert(get_alert_id(alert))

def _play_notify_expired(self, alert: Alert):
def _play_notify_expired(self, alert: Alert, message: Message):
"""
Handle audio playback on alert expiration
:param alert: Alert that has expired
"""
alert_message = Message("neon.alert", alert.data, alert.context)
if alert.audio_file:
LOG.debug(alert.audio_file)
self.speak_dialog("expired_audio_alert_intro", private=True)
Expand All @@ -1029,40 +1034,43 @@ def _play_notify_expired(self, alert: Alert):
to_play = None

if not to_play:
self._speak_notify_expired(alert)
LOG.warning("Falling back to spoken notification")
self._speak_notify_expired(alert, message)
return

timeout = time.time() + self.alert_timeout_seconds
alert_id = get_alert_id(alert)
volume_message = Message("mycroft.volume.get")
volume_message = message.forward("mycroft.volume.get")
resp = self.bus.wait_for_response(volume_message)
if resp:
volume = resp.data.get('percent')
else:
volume = None
while self.alert_manager.get_alert_status(alert_id) == \
AlertState.ACTIVE and time.time() < timeout:
if alert_message.context.get("klat_data"):
if message.context.get("klat_data"):
log_deprecation("`klat.response` emit will be removed. Listen "
"for `neon.alert_expired", "4.0.0")
self.send_with_audio(self.dialog_renderer.render(
"expired_alert", {'name': alert.alert_name}),
to_play, alert_message, private=True)
to_play, message, private=True)
else:
# TODO: refactor to `self.play_audio`
LOG.debug(f"Playing file: {to_play}")
play_audio(to_play).wait(60)
time.sleep(1)
time.sleep(1) # TODO: Skip this and play continuously?
if self.escalate_volume:
self.bus.emit(Message("mycroft.volume.increase"))
self.bus.emit(message.forward("mycroft.volume.increase"))

if volume:
# Reset initial volume
self.bus.emit(Message("mycroft.volume.set", {"percent": volume}))
self.bus.emit(message.forward("mycroft.volume.set",
{"percent": volume}))
if self.alert_manager.get_alert_status(alert_id) == AlertState.ACTIVE:
self._missed_alert(alert_id)

def _speak_notify_expired(self, alert: Alert):
def _speak_notify_expired(self, alert: Alert, message: Message):
LOG.debug(f"notify alert expired: {get_alert_id(alert)}")
alert_message = Message("neon.alert", alert.data, alert.context)

# Notify user until they dismiss the alert
timeout = time.time() + self.alert_timeout_seconds
Expand All @@ -1072,9 +1080,11 @@ def _speak_notify_expired(self, alert: Alert):
if alert.alert_type == AlertType.REMINDER:
self.speak_dialog('expired_reminder',
{'name': alert.alert_name},
message=message,
private=True, wait=True)
else:
self.speak_dialog('expired_alert', {'name': alert.alert_name},
message=message,
private=True, wait=True)
self.make_active()
time.sleep(10)
Expand All @@ -1098,6 +1108,23 @@ def _missed_alert(self, alert_id: str):
self._create_notification(alert)
self._update_homescreen(do_alarms=True)

def _ack_alert(self, message: Message):
"""
Handle an emitted message acknowledging an expired alert.
@param message: neon.acknowledge_alert message
"""
alert_id = message.data.get('alert_id')
if not alert_id:
raise ValueError(f"Message data missing `alert_id`: {message.data}")
alert: Alert = self.alert_manager.active_alerts.get(alert_id)
if not alert:
LOG.error(f"Alert not active!: {alert_id}")
return
if message.data.get('missed'):
self._missed_alert(alert_id)
else:
self._dismiss_alert(alert_id, alert.alert_type)

def _dismiss_alert(self, alert_id: str, alert_type: AlertType,
speak: bool = False):
"""
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
json_database~=0.5
neon-utils~=1.2
combo_lock~=0.2
ovos-utils~=0.0.32
ovos-utils~=0.0, >=0.0.32
ovos-bus-client~=0.0.3
1 change: 1 addition & 0 deletions requirements/test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
neon-minerva[padatious]~=0.2
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def find_resource_files():
url=f'https://github.com/NeonGeckoCom/{SKILL_NAME}',
license='BSD-3-Clause',
install_requires=get_requirements("requirements.txt"),
extras_require={"test": get_requirements("requirements/test.txt")},
author='Neongecko',
author_email='[email protected]',
long_description=long_description,
Expand Down
8 changes: 4 additions & 4 deletions skill.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"title": "Alerts",
"url": "https://github.com/NeonGeckoCom/skill-alerts",
"summary": "A skill to schedule alarms, timers, and reminders",
"short_description": "A skill to schedule alarms, timers, and reminders",
"description": "The skill provides functionality to create alarms, timers and reminders, remove them by name, time, or type, and ask for what is active. You may also silence all alerts and ask for a summary of what was missed if you were away, your device was off, or you had quiet hours enabled. Alarms and reminders may be set to recur daily or weekly. An active alert may be snoozed for a specified amount of time while it is active. Any alerts that are not acknowledged will be added to a list of missed alerts that may be read and cleared when requested.",
"summary": "A skill to schedule alarms, timers, and reminders.",
"short_description": "A skill to schedule alarms, timers, and reminders.",
"description": "The skill provides functionality to create alarms, timers and reminders, remove them by name, time, or type, and ask for what is active. You may also silence all alerts and ask for a summary of what was missed if you were away, your device was off, or you had quiet hours enabled. Alarms and reminders may be set to recur daily or weekly. An active alert may be snoozed for a specified amount of time while it is active. Any alerts that are not acknowledged will be added to a list of missed alerts that may be read and cleared when requested. Other modules may integrate with the alerts skill by listening for `neon.alert_expired` events. This event will be emitted when a scheduled alert expires and will include any context associated with the event creation. If the event was created with `mq` context, the mq connector module will forward the expired alert for the client module to handle and the alert will be marked `active` until the client module emits a `neon.acknowledge_alert` Message with the `alert_id` and `missed` data, i.e.: ``` Message(\"neon.acknowledge_alert\", {\"alert_id\": <alert_id>, \"missed\": False}, <context>) ```",
"examples": [
"Set an alarm for 8 AM.",
"When is my next alarm?",
Expand Down Expand Up @@ -34,7 +34,7 @@
"json_database~=0.5",
"neon-utils~=1.2",
"ovos-bus-client~=0.0.3",
"ovos-utils~=0.0.32"
"ovos-utils~=0.0, >=0.0.32"
],
"system": {},
"skill": []
Expand Down
Loading

0 comments on commit f09effb

Please sign in to comment.