From 58b8f8641ed6cb22b476683faf436bb0c1905dfd Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Thu, 21 Sep 2023 02:53:32 +0100 Subject: [PATCH 1/3] feat/compat_handler_decorator add a decorator to specify backwards compatible alternate handlers for specific ovos/mycroft versions --- ovos_workshop/decorators/compat.py | 44 ++++++ ovos_workshop/skills/base.py | 65 ++++----- ovos_workshop/skills/common_query_skill.py | 29 ++-- ovos_workshop/skills/fallback.py | 39 +++--- ovos_workshop/skills/mycroft_skill.py | 147 ++++++++++++--------- ovos_workshop/skills/ovos.py | 141 +++++++++++--------- 6 files changed, 274 insertions(+), 191 deletions(-) create mode 100644 ovos_workshop/decorators/compat.py diff --git a/ovos_workshop/decorators/compat.py b/ovos_workshop/decorators/compat.py new file mode 100644 index 00000000..a9952b91 --- /dev/null +++ b/ovos_workshop/decorators/compat.py @@ -0,0 +1,44 @@ +from functools import wraps + + +def backwards_compat(classic_core=None, pre_008=None, no_core=None): + """ + Decorator to run a different method if specific ovos-core versions are detected + """ + + def backwards_compat_decorator(func): + is_classic = False + is_old = False + is_standalone = True + try: + from mycroft.version import CORE_VERSION_STR # all classic mycroft and ovos versions + is_classic = True + is_standalone = False + + try: + from ovos_core.version import OVOS_VERSION_MINOR # ovos-core >= 0.0.8 + is_classic = False + except ImportError: + is_old = True + try: + from mycroft.version import OVOS_VERSION_MINOR # ovos-core <= 0.0.7 + is_classic = False + except: + is_standalone = True + + except: + is_standalone = True + + @wraps(func) + def func_wrapper(*args, **kwargs): + if is_classic and callable(classic_core): + return classic_core(*args, **kwargs) + if is_old and callable(pre_008): + return pre_008(*args, **kwargs) + if is_standalone and callable(no_core): + return no_core(*args, **kwargs) + return func(*args, **kwargs) + + return func_wrapper + + return backwards_compat_decorator diff --git a/ovos_workshop/skills/base.py b/ovos_workshop/skills/base.py index f1ba7c04..3da7b527 100644 --- a/ovos_workshop/skills/base.py +++ b/ovos_workshop/skills/base.py @@ -52,9 +52,9 @@ from ovos_utils.parse import match_one from ovos_utils.process_utils import RuntimeRequirements from ovos_utils.skills import get_non_properties -from ovos_utils.sound import play_acknowledge_sound from ovos_utils import classproperty +from ovos_workshop.decorators.compat import backwards_compat from ovos_workshop.decorators.killable import AbortEvent from ovos_workshop.decorators.killable import killable_event, \ AbortQuestion @@ -73,23 +73,6 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) -def is_classic_core() -> bool: - """ - Check if the current core is the classic mycroft-core - """ - try: - from mycroft.version import OVOS_VERSION_STR - return False # ovos-core - except ImportError: - try: - log_deprecation("Support for `mycroft_core` will be deprecated", - "0.1.0") - from mycroft.version import CORE_VERSION_STR - return True # mycroft-core - except ImportError: - return False # standalone - - def simple_trace(stack_trace: List[str]) -> str: """ Generate a simplified traceback. @@ -713,6 +696,26 @@ def _load_lang(self, root_directory: Optional[str] = None, skill_id=self.skill_id) return self._lang_resources[lang] + def __bind_classic(self, bus): + self._bus = bus + self.events.set_bus(bus) + self.intent_service.set_bus(bus) + self.event_scheduler.set_bus(bus) + self._enclosure.set_bus(bus) + self._register_system_event_handlers() + self._register_public_api() + log_deprecation("Support for mycroft-core is deprecated", + "0.1.0") + # inject ovos exclusive features in vanilla mycroft-core + # if possible + # limited support for missing skill deactivated event + # TODO - update ConverseTracker + ConverseTracker.connect_bus(self.bus) # pull/1468 + self.add_event("converse.skill.deactivated", + self._handle_skill_deactivated, + speak_errors=False) + + @backwards_compat(classic_core=__bind_classic) def bind(self, bus: MessageBusClient): """ Register MessageBusClient with skill. @@ -727,18 +730,6 @@ def bind(self, bus: MessageBusClient): self._register_system_event_handlers() self._register_public_api() - if is_classic_core(): - log_deprecation("Support for mycroft-core is deprecated", - "0.1.0") - # inject ovos exclusive features in vanilla mycroft-core - # if possible - # limited support for missing skill deactivated event - # TODO - update ConverseTracker - ConverseTracker.connect_bus(self.bus) # pull/1468 - self.add_event("converse.skill.deactivated", - self._handle_skill_deactivated, - speak_errors=False) - def _register_public_api(self): """ Find and register API methods decorated with `@api_method` and create a @@ -1177,6 +1168,20 @@ def _real_wait_response(self, is_cancel, validator, on_fail, num_retries): else: self.bus.emit(msg) + @staticmethod + def acknowledge(): + """ + Acknowledge a successful request. + + This method plays a sound to acknowledge a request that does not + require a verbal response. This is intended to provide simple feedback + to the user that their request was handled successfully. + """ + # DEPRECATED - note that this is a staticmethod and uses the old endpoint + # the OVOSSkill class does things properly + from ovos_utils.sound import play_acknowledge_sound + return play_acknowledge_sound() + def ask_yesno(self, prompt: str, data: Optional[dict] = None) -> Optional[str]: """ diff --git a/ovos_workshop/skills/common_query_skill.py b/ovos_workshop/skills/common_query_skill.py index 9fdd052b..f3757e47 100644 --- a/ovos_workshop/skills/common_query_skill.py +++ b/ovos_workshop/skills/common_query_skill.py @@ -16,7 +16,8 @@ from ovos_utils.file_utils import resolve_resource_file from ovos_utils.log import LOG -from ovos_workshop.skills.ovos import OVOSSkill, is_classic_core +from ovos_workshop.skills.ovos import OVOSSkill +from ovos_workshop.decorators.compat import backwards_compat class CQSMatchLevel(IntEnum): @@ -187,6 +188,17 @@ def __calc_confidence(self, match, phrase, level, answer): return confidence + def __handle_query_classic(self, message): + """does not perform self.speak, < 0.0.8 this is done by core itself""" + if message.data["skill_id"] != self.skill_id: + # Not for this skill! + return + phrase = message.data["phrase"] + data = message.data.get("callback_data") or {} + # Invoke derived class to provide playback data + self.CQS_action(phrase, data) + + @backwards_compat(classic_core=__handle_query_classic, pre_008=__handle_query_classic) def __handle_query_action(self, message): """Message handler for question:action. @@ -199,20 +211,7 @@ def __handle_query_action(self, message): phrase = message.data["phrase"] data = message.data.get("callback_data") or {} if data.get("answer"): - # check core version, ovos-core does this speak call itself up to version 0.0.8a4 - core_speak = is_classic_core() - if not core_speak: - try: - from mycroft.version import OVOS_VERSION_MAJOR, OVOS_VERSION_MINOR, OVOS_VERSION_BUILD, OVOS_VERSIOM_ALPHA - if OVOS_VERSION_MAJOR == 0 and OVOS_VERSION_MINOR == 0 and OVOS_VERSION_BUILD < 8: - core_speak = True - elif OVOS_VERSION_MAJOR == 0 and OVOS_VERSION_MINOR == 0 and OVOS_VERSION_BUILD == 8 and \ - OVOS_VERSIOM_ALPHA < 5: - core_speak = True - except ImportError: - pass - if not core_speak: - self.speak(data["answer"]) + self.speak(data["answer"]) # Invoke derived class to provide playback data self.CQS_action(phrase, data) diff --git a/ovos_workshop/skills/fallback.py b/ovos_workshop/skills/fallback.py index 5bb80148..963f65ae 100644 --- a/ovos_workshop/skills/fallback.py +++ b/ovos_workshop/skills/fallback.py @@ -15,14 +15,16 @@ import operator from typing import Optional, List, Callable, Tuple +from ovos_config import Configuration + from ovos_bus_client import MessageBusClient from ovos_utils.log import LOG from ovos_utils.messagebus import get_handler_name, Message from ovos_utils.metrics import Stopwatch from ovos_utils.skills import get_non_properties -from ovos_config import Configuration +from ovos_workshop.decorators.compat import backwards_compat from ovos_workshop.permissions import FallbackMode -from ovos_workshop.skills.ovos import OVOSSkill, is_classic_core +from ovos_workshop.skills.ovos import OVOSSkill class _MutableFallback(type(OVOSSkill)): @@ -59,31 +61,22 @@ class FallbackSkill(_MetaFB, metaclass=_MutableFallback): A Fallback can either observe or consume an utterance. A consumed utterance will not be seen by any other Fallback handlers. """ - def __new__(cls, *args, **kwargs): + def __new__classic__(cls, *args, **kwargs): if cls is FallbackSkill: - # direct instantiation of class, dynamic wizardry or unittests + # direct instantiation of class, dynamic wizardry for unittests # return V2 as expected, V1 will eventually be dropped return FallbackSkillV2(*args, **kwargs) + cls.__bases__ = (FallbackSkillV1, FallbackSkill, _MetaFB) + return super().__new__(cls, *args, **kwargs) - is_old = is_classic_core() - if not is_old: - try: - from mycroft.version import OVOS_VERSION_MAJOR, \ - OVOS_VERSION_MINOR, OVOS_VERSION_BUILD, OVOS_VERSION_ALPHA - if OVOS_VERSION_MAJOR == 0 and OVOS_VERSION_MINOR == 0 and \ - OVOS_VERSION_BUILD < 8: - is_old = True - elif OVOS_VERSION_MAJOR == 0 and OVOS_VERSION_MINOR == 0 and \ - OVOS_VERSION_BUILD == 8 and 0 < OVOS_VERSION_ALPHA < 5: - is_old = True - except ImportError: - pass - if is_old: - LOG.debug("Using V1 Fallback") - cls.__bases__ = (FallbackSkillV1, FallbackSkill, _MetaFB) - else: - LOG.debug("Using V2 Fallback") - cls.__bases__ = (FallbackSkillV2, FallbackSkill, _MetaFB) + @backwards_compat(classic_core=__new__classic__, + pre_008=__new__classic__) + def __new__(cls, *args, **kwargs): + if cls is FallbackSkill: + # direct instantiation of class, dynamic wizardry for unittests + # return V2 as expected, V1 will eventually be dropped + return FallbackSkillV2(*args, **kwargs) + cls.__bases__ = (FallbackSkillV2, FallbackSkill, _MetaFB) return super().__new__(cls, *args, **kwargs) @classmethod diff --git a/ovos_workshop/skills/mycroft_skill.py b/ovos_workshop/skills/mycroft_skill.py index cd1a2e1d..ca587290 100644 --- a/ovos_workshop/skills/mycroft_skill.py +++ b/ovos_workshop/skills/mycroft_skill.py @@ -13,15 +13,26 @@ # limitations under the License. import shutil - from abc import ABCMeta from os.path import join, exists from typing import Optional from ovos_bus_client import MessageBusClient, Message from ovos_utils.log import LOG, log_deprecation, deprecated -from ovos_workshop.skills.base import BaseSkill, is_classic_core -from ovos_utils.sound import play_acknowledge_sound +from ovos_workshop.decorators.compat import backwards_compat +from ovos_workshop.skills.base import BaseSkill + + +def is_classic_core(): + try: + from mycroft.version import OVOS_VERSION_STR + return False + except: + try: + import mycroft + return True + except: + return False class _SkillMetaclass(ABCMeta): @@ -110,13 +121,15 @@ def __call__(cls, *args, **kwargs): skill._startup(bus, skill_id) return skill - def __instancecheck__(self, instance): - if is_classic_core(): - # instance imported from vanilla mycroft - from mycroft.skills import MycroftSkill as _CoreSkill - if issubclass(instance.__class__, _CoreSkill): - return True + def __instancecheck_classic__(self, instance): + # instance imported from vanilla mycroft + from mycroft.skills import MycroftSkill as _CoreSkill + if issubclass(instance.__class__, _CoreSkill): + return True + return issubclass(instance.__class__, MycroftSkill) + @backwards_compat(classic_core=__instancecheck_classic__) + def __instancecheck__(self, instance): from ovos_workshop.skills.ovos import OVOSSkill return super().__instancecheck__(instance) or \ issubclass(instance.__class__, OVOSSkill) @@ -129,9 +142,9 @@ class MycroftSkill(BaseSkill, metaclass=_SkillMetaclass): recommended to implement `OVOSSkill` to properly implement new methods. """ - @deprecated("MycroftSkill class has been deprecated, please subclass from OVOSSkill", "0.1.0") - def __init__(self, name: str = None, bus: MessageBusClient = None, - use_settings: bool = True, *args, **kwargs): + @deprecated("mycroft-core has been deprecated, please move to ovos-core", "0.1.0") + def __classic_init__(self, name: str = None, bus: MessageBusClient = None, + use_settings: bool = True, *args, **kwargs): """ Create a MycroftSkill object. @param name: DEPRECATED skill_name @@ -149,49 +162,56 @@ def __init__(self, name: str = None, bus: MessageBusClient = None, log_deprecation("use_settings has been deprecated! " "skill settings are always enabled", "0.1.0") - if is_classic_core(): - self.settings_write_path = self.root_dir + self.settings_write_path = self.root_dir + + @backwards_compat(classic_core=__classic_init__) + @deprecated("MycroftSkill class has been deprecated, please subclass from OVOSSkill", "0.1.0") + def __init__(self, name: str = None, bus: MessageBusClient = None, + use_settings: bool = True, *args, **kwargs): + """ + Create a MycroftSkill object. + @param name: DEPRECATED skill_name + @param bus: MessageBusClient to bind to skill + @param use_settings: DEPRECATED option to disable settings sync + """ + super().__init__(name=name, bus=bus, *args, **kwargs) + self._initial_settings = {} + self.settings_write_path = None + self.settings_manager = None + + def __init_settings_manager_classic(self): + super()._init_settings_manager() + from mycroft.skills.settings import SettingsMetaUploader + self._settings_meta = SettingsMetaUploader(self.root_dir, + self.skill_id) + + def __init_settings_manager_standalone(self): + super()._init_settings_manager() + @backwards_compat(classic_core=__init_settings_manager_classic, + no_core=__init_settings_manager_standalone) def _init_settings_manager(self): super()._init_settings_manager() # backwards compat - self.settings_meta has been deprecated # in favor of settings manager - if is_classic_core(): - from mycroft.skills.settings import SettingsMetaUploader - else: - try: # ovos-core compat layer - from mycroft.deprecated.skills.settings import \ - SettingsMetaUploader - self._settings_meta = SettingsMetaUploader(self.root_dir, - self.skill_id) - except ImportError: - pass # standalone skill, skip backwards compat property - - @staticmethod - def acknowledge(): - """ - Acknowledge a successful request. - - This method plays a sound to acknowledge a request that does not - require a verbal response. This is intended to provide simple feedback - to the user that their request was handled successfully. - """ - # DEPRECATED - note that this is a staticmethod and uses the old endpoint - # the OVOSSkill class does things properly - return play_acknowledge_sound() + from mycroft.deprecated.skills.settings import SettingsMetaUploader + self._settings_meta = SettingsMetaUploader(self.root_dir, + self.skill_id) + + def __init_settings_classic(self): + # migrate settings if needed + if not exists(self._settings_path) and \ + exists(self._old_settings_path): + LOG.warning("Found skill settings at pre-xdg location, " + "migrating!") + shutil.copy(self._old_settings_path, self._settings_path) + LOG.info(f"{self._old_settings_path} moved to " + f"{self._settings_path}") + super()._init_settings() + @backwards_compat(classic_core=__init_settings_classic) def _init_settings(self): """Setup skill settings.""" - if is_classic_core(): - # migrate settings if needed - if not exists(self._settings_path) and \ - exists(self._old_settings_path): - LOG.warning("Found skill settings at pre-xdg location, " - "migrating!") - shutil.copy(self._old_settings_path, self._settings_path) - LOG.info(f"{self._old_settings_path} moved to " - f"{self._settings_path}") - super()._init_settings() # renamed in base class for naming consistency @@ -216,14 +236,8 @@ def make_active(self): self._activate() # patched due to functional (internal) differences under mycroft-core - def _on_event_end(self, message: Message, handler_info: str, - skill_data: dict): - """ - Store settings and indicate that the skill handler has completed - """ - if not is_classic_core(): - return super()._on_event_end(message, handler_info, skill_data) - + def __on_end_classic(self, message: Message, handler_info: str, + skill_data: dict): # mycroft-core style settings if self.settings != self._initial_settings: try: @@ -237,6 +251,14 @@ def _on_event_end(self, message: Message, handler_info: str, message.context["skill_id"] = self.skill_id self.bus.emit(message.forward(msg_type, skill_data)) + @backwards_compat(classic_core=__on_end_classic) + def _on_event_end(self, message: Message, handler_info: str, + skill_data: dict): + """ + Store settings and indicate that the skill handler has completed + """ + return super()._on_event_end(message, handler_info, skill_data) + # renamed in base class for naming consistency # refactored to use new resource utils def translate(self, text: str, data: Optional[dict] = None): @@ -300,12 +322,15 @@ def _old_settings_path(self): return join(old_dir, old_folder, self.skill_id, 'settings.json') # patched due to functional (internal) differences under mycroft-core + def __get_settings_pclassic(self): + if self.settings_write_path and \ + self.settings_write_path != self.root_dir: + log_deprecation("`self.settings_write_path` is no longer used", + "0.1.0") + return join(self.settings_write_path, 'settings.json') + return super()._settings_path + @property + @backwards_compat(classic_core=__get_settings_pclassic) def _settings_path(self): - if is_classic_core(): - if self.settings_write_path and \ - self.settings_write_path != self.root_dir: - log_deprecation("`self.settings_write_path` is no longer used", - "0.1.0") - return join(self.settings_write_path, 'settings.json') return super()._settings_path diff --git a/ovos_workshop/skills/ovos.py b/ovos_workshop/skills/ovos.py index dfa2e19f..7dca9a00 100644 --- a/ovos_workshop/skills/ovos.py +++ b/ovos_workshop/skills/ovos.py @@ -11,10 +11,11 @@ from ovos_utils.skills.audioservice import OCPInterface from ovos_utils.skills.settings import PrivateSettings from ovos_utils.sound import play_audio +from ovos_workshop.decorators.compat import backwards_compat from ovos_workshop.decorators.layers import IntentLayers from ovos_workshop.resource_files import SkillResources from ovos_workshop.skills.base import BaseSkill -from ovos_workshop.skills.mycroft_skill import is_classic_core, MycroftSkill +from ovos_workshop.skills.mycroft_skill import MycroftSkill class _OVOSSkillMetaclass(ABCMeta): @@ -22,51 +23,19 @@ class _OVOSSkillMetaclass(ABCMeta): To override isinstance checks """ - def __instancecheck__(self, instance): - if is_classic_core(): - # instance imported from vanilla mycroft - from mycroft.skills import MycroftSkill as _CoreSkill - if issubclass(instance.__class__, _CoreSkill): - return True + def __instancecheck_classic__(self, instance): + # instance imported from vanilla mycroft + from mycroft.skills import MycroftSkill as _CoreSkill + if issubclass(instance.__class__, _CoreSkill): + return True + return issubclass(instance.__class__, MycroftSkill) + @backwards_compat(classic_core=__instancecheck_classic__) + def __instancecheck__(self, instance): return super().__instancecheck__(instance) or \ issubclass(instance.__class__, MycroftSkill) -def _can_play_audio(instant=False): - """ helper method to ensure skills keep working on old versions of ovos/mycroft""" - # TODO - completely drop support for older core versions in 0.1.0 release - try: - import mycroft - except ImportError: - # skills don't require core anymore, running standalone - return True - - if instant: - try: - from ovos_core.version import OVOS_VERSION_BUILD, \ - OVOS_VERSION_MINOR, OVOS_VERSION_MAJOR - if OVOS_VERSION_MAJOR >= 1 or \ - OVOS_VERSION_MINOR > 0 or \ - OVOS_VERSION_BUILD >= 8: - return True # min version of ovos-core - 0.0.8 - except ImportError: - pass - else: - try: - # feature introduced before ovos_core module - from mycroft.version import OVOS_VERSION_BUILD, \ - OVOS_VERSION_MINOR, OVOS_VERSION_MAJOR - if OVOS_VERSION_MAJOR >= 1 or \ - OVOS_VERSION_MINOR > 0 or \ - OVOS_VERSION_BUILD >= 4: - return True # min version of ovos-core - 0.0.4 - except ImportError: - pass - - return False - - class OVOSSkill(BaseSkill, metaclass=_OVOSSkillMetaclass): """ New features: @@ -174,6 +143,19 @@ def deactivate(self): """ self._deactivate() + @staticmethod + def __acknowledge_classic(): + """ + Acknowledge a successful request. + + This method plays a sound to acknowledge a request that does not + require a verbal response. This is intended to provide simple feedback + to the user that their request was handled successfully. + """ + # use BaseSkill method, self.play_audio does not exist + return super().acknowledge() + + @backwards_compat(classic_core=__acknowledge_classic) def acknowledge(self): """ Acknowledge a successful request. @@ -186,26 +168,41 @@ def acknowledge(self): 'snd/acknowledge.mp3') self.play_audio(audio_file, instant=True) + def _play_audio_old(self, filename: str, instant: bool = False): + """ compat for ovos-core <= 0.0.7 """ + if instant: + LOG.warning("self.play_audio instant flag requires ovos-core >= 0.0.8, " + "falling back to local skill playback") + play_audio(filename).wait() + else: + message = dig_for_message() or Message("") + self.bus.emit(message.forward("mycroft.audio.queue", + {"filename": filename, # TODO - deprecate filename in ovos-audio + "uri": filename # new namespace + })) + + def _play_audio_classic(self, filename: str, instant: bool = False): + """ compat for classic mycroft-core """ + LOG.warning("self.play_audio requires ovos-core >= 0.0.4, " + "falling back to local skill playback") + play_audio(filename).wait() + + @backwards_compat(pre_008=_play_audio_old, classic_core=_play_audio_classic) def play_audio(self, filename: str, instant: bool = False): """ Queue and audio file for playback @param filename: File to play @param instant: if True audio will be played instantly instead of queued with TTS """ - if _can_play_audio(instant): - message = dig_for_message() or Message("") - if instant: - self.bus.emit(message.forward("mycroft.audio.play_sound", - {"uri": filename})) - else: - self.bus.emit(message.forward("mycroft.audio.queue", - {"filename": filename, # TODO - deprecate filename in ovos-audio - "uri": filename # new namespace - })) + message = dig_for_message() or Message("") + if instant: + self.bus.emit(message.forward("mycroft.audio.play_sound", + {"uri": filename})) else: - LOG.warning("self.play_audio requires ovos-core >= 0.0.4a45, " - "falling back to local skill playback") - play_audio(filename).wait() + self.bus.emit(message.forward("mycroft.audio.queue", + {"filename": filename, # TODO - deprecate filename in ovos-audio + "uri": filename # new namespace + })) def load_lang(self, root_directory: Optional[str] = None, lang: Optional[str] = None): @@ -296,7 +293,7 @@ def register_intent_layer(self, layer_name: str, self.intent_layers.update_layer(layer_name, [name]) # killable_events support - def send_stop_signal(self, stop_event: Optional[str] = None): + def __send_stop_signal_classic(self, stop_event: Optional[str] = None): """ Notify services to stop current execution @param stop_event: optional `stop` event name to forward @@ -313,13 +310,33 @@ def send_stop_signal(self, stop_event: Optional[str] = None): # Tell ovos-core to stop recording (not in mycroft-core) self.bus.emit(msg.forward('recognizer_loop:record_stop')) - # special non-ovos handling - if is_classic_core(): - # NOTE: mycroft does not have an event to stop recording - # this attempts to force a stop by sending silence to end STT step - self.bus.emit(Message('mycroft.mic.mute')) - waiter.wait(1.5) # the silence from muting should make STT stop recording - self.bus.emit(Message('mycroft.mic.unmute')) + # NOTE: mycroft does not have an event to stop recording + # this attempts to force a stop by sending silence to end STT step + self.bus.emit(Message('mycroft.mic.mute')) + waiter.wait(1.5) # the silence from muting should make STT stop recording + self.bus.emit(Message('mycroft.mic.unmute')) + + # TODO: register TTS events to track state instead of guessing + waiter.wait(0.5) # if TTS had not yet started + self.bus.emit(msg.forward("mycroft.audio.speech.stop")) + + @backwards_compat(classic_core=__send_stop_signal_classic) + def send_stop_signal(self, stop_event: Optional[str] = None): + """ + Notify services to stop current execution + @param stop_event: optional `stop` event name to forward + """ + waiter = Event() + msg = dig_for_message() or Message("mycroft.stop") + # stop event execution + if stop_event: + self.bus.emit(msg.forward(stop_event)) + + # stop TTS + self.bus.emit(msg.forward("mycroft.audio.speech.stop")) + + # Tell ovos-core to stop recording (not in mycroft-core) + self.bus.emit(msg.forward('recognizer_loop:record_stop')) # TODO: register TTS events to track state instead of guessing waiter.wait(0.5) # if TTS had not yet started From ebf2ebe9b89a106a38b2c40fd67f30dae081bf36 Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Thu, 21 Sep 2023 04:24:22 +0100 Subject: [PATCH 2/3] restor backwaeds compat method --- ovos_workshop/skills/base.py | 25 +++++++++++++++++++------ ovos_workshop/skills/mycroft_skill.py | 12 ------------ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/ovos_workshop/skills/base.py b/ovos_workshop/skills/base.py index 3da7b527..dca29406 100644 --- a/ovos_workshop/skills/base.py +++ b/ovos_workshop/skills/base.py @@ -26,16 +26,18 @@ from threading import Event, RLock from typing import List, Optional, Dict, Callable, Union -from ovos_bus_client import MessageBusClient -from ovos_bus_client.session import SessionManager from json_database import JsonStorage from lingua_franca.format import pronounce_number, join_list from lingua_franca.parse import yes_or_no, extract_number from ovos_backend_client.api import EmailApi, MetricsApi -from ovos_bus_client.message import Message, dig_for_message from ovos_config.config import Configuration from ovos_config.locations import get_xdg_config_save_path + +from ovos_bus_client import MessageBusClient +from ovos_bus_client.message import Message, dig_for_message +from ovos_bus_client.session import SessionManager from ovos_utils import camel_case_split +from ovos_utils import classproperty from ovos_utils.dialog import get_dialog, MustacheDialogRenderer from ovos_utils.enclosure.api import EnclosureAPI from ovos_utils.events import EventContainer, EventSchedulerInterface @@ -52,8 +54,6 @@ from ovos_utils.parse import match_one from ovos_utils.process_utils import RuntimeRequirements from ovos_utils.skills import get_non_properties -from ovos_utils import classproperty - from ovos_workshop.decorators.compat import backwards_compat from ovos_workshop.decorators.killable import AbortEvent from ovos_workshop.decorators.killable import killable_event, \ @@ -64,6 +64,18 @@ from ovos_workshop.settings import SkillSettingsManager +def is_classic_core(): + try: + from mycroft.version import OVOS_VERSION_STR + return False + except: + try: + import mycroft + return True + except: + return False + + # backwards compat alias class SkillNetworkRequirements(RuntimeRequirements): def __init__(self, *args, **kwargs): @@ -457,7 +469,7 @@ def _secondary_langs(self) -> List[str]: work in regular mycroft-core it was made private! """ return [lang.lower() for lang in self.config_core.get( - 'secondary_langs', []) if lang != self._core_lang] + 'secondary_langs', []) if lang != self._core_lang] # property not present in mycroft-core @property @@ -1000,6 +1012,7 @@ def __get_response(self): Returns: str: user's response or None on a timeout """ + # TODO: Support `message` signature like default? def converse(utterances, lang=None): converse.response = utterances[0] if utterances else None diff --git a/ovos_workshop/skills/mycroft_skill.py b/ovos_workshop/skills/mycroft_skill.py index ca587290..114ff936 100644 --- a/ovos_workshop/skills/mycroft_skill.py +++ b/ovos_workshop/skills/mycroft_skill.py @@ -23,18 +23,6 @@ from ovos_workshop.skills.base import BaseSkill -def is_classic_core(): - try: - from mycroft.version import OVOS_VERSION_STR - return False - except: - try: - import mycroft - return True - except: - return False - - class _SkillMetaclass(ABCMeta): """ This metaclass ensures we can load skills like regular python objects. From 456df9eba02775f650ea752f5c4a40e658baea11 Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Thu, 21 Sep 2023 14:01:51 +0100 Subject: [PATCH 3/3] tests --- ovos_workshop/skills/mycroft_skill.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ovos_workshop/skills/mycroft_skill.py b/ovos_workshop/skills/mycroft_skill.py index 114ff936..07e7abc0 100644 --- a/ovos_workshop/skills/mycroft_skill.py +++ b/ovos_workshop/skills/mycroft_skill.py @@ -20,7 +20,7 @@ from ovos_bus_client import MessageBusClient, Message from ovos_utils.log import LOG, log_deprecation, deprecated from ovos_workshop.decorators.compat import backwards_compat -from ovos_workshop.skills.base import BaseSkill +from ovos_workshop.skills.base import BaseSkill, is_classic_core class _SkillMetaclass(ABCMeta):