From f3e73b7e8b0b0d928704e2878f8400e516c69d98 Mon Sep 17 00:00:00 2001 From: j1nx Date: Tue, 20 Apr 2021 15:46:17 +0200 Subject: [PATCH] Add (temporarily) enclosure patch to mycroft-lib --- .../0001-Fix-enclosure-assumptions.patch | 251 ++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100644 buildroot-external/package/python-mycroft-lib/0001-Fix-enclosure-assumptions.patch diff --git a/buildroot-external/package/python-mycroft-lib/0001-Fix-enclosure-assumptions.patch b/buildroot-external/package/python-mycroft-lib/0001-Fix-enclosure-assumptions.patch new file mode 100644 index 00000000..b2423226 --- /dev/null +++ b/buildroot-external/package/python-mycroft-lib/0001-Fix-enclosure-assumptions.patch @@ -0,0 +1,251 @@ +From 17b09851fcf1b3a5d2e56cbb32e2d9827d5b0964 Mon Sep 17 00:00:00 2001 +From: j1nx +Date: Tue, 20 Apr 2021 12:35:41 +0200 +Subject: [PATCH 1/1] Fix enclosure assumptions + +--- + mycroft/client/enclosure/base.py | 48 +++++++++ + mycroft/client/enclosure/generic/__init__.py | 100 +------------------ + mycroft/skills/__main__.py | 10 +- + mycroft/stt/__init__.py | 2 +- + 4 files changed, 54 insertions(+), 106 deletions(-) + +diff --git a/mycroft/client/enclosure/base.py b/mycroft/client/enclosure/base.py +index e5bd25e0..eda01696 100644 +--- a/mycroft/client/enclosure/base.py ++++ b/mycroft/client/enclosure/base.py +@@ -17,12 +17,14 @@ import time + 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 +72,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: +@@ -153,6 +160,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 4dc52b9e..0a54c570 100644 +--- a/mycroft/client/enclosure/generic/__init__.py ++++ b/mycroft/client/enclosure/generic/__init__.py +@@ -12,107 +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. ++ Serves as a communication interface between GUI and ++ Mycroft Core. This is used for Picroft or other headless systems + """ +- +- _last_internet_notification = 0 +- +- def __init__(self): +- super().__init__() +- +- # Notifications from mycroft-core +- self.bus.on('enclosure.notify.no_internet', self.on_no_internet) +- +- # 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 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)) +diff --git a/mycroft/skills/__main__.py b/mycroft/skills/__main__.py +index dbb92210..56212907 100644 +--- a/mycroft/skills/__main__.py ++++ b/mycroft/skills/__main__.py +@@ -45,7 +45,7 @@ from mycroft.skills.intent_service import IntentService + from mycroft.skills.skill_manager import SkillManager + from mycroft.skills.msm_wrapper import MsmException + +-RASPBERRY_PI_PLATFORMS = ('mycroft_mark_1', 'picroft', 'mycroft_mark_2pi') ++RASPBERRY_PI_PLATFORMS = ('mycroft_mark_1', 'picroft', 'mycroft_mark_2pi', 'OpenVoiceOS') + + + class DevicePrimer(object): +@@ -124,14 +124,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 5028f20e..a0d49a6e 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 +