From 9170959d03537c7a3872c17f77a2b91f598e2ebc Mon Sep 17 00:00:00 2001 From: j1nx Date: Tue, 13 Apr 2021 20:23:38 +0200 Subject: [PATCH] (re)Add enclosure assumption patch --- .../0003-Fix-enclosure-assumptions.patch | 302 ++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 buildroot-external/package/python-mycroft/0003-Fix-enclosure-assumptions.patch diff --git a/buildroot-external/package/python-mycroft/0003-Fix-enclosure-assumptions.patch b/buildroot-external/package/python-mycroft/0003-Fix-enclosure-assumptions.patch new file mode 100644 index 00000000..e99334d5 --- /dev/null +++ b/buildroot-external/package/python-mycroft/0003-Fix-enclosure-assumptions.patch @@ -0,0 +1,302 @@ +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 +