Fixed some pylint warnings
This commit is contained in:
parent
a0ac30e9b6
commit
81803a364d
6 changed files with 113 additions and 70 deletions
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2017, Fabio Manganiello
|
Copyright (c) 2017, 2018 Fabio Manganiello
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
"""
|
||||||
|
Platypush
|
||||||
|
|
||||||
|
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||||
|
.. license:: MIT
|
||||||
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
@ -18,30 +25,29 @@ from .message.response import Response
|
||||||
__author__ = 'Fabio Manganiello <blacklight86@gmail.com>'
|
__author__ = 'Fabio Manganiello <blacklight86@gmail.com>'
|
||||||
__version__ = '0.9'
|
__version__ = '0.9'
|
||||||
|
|
||||||
#-----------#
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
class Daemon:
|
||||||
|
|
||||||
class Daemon(object):
|
|
||||||
""" Main class for the Platypush daemon """
|
""" Main class for the Platypush daemon """
|
||||||
|
|
||||||
""" Configuration file (default: either ~/.config/platypush/config.yaml or
|
# Configuration file (default: either ~/.config/platypush/config.yaml or
|
||||||
/etc/platypush/config.yaml) """
|
# /etc/platypush/config.yaml
|
||||||
config_file = None
|
config_file = None
|
||||||
|
|
||||||
""" Application bus. It's an internal queue where:
|
# Application bus. It's an internal queue where:
|
||||||
- backends will post the messages they receive
|
# - backends will post the messages they receive
|
||||||
- plugins will post the responses they process """
|
# - plugins will post the responses they process
|
||||||
bus = None
|
bus = None
|
||||||
|
|
||||||
""" backend_name => backend_obj map """
|
# backend_name => backend_obj map
|
||||||
backends = None
|
backends = None
|
||||||
|
|
||||||
""" number of executions retries before a request fails """
|
# number of executions retries before a request fails
|
||||||
n_tries = 2
|
n_tries = 2
|
||||||
|
|
||||||
def __init__(self, config_file=None, requests_to_process=None):
|
def __init__(self, config_file=None, requests_to_process=None):
|
||||||
""" Constructor
|
"""
|
||||||
|
Constructor
|
||||||
Params:
|
Params:
|
||||||
config_file -- Configuration file override (default: None)
|
config_file -- Configuration file override (default: None)
|
||||||
requests_to_process -- Exit after processing the specified number
|
requests_to_process -- Exit after processing the specified number
|
||||||
|
@ -58,7 +64,8 @@ class Daemon(object):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def build_from_cmdline(cls, args):
|
def build_from_cmdline(cls, args):
|
||||||
""" Build the app from command line arguments.
|
"""
|
||||||
|
Build the app from command line arguments.
|
||||||
Params:
|
Params:
|
||||||
args -- Your sys.argv[1:] [List of strings]
|
args -- Your sys.argv[1:] [List of strings]
|
||||||
"""
|
"""
|
||||||
|
@ -70,30 +77,34 @@ class Daemon(object):
|
||||||
return cls(config_file=opts.config)
|
return cls(config_file=opts.config)
|
||||||
|
|
||||||
def on_message(self):
|
def on_message(self):
|
||||||
""" Default message handler """
|
"""
|
||||||
|
Default message handler
|
||||||
|
"""
|
||||||
|
|
||||||
def _f(msg):
|
def _f(msg):
|
||||||
""" on_message closure
|
"""
|
||||||
|
on_message closure
|
||||||
Params:
|
Params:
|
||||||
msg -- platypush.message.Message instance """
|
msg -- platypush.message.Message instance
|
||||||
|
"""
|
||||||
|
|
||||||
if isinstance(msg, Request):
|
if isinstance(msg, Request):
|
||||||
try:
|
try:
|
||||||
msg.execute(n_tries=self.n_tries)
|
msg.execute(n_tries=self.n_tries)
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
logger.info('Dropped unauthorized request: {}'.format(msg))
|
LOGGER.info('Dropped unauthorized request: {}'.format(msg))
|
||||||
|
|
||||||
self.processed_requests += 1
|
self.processed_requests += 1
|
||||||
if self.requests_to_process \
|
if self.requests_to_process \
|
||||||
and self.processed_requests >= self.requests_to_process:
|
and self.processed_requests >= self.requests_to_process:
|
||||||
self.stop_app()
|
self.stop_app()
|
||||||
elif isinstance(msg, Response):
|
elif isinstance(msg, Response):
|
||||||
logger.info('Received response: {}'.format(msg))
|
LOGGER.info('Received response: {}'.format(msg))
|
||||||
elif isinstance(msg, StopEvent) and msg.targets_me():
|
elif isinstance(msg, StopEvent) and msg.targets_me():
|
||||||
logger.info('Received STOP event: {}'.format(msg))
|
LOGGER.info('Received STOP event: {}'.format(msg))
|
||||||
self.stop_app()
|
self.stop_app()
|
||||||
elif isinstance(msg, Event):
|
elif isinstance(msg, Event):
|
||||||
logger.info('Received event: {}'.format(msg))
|
LOGGER.info('Received event: {}'.format(msg))
|
||||||
self.event_processor.process_event(msg)
|
self.event_processor.process_event(msg)
|
||||||
|
|
||||||
return _f
|
return _f
|
||||||
|
@ -124,17 +135,20 @@ class Daemon(object):
|
||||||
# Poll for messages on the bus
|
# Poll for messages on the bus
|
||||||
try:
|
try:
|
||||||
self.bus.poll()
|
self.bus.poll()
|
||||||
except KeyboardInterrupt as e:
|
except KeyboardInterrupt:
|
||||||
logger.info('SIGINT received, terminating application')
|
LOGGER.info('SIGINT received, terminating application')
|
||||||
finally:
|
finally:
|
||||||
self.stop_app()
|
self.stop_app()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
"""
|
||||||
|
Platypush daemon main
|
||||||
|
"""
|
||||||
|
|
||||||
print('Starting platypush v.{}'.format(__version__))
|
print('Starting platypush v.{}'.format(__version__))
|
||||||
app = Daemon.build_from_cmdline(sys.argv[1:])
|
app = Daemon.build_from_cmdline(sys.argv[1:])
|
||||||
app.start()
|
app.start()
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
"""
|
||||||
|
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||||
|
.. license:: MIT
|
||||||
|
"""
|
||||||
|
|
||||||
import importlib
|
import importlib
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
"""
|
||||||
|
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||||
|
.. license:: MIT
|
||||||
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
@ -36,10 +41,11 @@ class AssistantGoogleBackend(Backend):
|
||||||
* **google-assistant-sdk[samples]** (``pip install google-assistant-sdk[samples]``)
|
* **google-assistant-sdk[samples]** (``pip install google-assistant-sdk[samples]``)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, credentials_file=os.path.join(
|
def __init__(self,
|
||||||
os.path.expanduser('~/.config'),
|
credentials_file=os.path.join(
|
||||||
'google-oauthlib-tool', 'credentials.json'),
|
os.path.expanduser('~/.config'),
|
||||||
device_model_id='Platypush', **kwargs):
|
'google-oauthlib-tool', 'credentials.json'),
|
||||||
|
device_model_id='Platypush', **kwargs):
|
||||||
"""
|
"""
|
||||||
:param credentials_file: Path to the Google OAuth credentials file (default: ~/.config/google-oauthlib-tool/credentials.json). See https://developers.google.com/assistant/sdk/guides/library/python/embed/install-sample#generate_credentials for how to get your own credentials file.
|
:param credentials_file: Path to the Google OAuth credentials file (default: ~/.config/google-oauthlib-tool/credentials.json). See https://developers.google.com/assistant/sdk/guides/library/python/embed/install-sample#generate_credentials for how to get your own credentials file.
|
||||||
:type credentials_file: str
|
:type credentials_file: str
|
||||||
|
@ -80,12 +86,14 @@ class AssistantGoogleBackend(Backend):
|
||||||
|
|
||||||
def start_conversation(self):
|
def start_conversation(self):
|
||||||
""" Starts an assistant conversation """
|
""" Starts an assistant conversation """
|
||||||
if self.assistant: self.assistant.start_conversation()
|
if self.assistant:
|
||||||
|
self.assistant.start_conversation()
|
||||||
|
|
||||||
|
|
||||||
def stop_conversation(self):
|
def stop_conversation(self):
|
||||||
""" Stops an assistant conversation """
|
""" Stops an assistant conversation """
|
||||||
if self.assistant: self.assistant.stop_conversation()
|
if self.assistant:
|
||||||
|
self.assistant.stop_conversation()
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -98,4 +106,3 @@ class AssistantGoogleBackend(Backend):
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
|
"""
|
||||||
|
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||||
|
.. license:: MIT
|
||||||
|
"""
|
||||||
|
|
||||||
|
import concurrent
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
import google.auth.transport.grpc
|
import google.auth.transport.grpc
|
||||||
import google.auth.transport.requests
|
import google.auth.transport.requests
|
||||||
import google.oauth2.credentials
|
import google.oauth2.credentials
|
||||||
|
from google.assistant.embedded.v1alpha2 import embedded_assistant_pb2, embedded_assistant_pb2_grpc
|
||||||
|
|
||||||
import googlesamples.assistant.grpc.audio_helpers as audio_helpers
|
import googlesamples.assistant.grpc.audio_helpers as audio_helpers
|
||||||
import googlesamples.assistant.grpc.device_helpers as device_helpers
|
import googlesamples.assistant.grpc.device_helpers as device_helpers
|
||||||
import googlesamples.assistant.grpc.assistant_helpers as assistant_helpers
|
import googlesamples.assistant.grpc.assistant_helpers as assistant_helpers
|
||||||
|
|
||||||
from tenacity import retry, stop_after_attempt, retry_if_exception
|
from tenacity import retry, stop_after_attempt, retry_if_exception
|
||||||
from google.assistant.embedded.v1alpha2 import (
|
|
||||||
embedded_assistant_pb2,
|
|
||||||
embedded_assistant_pb2_grpc
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
from platypush.backend import Backend
|
from platypush.backend import Backend
|
||||||
|
@ -53,15 +55,16 @@ class AssistantGooglePushtotalkBackend(Backend):
|
||||||
audio_flush_size = audio_helpers.DEFAULT_AUDIO_DEVICE_FLUSH_SIZE
|
audio_flush_size = audio_helpers.DEFAULT_AUDIO_DEVICE_FLUSH_SIZE
|
||||||
grpc_deadline = 60 * 3 + 5
|
grpc_deadline = 60 * 3 + 5
|
||||||
|
|
||||||
def __init__(self, credentials_file=os.path.join(
|
def __init__(self, *args,
|
||||||
os.path.expanduser('~'), '.config',
|
credentials_file=os.path.join(
|
||||||
'google-oauthlib-tool', 'credentials.json'),
|
os.path.expanduser('~'), '.config',
|
||||||
device_config=os.path.join(
|
'google-oauthlib-tool', 'credentials.json'),
|
||||||
os.path.expanduser('~'), '.config', 'googlesamples-assistant',
|
device_config=os.path.join(
|
||||||
'device_config.json'),
|
os.path.expanduser('~'), '.config', 'googlesamples-assistant',
|
||||||
lang='en-US',
|
'device_config.json'),
|
||||||
conversation_start_fifo = os.path.join(os.path.sep, 'tmp', 'pushtotalk.fifo'),
|
lang='en-US',
|
||||||
*args, **kwargs):
|
conversation_start_fifo=os.path.join(os.path.sep, 'tmp', 'pushtotalk.fifo'),
|
||||||
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
:param credentials_file: Path to the Google OAuth credentials file (default: ~/.config/google-oauthlib-tool/credentials.json). See https://developers.google.com/assistant/sdk/guides/library/python/embed/install-sample#generate_credentials for how to get your own credentials file.
|
:param credentials_file: Path to the Google OAuth credentials file (default: ~/.config/google-oauthlib-tool/credentials.json). See https://developers.google.com/assistant/sdk/guides/library/python/embed/install-sample#generate_credentials for how to get your own credentials file.
|
||||||
:type credentials_file: str
|
:type credentials_file: str
|
||||||
|
@ -98,10 +101,10 @@ class AssistantGooglePushtotalkBackend(Backend):
|
||||||
**json.load(f))
|
**json.load(f))
|
||||||
http_request = google.auth.transport.requests.Request()
|
http_request = google.auth.transport.requests.Request()
|
||||||
credentials.refresh(http_request)
|
credentials.refresh(http_request)
|
||||||
except:
|
except Exception as ex:
|
||||||
self.logger.error('Error loading credentials: %s', e)
|
self.logger.error('Error loading credentials: %s', str(ex))
|
||||||
self.logger.error('Run google-oauthlib-tool to initialize '
|
self.logger.error('Run google-oauthlib-tool to initialize '
|
||||||
'new OAuth 2.0 credentials.')
|
'new OAuth 2.0 credentials.')
|
||||||
raise
|
raise
|
||||||
|
|
||||||
# Create an authorized gRPC channel.
|
# Create an authorized gRPC channel.
|
||||||
|
@ -151,43 +154,44 @@ class AssistantGooglePushtotalkBackend(Backend):
|
||||||
self.conversation_stream.stop_playback()
|
self.conversation_stream.stop_playback()
|
||||||
self.bus.post(ConversationEndEvent())
|
self.bus.post(ConversationEndEvent())
|
||||||
|
|
||||||
def send_message(self, msg):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_conversation_start(self):
|
def on_conversation_start(self):
|
||||||
|
""" Conversation start handler """
|
||||||
self.bus.post(ConversationStartEvent())
|
self.bus.post(ConversationStartEvent())
|
||||||
|
|
||||||
def on_conversation_end(self):
|
def on_conversation_end(self):
|
||||||
|
""" Conversation end handler """
|
||||||
self.bus.post(ConversationEndEvent())
|
self.bus.post(ConversationEndEvent())
|
||||||
|
|
||||||
def on_speech_recognized(self, speech):
|
def on_speech_recognized(self, speech):
|
||||||
|
""" Speech recognized handler """
|
||||||
self.bus.post(SpeechRecognizedEvent(phrase=speech))
|
self.bus.post(SpeechRecognizedEvent(phrase=speech))
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
""" Backend executor """
|
||||||
super().run()
|
super().run()
|
||||||
|
|
||||||
with SampleAssistant(self.lang, self.device_model_id, self.device_id,
|
with SampleAssistant(self.lang, self.device_model_id, self.device_id,
|
||||||
self.conversation_stream,
|
self.conversation_stream,
|
||||||
self.grpc_channel, self.grpc_deadline,
|
self.grpc_channel, self.grpc_deadline,
|
||||||
self.device_handler,
|
self.device_handler,
|
||||||
on_conversation_start=self.on_conversation_start,
|
on_conversation_start=self.on_conversation_start,
|
||||||
on_conversation_end=self.on_conversation_end,
|
on_conversation_end=self.on_conversation_end,
|
||||||
on_speech_recognized=self.on_speech_recognized) as self.assistant:
|
on_speech_recognized=self.on_speech_recognized) as self.assistant:
|
||||||
while not self.should_stop():
|
while not self.should_stop():
|
||||||
with open(self.conversation_start_fifo, 'r') as f:
|
with open(self.conversation_start_fifo, 'r') as f:
|
||||||
for line in f: pass
|
f.read()
|
||||||
|
|
||||||
self.logger.info('Received conversation start event')
|
self.logger.info('Received conversation start event')
|
||||||
continue_conversation = True
|
continue_conversation = True
|
||||||
user_request = None
|
|
||||||
|
|
||||||
while continue_conversation:
|
while continue_conversation:
|
||||||
(user_request, continue_conversation) = self.assistant.assist()
|
(user_request, continue_conversation) = self.assistant.assist()
|
||||||
|
self.logger('User request: {}'.format(user_request))
|
||||||
|
|
||||||
self.on_conversation_end()
|
self.on_conversation_end()
|
||||||
|
|
||||||
|
|
||||||
class SampleAssistant(object):
|
class SampleAssistant:
|
||||||
"""Sample Assistant that supports conversations and device actions.
|
"""Sample Assistant that supports conversations and device actions.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -242,11 +246,14 @@ class SampleAssistant(object):
|
||||||
if e:
|
if e:
|
||||||
return False
|
return False
|
||||||
self.conversation_stream.close()
|
self.conversation_stream.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def is_grpc_error_unavailable(e):
|
def is_grpc_error_unavailable(e):
|
||||||
|
""" Returns True if the gRPC is not available """
|
||||||
is_grpc_error = isinstance(e, grpc.RpcError)
|
is_grpc_error = isinstance(e, grpc.RpcError)
|
||||||
if is_grpc_error and (e.code() == grpc.StatusCode.UNAVAILABLE):
|
if is_grpc_error and (e.code() == grpc.StatusCode.UNAVAILABLE):
|
||||||
self.logger.error('grpc unavailable error: %s', e)
|
print('grpc unavailable error: {}'.format(e))
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -288,7 +295,7 @@ class SampleAssistant(object):
|
||||||
|
|
||||||
self.logger.info('Transcript of user request: "%s".', user_request)
|
self.logger.info('Transcript of user request: "%s".', user_request)
|
||||||
self.logger.info('Playing assistant response.')
|
self.logger.info('Playing assistant response.')
|
||||||
if len(resp.audio_out.audio_data) > 0:
|
if resp.audio_out.audio_data:
|
||||||
self.conversation_stream.write(resp.audio_out.audio_data)
|
self.conversation_stream.write(resp.audio_out.audio_data)
|
||||||
if resp.dialog_state_out.conversation_state:
|
if resp.dialog_state_out.conversation_state:
|
||||||
conversation_state = resp.dialog_state_out.conversation_state
|
conversation_state = resp.dialog_state_out.conversation_state
|
||||||
|
@ -311,7 +318,7 @@ class SampleAssistant(object):
|
||||||
if fs:
|
if fs:
|
||||||
device_actions_futures.extend(fs)
|
device_actions_futures.extend(fs)
|
||||||
|
|
||||||
if len(device_actions_futures):
|
if device_actions_futures:
|
||||||
self.logger.info('Waiting for device executions to complete.')
|
self.logger.info('Waiting for device executions to complete.')
|
||||||
concurrent.futures.wait(device_actions_futures)
|
concurrent.futures.wait(device_actions_futures)
|
||||||
|
|
||||||
|
@ -319,7 +326,7 @@ class SampleAssistant(object):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.conversation_stream.stop_playback()
|
self.conversation_stream.stop_playback()
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if user_request and self.on_speech_recognized:
|
if user_request and self.on_speech_recognized:
|
||||||
|
@ -331,12 +338,14 @@ class SampleAssistant(object):
|
||||||
"""Yields: AssistRequest messages to send to the API."""
|
"""Yields: AssistRequest messages to send to the API."""
|
||||||
|
|
||||||
dialog_state_in = embedded_assistant_pb2.DialogStateIn(
|
dialog_state_in = embedded_assistant_pb2.DialogStateIn(
|
||||||
language_code=self.language_code,
|
language_code=self.language_code,
|
||||||
conversation_state=b''
|
conversation_state=b''
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.conversation_state:
|
if self.conversation_state:
|
||||||
self.logger.debug('Sending conversation state.')
|
self.logger.debug('Sending conversation state.')
|
||||||
dialog_state_in.conversation_state = self.conversation_state
|
dialog_state_in.conversation_state = self.conversation_state
|
||||||
|
|
||||||
config = embedded_assistant_pb2.AssistConfig(
|
config = embedded_assistant_pb2.AssistConfig(
|
||||||
audio_in_config=embedded_assistant_pb2.AudioInConfig(
|
audio_in_config=embedded_assistant_pb2.AudioInConfig(
|
||||||
encoding='LINEAR16',
|
encoding='LINEAR16',
|
||||||
|
@ -353,6 +362,7 @@ class SampleAssistant(object):
|
||||||
device_model_id=self.device_model_id,
|
device_model_id=self.device_model_id,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# The first AssistRequest must contain the AssistConfig
|
# The first AssistRequest must contain the AssistConfig
|
||||||
# and no audio data.
|
# and no audio data.
|
||||||
yield embedded_assistant_pb2.AssistRequest(config=config)
|
yield embedded_assistant_pb2.AssistRequest(config=config)
|
||||||
|
@ -362,4 +372,3 @@ class SampleAssistant(object):
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
"""
|
||||||
|
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||||
|
.. license:: MIT
|
||||||
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
@ -57,6 +62,10 @@ class AssistantSnowboyBackend(Backend):
|
||||||
self.logger.info('Initialized Snowboy hotword detection')
|
self.logger.info('Initialized Snowboy hotword detection')
|
||||||
|
|
||||||
def hotword_detected(self):
|
def hotword_detected(self):
|
||||||
|
"""
|
||||||
|
Callback called on hotword detection
|
||||||
|
"""
|
||||||
|
|
||||||
def callback():
|
def callback():
|
||||||
self.bus.post(HotwordDetectedEvent(hotword=self.hotword))
|
self.bus.post(HotwordDetectedEvent(hotword=self.hotword))
|
||||||
return callback
|
return callback
|
||||||
|
@ -67,4 +76,3 @@ class AssistantSnowboyBackend(Backend):
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue