From 6084b61e0dd5bda709c5579d28e42bf48e386049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Sat, 13 Apr 2024 12:27:20 -0400 Subject: [PATCH] Few fixes and others... (#21) Add a setting to mute the audio when going to sleep --- README.md | 38 ++++++++++++----- __init__.py | 107 ++++++++++++++++++++++++++--------------------- requirements.txt | 2 +- setup.py | 66 +++++++++++++++-------------- 4 files changed, 123 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index 41eab5a..f1f42db 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,46 @@ # Naptime -Put the assistant to sleep when you don't want to be disturbed -## About +Put the assistant to sleep when you don't want to be disturbed. + +## About + Tell the assistant to sleep when you don't want to be disturbed in any way. -This stops all calls to Speech to Text system, guaranteeing your voice won't -be sent anywhere on an accidental activation. +This stops all calls to Speech to Text system, guaranteeing your voice won't be sent anywhere on an accidental activation. + +When sleeping, the assistant will only listen locally for the wake word `Hey Mycroft, wake up`. Otherwise the system will be totally silent and won't bother you. + +On a Mark 1 device this also dims the eyes. + +Skill can mute the audio as well when entering into sleep mode if required. -When sleeping, the assistant will only listen locally for the wake word "Hey Mycroft, -wake up". Otherwise the system will be totally silent and won't bother you. +## Configuration -On a Mark 1 this also dims the eyes. +The skill utilizes the `~/.config/mycroft/skills/skill-ovos-naptime.openvoiceos/settings.json` file which allows you to configure this skill. -## Examples -* "Go to sleep" -* "Nap time" -* "Wake up" +```json +{ + "mute": false +} +``` + +## Examples + +- "Go to sleep" +- "Nap time" +- "Wake up" ## Credits + OpenVoiceOS (@OpenVoiceOS) Mycroft AI (@MycroftAI) ## Category + **Daily** Configuration ## Tags + #nap #naptime #sleep diff --git a/__init__.py b/__init__.py index 0106da4..bad7e22 100644 --- a/__init__.py +++ b/__init__.py @@ -27,38 +27,45 @@ class NapTimeSkill(OVOSSkill): @classproperty def runtime_requirements(self): - return RuntimeRequirements(internet_before_load=False, - network_before_load=False, - gui_before_load=False, - requires_internet=False, - requires_network=False, - requires_gui=False, - no_internet_fallback=True, - no_network_fallback=True, - no_gui_fallback=True) + return RuntimeRequirements( + internet_before_load=False, + network_before_load=False, + gui_before_load=False, + requires_internet=False, + requires_network=False, + requires_gui=False, + no_internet_fallback=True, + no_network_fallback=True, + no_gui_fallback=True, + ) def initialize(self): self.started_by_skill = False self.sleeping = False self.old_brightness = 30 - self.add_event('mycroft.awoken', self.handle_awoken) - self.add_event('mycroft.awoken', self.mark1_wake_up_animation) - self.add_event('recognizer_loop:sleep', self.mark1_sleep_animation) - self.add_event('mycroft.awoken', self.display_waking_face) - self.add_event('recognizer_loop:sleep', self.display_sleep_face) + self.add_event("mycroft.awoken", self.handle_awoken) + self.add_event("mycroft.awoken", self.mark1_wake_up_animation) + self.add_event("recognizer_loop:sleep", self.mark1_sleep_animation) + self.add_event("mycroft.awoken", self.display_waking_face) + self.add_event("recognizer_loop:sleep", self.display_sleep_face) self.disabled_confirm_listening = False + @property + def mute(self): + return self.settings.get("mute", False) + @property def wake_word(self): - default = Configuration().get('listener', {}).get('wake_word') + default = Configuration().get("listener", {}).get("wake_word") # with multiple wakewords we can't be 100% sure what the correct name is # a device might have multiple names # - if the wake_word is set in listener consider that the main wakeword # - if the wake_word in listener config does not have a ww config ignore it - # - else use the first hotword that listens and is set to self.lang, assume config is ordered by priority + # - else use the first hotword that listens and is set to self.lang, assume config is + # ordered by priority # - else use the first hotword that listens, assume config is ordered by priority - hotwords = Configuration().get('hotwords', {}) + hotwords = Configuration().get("hotwords", {}) if default in hotwords: return default @@ -78,7 +85,7 @@ def wake_word(self): return default # TODO move mark1 handlers to PHAL mk1 plugin - def mark1_sleep_animation(self, message=None): + def mark1_sleep_animation(self, message: Message): time.sleep(0.5) # Dim and look downward to 'go to sleep' # TODO: Get current brightness from somewhere @@ -88,14 +95,14 @@ def mark1_sleep_animation(self, message=None): time.sleep(0.15) self.enclosure.eyes_look("d") - def mark1_wake_up_animation(self, message=None): + def mark1_wake_up_animation(self, message: Message): """Mild animation to come out of sleep from voice command. Pop open eyes and wait a sec. """ self.enclosure.eyes_reset() time.sleep(1) - self.enclosure.eyes_blink('b') + self.enclosure.eyes_blink("b") time.sleep(1) # brighten the rest of the way self.enclosure.eyes_brightness(self.old_brightness) @@ -128,40 +135,46 @@ def show_notification(self, content, action=None, noticetype="transient"): transient: 'Default' displays a notification with a timeout. sticky: displays a notification that sticks to the screen. """ - self.bus.emit(Message("homescreen.notification.set", - data={ - "sender": self.skill_id, - "text": content, - "action": action, - "type": noticetype - })) - - def handle_speak(self, message): + self.bus.emit( + Message( + "homescreen.notification.set", + data={ + "sender": self.skill_id, + "text": content, + "action": action, + "type": noticetype, + }, + ) + ) + + def handle_speak(self, message: Message): if self.sleeping: utt = message.data["utterance"] self.show_notification(utt) @intent_handler("naptime.intent") - def handle_go_to_sleep(self, message): + def handle_go_to_sleep(self, message: Message): """Sends a message to the speech client putting the listener to sleep. If the user has been told about the waking up process five times already, it sends a shorter message. """ if self.wake_word: - self.speak_dialog('going.to.sleep', {'wake_word': self.wake_word}, wait=True) + self.speak_dialog( + "going.to.sleep", {"wake_word": self.wake_word}, wait=True + ) else: - self.speak_dialog('going.to.sleep.short', wait=True) + self.speak_dialog("going.to.sleep.short", wait=True) - self.bus.emit(Message('recognizer_loop:sleep')) + self.bus.emit(Message("recognizer_loop:sleep")) self.sleeping = True self.started_by_skill = True - self.bus.emit(Message('mycroft.volume.mute', - data={"speak_message": False})) - if self.config_core['confirm_listening']: + if self.mute: + self.bus.emit(Message("mycroft.volume.mute")) + if self.config_core["confirm_listening"]: self.disable_confirm_listening() - def handle_awoken(self, message): + def handle_awoken(self, message: Message): """Handler for the mycroft.awoken message The message is sent when the listener hears 'Hey Mycroft, Wake Up', @@ -174,25 +187,25 @@ def handle_awoken(self, message): self.speak_dialog("i.am.awake", wait=True) def awaken(self): - self.bus.emit(Message('mycroft.volume.unmute', - data={"speak_message": False})) + if self.mute: + self.bus.emit(Message("mycroft.volume.unmute")) if self.disabled_confirm_listening: self.enable_confirm_listening() self.sleeping = False self.started_by_skill = False def disable_confirm_listening(self): - msg = Message('configuration.patch', - data={'config': {'confirm_listening': False}} - ) + msg = Message( + "configuration.patch", data={"config": {"confirm_listening": False}} + ) self.bus.emit(msg) self.disabled_confirm_listening = True - self.log.info('Disabled listen sound') + self.log.info("Disabled listen sound") def enable_confirm_listening(self): - msg = Message('configuration.patch', - data={'config': {'confirm_listening': True}} - ) + msg = Message( + "configuration.patch", data={"config": {"confirm_listening": True}} + ) self.bus.emit(msg) self.disabled_confirm_listening = False - self.log.info('Enabled listen sound') + self.log.info("Enabled listen sound") diff --git a/requirements.txt b/requirements.txt index 94b5c34..298a28e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ ovos-utils~=0.0, >=0.0.38 ovos_workshop~=0.0, >=0.0.15 -ovos-bus-client~=0.0, >=0.0.8 \ No newline at end of file +ovos-bus-client~=0.0, >=0.0.8 diff --git a/setup.py b/setup.py index 99694ff..b4abe99 100755 --- a/setup.py +++ b/setup.py @@ -10,21 +10,25 @@ # below derived from github url to ensure standard skill_id SKILL_AUTHOR, SKILL_NAME = URL.split(".com/")[-1].split("/") -SKILL_PKG = SKILL_NAME.lower().replace('-', '_') -PLUGIN_ENTRY_POINT = f'{SKILL_NAME.lower()}.{SKILL_AUTHOR.lower()}={SKILL_PKG}:{SKILL_CLAZZ}' +SKILL_PKG = SKILL_NAME.lower().replace("-", "_") +PLUGIN_ENTRY_POINT = ( + f"{SKILL_NAME.lower()}.{SKILL_AUTHOR.lower()}={SKILL_PKG}:{SKILL_CLAZZ}" +) # skill_id=package_name:SkillClass def get_requirements(requirements_filename: str): - requirements_file = path.join(path.abspath(path.dirname(__file__)), - requirements_filename) - with open(requirements_file, 'r', encoding='utf-8') as r: + requirements_file = path.join( + path.abspath(path.dirname(__file__)), requirements_filename + ) + with open(requirements_file, "r", encoding="utf-8") as r: requirements = r.readlines() - requirements = [r.strip() for r in requirements if r.strip() - and not r.strip().startswith("#")] - if 'MYCROFT_LOOSE_REQUIREMENTS' in os.environ: - print('USING LOOSE REQUIREMENTS!') - requirements = [r.replace('==', '>=').replace('~=', '>=') for r in requirements] + requirements = [ + r.strip() for r in requirements if r.strip() and not r.strip().startswith("#") + ] + if "MYCROFT_LOOSE_REQUIREMENTS" in os.environ: + print("USING LOOSE REQUIREMENTS!") + requirements = [r.replace("==", ">=").replace("~=", ">=") for r in requirements] return requirements @@ -34,34 +38,34 @@ def find_resource_files(): package_data = ["*.json"] for res in resource_base_dirs: if path.isdir(path.join(base_dir, res)): - for (directory, _, files) in walk(path.join(base_dir, res)): + for directory, _, files in walk(path.join(base_dir, res)): if files: package_data.append( - path.join(directory.replace(base_dir, "").lstrip('/'), - '*')) + path.join(directory.replace(base_dir, "").lstrip("/"), "*") + ) return package_data with open("README.md", "r") as f: long_description = f.read() + def get_version(): - """ Find the version of this skill""" - version_file = os.path.join(os.path.dirname(__file__), 'version.py') + """Find the version of this skill""" + version_file = os.path.join(os.path.dirname(__file__), "version.py") major, minor, build, alpha = (None, None, None, None) with open(version_file) as f: for line in f: - if 'VERSION_MAJOR' in line: - major = line.split('=')[1].strip() - elif 'VERSION_MINOR' in line: - minor = line.split('=')[1].strip() - elif 'VERSION_BUILD' in line: - build = line.split('=')[1].strip() - elif 'VERSION_ALPHA' in line: - alpha = line.split('=')[1].strip() + if "VERSION_MAJOR" in line: + major = line.split("=")[1].strip() + elif "VERSION_MINOR" in line: + minor = line.split("=")[1].strip() + elif "VERSION_BUILD" in line: + build = line.split("=")[1].strip() + elif "VERSION_ALPHA" in line: + alpha = line.split("=")[1].strip() - if ((major and minor and build and alpha) or - '# END_VERSION_BLOCK' in line): + if (major and minor and build and alpha) or "# END_VERSION_BLOCK" in line: break version = f"{major}.{minor}.{build}" if int(alpha): @@ -72,18 +76,18 @@ def get_version(): setup( name=PYPI_NAME, version=get_version(), - description='ovos skill plugin', + description="ovos skill plugin", long_description=long_description, long_description_content_type="text/markdown", url=URL, - author='JarbasAi', - author_email='jarbasai@mailfence.com', - license='Apache-2.0', + author="JarbasAi", + author_email="jarbasai@mailfence.com", + license="Apache-2.0", package_dir={SKILL_PKG: ""}, package_data={SKILL_PKG: find_resource_files()}, packages=[SKILL_PKG], include_package_data=True, install_requires=get_requirements("requirements.txt"), - keywords='ovos skill plugin', - entry_points={'ovos.plugin.skill': PLUGIN_ENTRY_POINT} + keywords="ovos skill plugin", + entry_points={"ovos.plugin.skill": PLUGIN_ENTRY_POINT}, )