forked from platypush/platypush
[assistant.google] Propagate plugin name as a string to events.
This also makes it easier to programmatically stop conversations on `SpeechRecognizedEvent` with a matched phrase.
This commit is contained in:
parent
199b42584f
commit
0de322fb95
4 changed files with 69 additions and 35 deletions
|
@ -255,6 +255,8 @@ class Event(Message):
|
||||||
else:
|
else:
|
||||||
result.score = 0
|
result.score = 0
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""
|
"""
|
||||||
Overrides the str() operator and converts
|
Overrides the str() operator and converts
|
||||||
|
|
|
@ -1,15 +1,27 @@
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from platypush.context import get_plugin
|
||||||
from platypush.message.event import Event
|
from platypush.message.event import Event
|
||||||
|
|
||||||
|
|
||||||
class AssistantEvent(Event):
|
class AssistantEvent(Event):
|
||||||
"""Base class for assistant events"""
|
"""Base class for assistant events"""
|
||||||
|
|
||||||
def __init__(self, *args, assistant=None, **kwargs):
|
def __init__(self, *args, assistant: Optional[str] = None, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
"""
|
||||||
self._assistant = assistant
|
:param assistant: Name of the assistant plugin that triggered the event.
|
||||||
|
"""
|
||||||
|
super().__init__(*args, assistant=assistant, **kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _assistant(self):
|
||||||
|
return (
|
||||||
|
get_plugin(self.args.get('assistant'))
|
||||||
|
if self.args.get('assistant')
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ConversationStartEvent(AssistantEvent):
|
class ConversationStartEvent(AssistantEvent):
|
||||||
|
@ -26,10 +38,10 @@ class ConversationEndEvent(AssistantEvent):
|
||||||
Event triggered when a conversation ends
|
Event triggered when a conversation ends
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, with_follow_on_turn=False, **kwargs):
|
def __init__(self, *args, with_follow_on_turn: bool = False, **kwargs):
|
||||||
"""
|
"""
|
||||||
:param with_follow_on_turn: Set to true if the conversation expects a user follow-up, false otherwise
|
:param with_follow_on_turn: Set to true if the conversation expects a
|
||||||
:type with_follow_on_turn: str
|
user follow-up, false otherwise
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(*args, with_follow_on_turn=with_follow_on_turn, **kwargs)
|
super().__init__(*args, with_follow_on_turn=with_follow_on_turn, **kwargs)
|
||||||
|
@ -49,10 +61,9 @@ class ResponseEvent(ConversationEndEvent):
|
||||||
Event triggered when a response is processed by the assistant
|
Event triggered when a response is processed by the assistant
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, response_text, *args, **kwargs):
|
def __init__(self, *args, response_text: str, **kwargs):
|
||||||
"""
|
"""
|
||||||
:param response_text: Response text processed by the assistant
|
:param response_text: Response text processed by the assistant
|
||||||
:type response_text: str
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(*args, response_text=response_text, **kwargs)
|
super().__init__(*args, response_text=response_text, **kwargs)
|
||||||
|
@ -69,10 +80,9 @@ class SpeechRecognizedEvent(AssistantEvent):
|
||||||
Event triggered when a speech is recognized
|
Event triggered when a speech is recognized
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, phrase, *args, **kwargs):
|
def __init__(self, *args, phrase: str, **kwargs):
|
||||||
"""
|
"""
|
||||||
:param phrase: Recognized user phrase
|
:param phrase: Recognized user phrase
|
||||||
:type phrase: str
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(*args, phrase=phrase, **kwargs)
|
super().__init__(*args, phrase=phrase, **kwargs)
|
||||||
|
@ -81,7 +91,7 @@ class SpeechRecognizedEvent(AssistantEvent):
|
||||||
def matches_condition(self, condition):
|
def matches_condition(self, condition):
|
||||||
"""
|
"""
|
||||||
Overrides matches condition, and stops the conversation to prevent the
|
Overrides matches condition, and stops the conversation to prevent the
|
||||||
default assistant response if the event matched some event hook condition
|
default assistant response if the event matched some event hook condition.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
result = super().matches_condition(condition)
|
result = super().matches_condition(condition)
|
||||||
|
@ -167,20 +177,19 @@ class HotwordDetectedEvent(AssistantEvent):
|
||||||
Event triggered when a custom hotword is detected
|
Event triggered when a custom hotword is detected
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, hotword=None, **kwargs):
|
def __init__(self, *args, hotword: Optional[str] = None, **kwargs):
|
||||||
"""
|
"""
|
||||||
:param hotword: The detected user hotword
|
:param hotword: The detected user hotword.
|
||||||
:type hotword: str
|
|
||||||
"""
|
"""
|
||||||
super().__init__(*args, hotword=hotword, **kwargs)
|
super().__init__(*args, hotword=hotword, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class VolumeChangedEvent(AssistantEvent):
|
class VolumeChangedEvent(AssistantEvent):
|
||||||
"""
|
"""
|
||||||
Event triggered when the volume of the assistant changes
|
Event triggered when the volume of the assistant changes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, volume, *args, **kwargs):
|
def __init__(self, *args, volume: float, **kwargs):
|
||||||
super().__init__(*args, volume=volume, **kwargs)
|
super().__init__(*args, volume=volume, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from dataclasses import asdict, dataclass
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
import os
|
import os
|
||||||
from threading import Event
|
from threading import Event
|
||||||
from typing import Any, Collection, Dict, Optional
|
from typing import Any, Collection, Dict, Optional, Type
|
||||||
|
|
||||||
from platypush.context import get_bus, get_plugin
|
from platypush.context import get_bus, get_plugin
|
||||||
from platypush.entities.assistants import Assistant
|
from platypush.entities.assistants import Assistant
|
||||||
|
@ -67,7 +67,8 @@ class AssistantPlugin(Plugin, AssistantEntityManager, ABC):
|
||||||
tts_plugin: Optional[str] = None,
|
tts_plugin: Optional[str] = None,
|
||||||
tts_plugin_args: Optional[Dict[str, Any]] = None,
|
tts_plugin_args: Optional[Dict[str, Any]] = None,
|
||||||
conversation_start_sound: Optional[str] = None,
|
conversation_start_sound: Optional[str] = None,
|
||||||
**kwargs
|
stop_conversation_on_speech_match: bool = False,
|
||||||
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
:param tts_plugin: If set, the assistant will use this plugin (e.g.
|
:param tts_plugin: If set, the assistant will use this plugin (e.g.
|
||||||
|
@ -81,10 +82,19 @@ class AssistantPlugin(Plugin, AssistantEntityManager, ABC):
|
||||||
audio file when it detects a speech. The sound file will be played
|
audio file when it detects a speech. The sound file will be played
|
||||||
on the default audio output device. If not set, the assistant won't
|
on the default audio output device. If not set, the assistant won't
|
||||||
play any sound when it detects a speech.
|
play any sound when it detects a speech.
|
||||||
|
|
||||||
|
:param stop_conversation_on_speech_match: If set, the plugin will close the
|
||||||
|
conversation if the latest recognized speech matches a registered
|
||||||
|
:class:`platypush.message.event.assistant.SpeechRecognizedEvent` hook
|
||||||
|
with a phrase. This is usually set to ``True`` for
|
||||||
|
:class:`platypush.plugins.assistant.google.GoogleAssistantPlugin`,
|
||||||
|
as it overrides the default assistant response when a speech event is
|
||||||
|
actually handled on the application side.
|
||||||
"""
|
"""
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.tts_plugin = tts_plugin
|
self.tts_plugin = tts_plugin
|
||||||
self.tts_plugin_args = tts_plugin_args or {}
|
self.tts_plugin_args = tts_plugin_args or {}
|
||||||
|
self.stop_conversation_on_speech_match = stop_conversation_on_speech_match
|
||||||
self._conversation_start_sound = None
|
self._conversation_start_sound = None
|
||||||
if conversation_start_sound:
|
if conversation_start_sound:
|
||||||
self._conversation_start_sound = os.path.abspath(
|
self._conversation_start_sound = os.path.abspath(
|
||||||
|
@ -97,6 +107,7 @@ class AssistantPlugin(Plugin, AssistantEntityManager, ABC):
|
||||||
self._last_query: Optional[str] = None
|
self._last_query: Optional[str] = None
|
||||||
self._last_response: Optional[str] = None
|
self._last_response: Optional[str] = None
|
||||||
self._cur_alert_type: Optional[AlertType] = None
|
self._cur_alert_type: Optional[AlertType] = None
|
||||||
|
self._plugin_name = get_plugin_name_by_class(type(self))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _state(self) -> AssistantState:
|
def _state(self) -> AssistantState:
|
||||||
|
@ -189,35 +200,35 @@ class AssistantPlugin(Plugin, AssistantEntityManager, ABC):
|
||||||
|
|
||||||
audio.play(self._conversation_start_sound)
|
audio.play(self._conversation_start_sound)
|
||||||
|
|
||||||
def _send_event(self, event: AssistantEvent):
|
def _send_event(self, event_type: Type[AssistantEvent], **kwargs):
|
||||||
self.publish_entities([self])
|
self.publish_entities([self])
|
||||||
get_bus().post(event)
|
get_bus().post(event_type(assistant=self._plugin_name, **kwargs))
|
||||||
|
|
||||||
def _on_conversation_start(self):
|
def _on_conversation_start(self):
|
||||||
self._last_response = None
|
self._last_response = None
|
||||||
self._last_query = None
|
self._last_query = None
|
||||||
self._conversation_running.set()
|
self._conversation_running.set()
|
||||||
self._send_event(ConversationStartEvent(assistant=self))
|
self._send_event(ConversationStartEvent)
|
||||||
self._play_conversation_start_sound()
|
self._play_conversation_start_sound()
|
||||||
|
|
||||||
def _on_conversation_end(self):
|
def _on_conversation_end(self):
|
||||||
self._conversation_running.clear()
|
self._conversation_running.clear()
|
||||||
self._send_event(ConversationEndEvent(assistant=self))
|
self._send_event(ConversationEndEvent)
|
||||||
|
|
||||||
def _on_conversation_timeout(self):
|
def _on_conversation_timeout(self):
|
||||||
self._last_response = None
|
self._last_response = None
|
||||||
self._last_query = None
|
self._last_query = None
|
||||||
self._conversation_running.clear()
|
self._conversation_running.clear()
|
||||||
self._send_event(ConversationTimeoutEvent(assistant=self))
|
self._send_event(ConversationTimeoutEvent)
|
||||||
|
|
||||||
def _on_no_response(self):
|
def _on_no_response(self):
|
||||||
self._last_response = None
|
self._last_response = None
|
||||||
self._conversation_running.clear()
|
self._conversation_running.clear()
|
||||||
self._send_event(NoResponseEvent(assistant=self))
|
self._send_event(NoResponseEvent)
|
||||||
|
|
||||||
def _on_reponse_rendered(self, text: Optional[str]):
|
def _on_reponse_rendered(self, text: Optional[str]):
|
||||||
self._last_response = text
|
self._last_response = text
|
||||||
self._send_event(ResponseEvent(assistant=self, response_text=text))
|
self._send_event(ResponseEvent, response_text=text)
|
||||||
tts = self._get_tts_plugin()
|
tts = self._get_tts_plugin()
|
||||||
|
|
||||||
if tts and text:
|
if tts and text:
|
||||||
|
@ -227,39 +238,39 @@ class AssistantPlugin(Plugin, AssistantEntityManager, ABC):
|
||||||
def _on_speech_recognized(self, phrase: Optional[str]):
|
def _on_speech_recognized(self, phrase: Optional[str]):
|
||||||
phrase = (phrase or '').lower().strip()
|
phrase = (phrase or '').lower().strip()
|
||||||
self._last_query = phrase
|
self._last_query = phrase
|
||||||
self._send_event(SpeechRecognizedEvent(assistant=self, phrase=phrase))
|
self._send_event(SpeechRecognizedEvent, phrase=phrase)
|
||||||
|
|
||||||
def _on_alarm_start(self):
|
def _on_alarm_start(self):
|
||||||
self._cur_alert_type = AlertType.ALARM
|
self._cur_alert_type = AlertType.ALARM
|
||||||
self._send_event(AlarmStartedEvent(assistant=self))
|
self._send_event(AlarmStartedEvent)
|
||||||
|
|
||||||
def _on_alarm_end(self):
|
def _on_alarm_end(self):
|
||||||
self._cur_alert_type = None
|
self._cur_alert_type = None
|
||||||
self._send_event(AlarmEndEvent(assistant=self))
|
self._send_event(AlarmEndEvent)
|
||||||
|
|
||||||
def _on_timer_start(self):
|
def _on_timer_start(self):
|
||||||
self._cur_alert_type = AlertType.TIMER
|
self._cur_alert_type = AlertType.TIMER
|
||||||
self._send_event(TimerStartedEvent(assistant=self))
|
self._send_event(TimerStartedEvent)
|
||||||
|
|
||||||
def _on_timer_end(self):
|
def _on_timer_end(self):
|
||||||
self._cur_alert_type = None
|
self._cur_alert_type = None
|
||||||
self._send_event(TimerEndEvent(assistant=self))
|
self._send_event(TimerEndEvent)
|
||||||
|
|
||||||
def _on_alert_start(self):
|
def _on_alert_start(self):
|
||||||
self._cur_alert_type = AlertType.ALERT
|
self._cur_alert_type = AlertType.ALERT
|
||||||
self._send_event(AlertStartedEvent(assistant=self))
|
self._send_event(AlertStartedEvent)
|
||||||
|
|
||||||
def _on_alert_end(self):
|
def _on_alert_end(self):
|
||||||
self._cur_alert_type = None
|
self._cur_alert_type = None
|
||||||
self._send_event(AlertEndEvent(assistant=self))
|
self._send_event(AlertEndEvent)
|
||||||
|
|
||||||
def _on_mute(self):
|
def _on_mute(self):
|
||||||
self._is_muted = True
|
self._is_muted = True
|
||||||
self._send_event(MicMutedEvent(assistant=self))
|
self._send_event(MicMutedEvent)
|
||||||
|
|
||||||
def _on_unmute(self):
|
def _on_unmute(self):
|
||||||
self._is_muted = False
|
self._is_muted = False
|
||||||
self._send_event(MicUnmutedEvent(assistant=self))
|
self._send_event(MicUnmutedEvent)
|
||||||
|
|
||||||
def _on_mute_changed(self, value: bool):
|
def _on_mute_changed(self, value: bool):
|
||||||
if value:
|
if value:
|
||||||
|
|
|
@ -76,6 +76,7 @@ class AssistantGooglePlugin(AssistantPlugin, RunnablePlugin):
|
||||||
self,
|
self,
|
||||||
credentials_file: Optional[str] = None,
|
credentials_file: Optional[str] = None,
|
||||||
device_model_id: str = 'Platypush',
|
device_model_id: str = 'Platypush',
|
||||||
|
stop_conversation_on_speech_match: bool = True,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
|
@ -94,9 +95,20 @@ class AssistantGooglePlugin(AssistantPlugin, RunnablePlugin):
|
||||||
:param device_model_id: The device model ID that identifies the device
|
:param device_model_id: The device model ID that identifies the device
|
||||||
where the assistant is running (default: Platypush). It can be a
|
where the assistant is running (default: Platypush). It can be a
|
||||||
custom string.
|
custom string.
|
||||||
|
|
||||||
|
:param stop_conversation_on_speech_match: If set, the plugin will close the
|
||||||
|
conversation if the latest recognized speech matches a registered
|
||||||
|
:class:`platypush.message.event.assistant.SpeechRecognizedEvent` hook
|
||||||
|
with a phrase. This is usually set to ``True`` for
|
||||||
|
:class:`platypush.plugins.assistant.google.GoogleAssistantPlugin`,
|
||||||
|
as it overrides the default assistant response when a speech event is
|
||||||
|
actually handled on the application side.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(**kwargs)
|
super().__init__(
|
||||||
|
stop_conversation_on_speech_match=stop_conversation_on_speech_match,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
self._credentials_file = credentials_file
|
self._credentials_file = credentials_file
|
||||||
self.device_model_id = device_model_id
|
self.device_model_id = device_model_id
|
||||||
self.credentials = None
|
self.credentials = None
|
||||||
|
|
Loading…
Reference in a new issue