From 2d1eedbabbbfb1f50351e0a7d61720505c6cb6e9 Mon Sep 17 00:00:00 2001 From: j1nx Date: Tue, 13 Apr 2021 19:47:07 +0200 Subject: [PATCH 1/1] Fix enclosure assumptions --- mycroft/client/enclosure/base.py | 49 ++++++- mycroft/client/enclosure/generic/__init__.py | 139 +------------------ mycroft/skills/__main__.py | 20 ++- mycroft/stt/__init__.py | 2 +- 4 files changed, 60 insertions(+), 150 deletions(-) diff --git a/mycroft/client/enclosure/base.py b/mycroft/client/enclosure/base.py index 4d26a42f43..aacefe4644 100644 --- a/mycroft/client/enclosure/base.py +++ b/mycroft/client/enclosure/base.py @@ -16,13 +16,14 @@ import asyncio from collections import namedtuple from threading import Lock - +from mycroft.api import is_paired from mycroft.configuration import Configuration from mycroft.messagebus.client import MessageBusClient from mycroft.util import create_daemon, start_message_bus_client from mycroft.util.log import LOG import json +import time import tornado.web as web from tornado import ioloop from tornado.websocket import WebSocketHandler @@ -70,6 +71,11 @@ class Enclosure: # Create Message Bus Client self.bus = MessageBusClient() + # TODO: this requires the Enclosure to be up and running before the + # training is complete. + self.bus.once('mycroft.skills.trained', + self.handle_check_device_readiness) + self.gui = create_gui_service(self, config['gui_websocket']) # This datastore holds the data associated with the GUI provider. Data # is stored in Namespaces, so you can have: @@ -115,6 +121,47 @@ class Enclosure: """Perform any enclosure shutdown processes.""" pass + def is_device_ready(self): + is_ready = False + # Bus service assumed to be alive if messages sent and received + # Enclosure assumed to be alive if this method is running + services = {'audio': False, 'speech': False, 'skills': False} + start = time.monotonic() + while not is_ready: + is_ready = self.check_services_ready(services) + if is_ready: + break + elif time.monotonic() - start >= 60: + raise Exception('Timeout waiting for services start.') + else: + time.sleep(3) + return is_ready + + def handle_check_device_readiness(self, message): + + def handle_ready(message=None): + if self.is_device_ready(): + LOG.info("Mycroft is all loaded and ready to roll!") + self.bus.emit(Message('mycroft.ready')) + + if not is_paired(): + self.bus.once("mycroft.paired", handle_ready) + else: + handle_ready() + + def check_services_ready(self, services): + """Report if all specified services are ready. + + services (iterable): service names to check. + """ + for ser in services: + services[ser] = False + response = self.bus.wait_for_response(Message( + 'mycroft.{}.is_ready'.format(ser))) + if response and response.data['status']: + services[ser] = True + return all([services[ser] for ser in services]) + ###################################################################### # GUI client API @property diff --git a/mycroft/client/enclosure/generic/__init__.py b/mycroft/client/enclosure/generic/__init__.py index 3c47a54594..23c4fde794 100644 --- a/mycroft/client/enclosure/generic/__init__.py +++ b/mycroft/client/enclosure/generic/__init__.py @@ -12,144 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. # -import subprocess -import time -import sys -from threading import Thread, Timer - -import mycroft.dialog from mycroft.client.enclosure.base import Enclosure -from mycroft.api import has_been_paired -from mycroft.audio import wait_while_speaking -from mycroft.enclosure.display_manager import \ - init_display_manager_bus_connection -from mycroft.messagebus.message import Message -from mycroft.util import connected -from mycroft.util.log import LOG class EnclosureGeneric(Enclosure): """ - Serves as a communication interface between a simple text frontend and - Mycroft Core. This is used for Picroft or other headless systems, - and/or for users of the CLI. - """ - - _last_internet_notification = 0 - - def __init__(self): - super().__init__() - - # Notifications from mycroft-core - self.bus.on('enclosure.notify.no_internet', self.on_no_internet) - # TODO: this requires the Enclosure to be up and running before the - # training is complete. - self.bus.on('mycroft.skills.trained', self.is_device_ready) - - # initiates the web sockets on display manager - # NOTE: this is a temporary place to connect the display manager - init_display_manager_bus_connection() - - # verify internet connection and prompt user on bootup if needed - if not connected(): - # We delay this for several seconds to ensure that the other - # clients are up and connected to the messagebus in order to - # receive the "speak". This was sometimes happening too - # quickly and the user wasn't notified what to do. - Timer(5, self._do_net_check).start() - - def is_device_ready(self, message): - is_ready = False - # Bus service assumed to be alive if messages sent and received - # Enclosure assumed to be alive if this method is running - services = {'audio': False, 'speech': False, 'skills': False} - start = time.monotonic() - while not is_ready: - is_ready = self.check_services_ready(services) - if is_ready: - break - elif time.monotonic() - start >= 60: - raise Exception('Timeout waiting for services start.') - else: - time.sleep(3) - - if is_ready: - LOG.info("Mycroft is all loaded and ready to roll!") - self.bus.emit(Message('mycroft.ready')) - - return is_ready - - def check_services_ready(self, services): - """Report if all specified services are ready. - - services (iterable): service names to check. - """ - for ser in services: - services[ser] = False - response = self.bus.wait_for_response(Message( - 'mycroft.{}.is_ready'.format(ser))) - if response and response.data['status']: - services[ser] = True - return all([services[ser] for ser in services]) - - def on_no_internet(self, event=None): - if connected(): - # One last check to see if connection was established - return - - if time.time() - Enclosure._last_internet_notification < 30: - # don't bother the user with multiple notifications with 30 secs - return - - Enclosure._last_internet_notification = time.time() - - # TODO: This should go into EnclosureMark1 subclass of Enclosure. - if has_been_paired(): - # Handle the translation within that code. - self.bus.emit(Message("speak", { - 'utterance': "This device is not connected to the Internet. " - "Either plug in a network cable or set up your " - "wifi connection."})) - else: - # enter wifi-setup mode automatically - self.bus.emit(Message('system.wifi.setup', {'lang': self.lang})) - - def speak(self, text): - self.bus.emit(Message("speak", {'utterance': text})) - - def _handle_pairing_complete(self, _): - """ - Handler for 'mycroft.paired', unmutes the mic after the pairing is - complete. - """ - self.bus.emit(Message("mycroft.mic.unmute")) - - def _do_net_check(self): - # TODO: This should live in the derived Enclosure, e.g. EnclosureMark1 - LOG.info("Checking internet connection") - if not connected(): # and self.conn_monitor is None: - if has_been_paired(): - # TODO: Enclosure/localization - self.speak("This unit is not connected to the Internet. " - "Either plug in a network cable or setup your " - "wifi connection.") - else: - # Begin the unit startup process, this is the first time it - # is being run with factory defaults. - - # TODO: This logic should be in EnclosureMark1 - # TODO: Enclosure/localization - - # Don't listen to mic during this out-of-box experience - self.bus.emit(Message("mycroft.mic.mute")) - # Setup handler to unmute mic at the end of on boarding - # i.e. after pairing is complete - self.bus.once('mycroft.paired', self._handle_pairing_complete) - - self.speak(mycroft.dialog.get('mycroft.intro')) - wait_while_speaking() - time.sleep(2) # a pause sounds better than just jumping in - - # Kick off wifi-setup automatically - data = {'allow_timeout': False, 'lang': self.lang} - self.bus.emit(Message('system.wifi.setup', data)) + Serves as a communication interface between GUI and + Mycroft Core. This is used for Picroft or other headless systems + """ \ No newline at end of file diff --git a/mycroft/skills/__main__.py b/mycroft/skills/__main__.py index 85f809bc91..5e7a768405 100644 --- a/mycroft/skills/__main__.py +++ b/mycroft/skills/__main__.py @@ -40,13 +40,13 @@ from mycroft.util.lang import set_active_lang from mycroft.util.log import LOG from mycroft.util.process_utils import ProcessStatus, StatusCallbackMap -from .api import SkillApi -from .core import FallbackSkill -from .event_scheduler import EventScheduler -from .intent_service import IntentService -from .skill_manager import SkillManager +from mycroft.skills.api import SkillApi +from mycroft.skills.core import FallbackSkill +from mycroft.skills.event_scheduler import EventScheduler +from mycroft.skills.intent_service import IntentService +from mycroft.skills.skill_manager import SkillManager -RASPBERRY_PI_PLATFORMS = ('mycroft_mark_1', 'picroft', 'mycroft_mark_2pi') +RASPBERRY_PI_PLATFORMS = ('mycroft_mark_1', 'picroft', 'mycroft_mark_2pi', 'OpenVoiceOS') class DevicePrimer(object): @@ -125,14 +125,10 @@ class DevicePrimer(object): def _ensure_device_is_paired(self): """Determine if device is paired, if not automatically start pairing. - - Pairing cannot be performed if there is no connection to the back end. - So skip pairing if the backend is down. """ - if not self.is_paired and not self.backend_down: + if not self.is_paired: LOG.info('Device not paired, invoking the pairing skill') - payload = dict(utterances=["pair my device"], lang="en-us") - self.bus.emit(Message("recognizer_loop:utterance", payload)) + self.bus.emit(Message("mycroft.not.paired")) def _update_device_attributes_on_backend(self): """Communicate version information to the backend. diff --git a/mycroft/stt/__init__.py b/mycroft/stt/__init__.py index 5028f20ea1..a0d49a6e51 100644 --- a/mycroft/stt/__init__.py +++ b/mycroft/stt/__init__.py @@ -285,7 +285,7 @@ def requires_pairing(func): if e.response.status_code == 401: LOG.warning('Access Denied at mycroft.ai') # phrase to start the pairing process - return 'pair my device' + return None else: raise return wrapper -- 2.20.1