Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Few fixes and others... #21

Merged
merged 5 commits into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 27 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,46 @@
# <img src='https://raw.githack.com/FortAwesome/Font-Awesome/master/svgs/solid/bed.svg' card_color='#22a7f0' width='50' height='50' style='vertical-align:bottom'/> 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
Expand Down
107 changes: 60 additions & 47 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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',
Expand All @@ -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")
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -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
ovos-bus-client~=0.0, >=0.0.8
66 changes: 35 additions & 31 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand All @@ -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):
Expand All @@ -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='[email protected]',
license='Apache-2.0',
author="JarbasAi",
author_email="[email protected]",
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},
)
Loading