diff --git a/mycroft/client/enclosure/enclosure.py b/mycroft/client/enclosure/enclosure.py
index b55852fc3e..c782622b7f 100644
--- a/mycroft/client/enclosure/enclosure.py
+++ b/mycroft/client/enclosure/enclosure.py
@@ -16,11 +16,12 @@
# along with Mycroft Core. If not, see .
-import serial
import sys
from Queue import Queue
from threading import Thread
+import serial
+
from mycroft.client.enclosure.arduino import EnclosureArduino
from mycroft.client.enclosure.eyes import EnclosureEyes
from mycroft.client.enclosure.mouth import EnclosureMouth
@@ -143,7 +144,7 @@ class Enclosure:
self.eyes = EnclosureEyes(self.client, self.writer)
self.mouth = EnclosureMouth(self.client, self.writer)
self.system = EnclosureArduino(self.client, self.writer)
- self.__init_events()
+ self.__register_events()
def __init_serial(self):
try:
@@ -161,11 +162,30 @@ class Enclosure:
"It is not possible to connect to serial port: " + self.port)
raise
- def __init_events(self):
+ def __register_events(self):
+ self.client.on('mycroft.paired', self.__update_events)
+ self.client.on('recognizer_loop:wakeword', self.eyes.blink)
+ self.__register_mouth_events()
+
+ def __register_mouth_events(self):
self.client.on('recognizer_loop:listening', self.mouth.listen)
self.client.on('recognizer_loop:audio_output_start', self.mouth.talk)
self.client.on('recognizer_loop:audio_output_end', self.mouth.reset)
- self.client.on('recognizer_loop:wakeword', self.eyes.blink)
+
+ def __remove_mouth_events(self):
+ self.client.remove('recognizer_loop:listening', self.mouth.listen)
+ self.client.remove('recognizer_loop:audio_output_start',
+ self.mouth.talk)
+ self.client.remove('recognizer_loop:audio_output_end',
+ self.mouth.reset)
+ self.mouth.reset()
+
+ def __update_events(self, event=None):
+ if event and event.metadata:
+ if event.metadata.get('paired', False):
+ self.__register_mouth_events()
+ else:
+ self.__remove_mouth_events()
def run(self):
try:
diff --git a/mycroft/client/speech/listener.py b/mycroft/client/speech/listener.py
index 2b4fc6ef6f..b0bc8481eb 100644
--- a/mycroft/client/speech/listener.py
+++ b/mycroft/client/speech/listener.py
@@ -81,7 +81,7 @@ class AudioConsumer(threading.Thread):
"""
# In seconds, the minimum audio size to be sent to remote STT
- MIN_AUDIO_SIZE = 1.0
+ MIN_AUDIO_SIZE = 0.5
def __init__(self, state, queue, emitter, wakeup_recognizer,
mycroft_recognizer, remote_recognizer):
diff --git a/mycroft/messagebus/client/ws.py b/mycroft/messagebus/client/ws.py
index 1a90a31f57..979af83959 100644
--- a/mycroft/messagebus/client/ws.py
+++ b/mycroft/messagebus/client/ws.py
@@ -17,14 +17,15 @@
import json
-from multiprocessing.pool import ThreadPool
import time
-from mycroft.configuration.config import ConfigurationManager
-from mycroft.messagebus.message import Message
-import mycroft.util.log
+from multiprocessing.pool import ThreadPool
+
from pyee import EventEmitter
from websocket import WebSocketApp
+import mycroft.util.log
+from mycroft.configuration.config import ConfigurationManager
+from mycroft.messagebus.message import Message
from mycroft.util import str2bool
__author__ = 'seanfitz'
@@ -99,6 +100,9 @@ class WebsocketClient(object):
def once(self, event_name, func):
self.emitter.once(event_name, func)
+ def remove(self, event_name, func):
+ self.emitter.remove_listener(event_name, func)
+
def run_forever(self):
self.client.run_forever()
diff --git a/mycroft/pairing/client.py b/mycroft/pairing/client.py
index 58b773628c..62a2af3c33 100644
--- a/mycroft/pairing/client.py
+++ b/mycroft/pairing/client.py
@@ -17,6 +17,7 @@
import shortuuid
+
from mycroft.configuration.config import ConfigurationManager
from mycroft.identity import IdentityManager
from mycroft.messagebus.client.ws import WebsocketClient
@@ -34,6 +35,7 @@ def generate_pairing_code():
class DevicePairingClient(object):
def __init__(self, config=_config, pairing_code=None):
self.config = config
+ self.paired = False
self.ws_client = WebsocketClient(host=config.get("host"),
port=config.get("port"),
path=config.get("route"),
@@ -53,6 +55,7 @@ class DevicePairingClient(object):
identity.owner = register_payload.get('user')
self.identity_manager.update(identity)
self.ws_client.close()
+ self.paired = True
def send_device_info(self):
msg = Message("device_info",
@@ -63,7 +66,8 @@ class DevicePairingClient(object):
self.ws_client.emit(msg)
- def print_error(self, message):
+ @staticmethod
+ def print_error(message):
print(repr(message))
def run(self):
@@ -76,5 +80,6 @@ class DevicePairingClient(object):
def main():
DevicePairingClient().run()
+
if __name__ == "__main__":
main()
diff --git a/mycroft/skills/pairing/__init__.py b/mycroft/skills/pairing/__init__.py
index f3de1bd931..acc8ac88b1 100644
--- a/mycroft/skills/pairing/__init__.py
+++ b/mycroft/skills/pairing/__init__.py
@@ -15,11 +15,12 @@
# You should have received a copy of the GNU General Public License
# along with Mycroft Core. If not, see .
-import threading
+from threading import Thread
from adapt.intent import IntentBuilder
from os.path import dirname
+from mycroft.messagebus.message import Message
from mycroft.pairing.client import DevicePairingClient
from mycroft.skills.core import MycroftSkill
@@ -27,21 +28,41 @@ from mycroft.skills.core import MycroftSkill
class PairingSkill(MycroftSkill):
def __init__(self):
super(PairingSkill, self).__init__(name="PairingSkill")
+ self.client = None
+ self.displaying = False
def initialize(self):
intent = IntentBuilder("PairingIntent").require(
- "DevicePairingPhrase").build()
+ "DevicePairingPhrase").build()
self.load_data_files(dirname(__file__))
self.register_intent(intent, handler=self.handle_pairing_request)
def handle_pairing_request(self, message):
- pairing_client = DevicePairingClient()
- pairing_code = pairing_client.pairing_code
- threading.Thread(target=pairing_client.run).start()
- self.enclosure.mouth_text("Pairing code is: " + pairing_code)
+ if not self.client:
+ self.displaying = False
+ self.__emit_paired(False)
+ self.client = DevicePairingClient()
+ Thread(target=self.client.run).start()
+ self.emitter.on("recognizer_loop:audio_output_start",
+ self.__display_pairing_code)
self.speak_dialog(
- "pairing.instructions",
- data={"pairing_code": ', ,'.join(pairing_code)})
+ "pairing.instructions",
+ data={"pairing_code": ', ,'.join(self.client.pairing_code)})
+
+ def __display_pairing_code(self, event=None):
+ if self.client.paired:
+ self.enclosure.mouth_talk()
+ self.client = None
+ self.__emit_paired(True)
+ self.emitter.remove("recognizer_loop:audio_output_start",
+ self.__display_pairing_code)
+ elif not self.displaying:
+ self.displaying = True
+ self.enclosure.mouth_text(self.client.pairing_code)
+
+ def __emit_paired(self, paired):
+ msg = Message('mycroft.paired', metadata={'paired': paired})
+ self.emitter.emit(msg)
def stop(self):
pass
diff --git a/mycroft/skills/welcome/__init__.py b/mycroft/skills/welcome/__init__.py
new file mode 100644
index 0000000000..1e0976c55a
--- /dev/null
+++ b/mycroft/skills/welcome/__init__.py
@@ -0,0 +1,48 @@
+# Copyright 2016 Mycroft AI, Inc.
+#
+# This file is part of Mycroft Core.
+#
+# Mycroft Core is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mycroft Core is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mycroft Core. If not, see .
+
+from os.path import dirname
+
+from adapt.intent import IntentBuilder
+from mycroft.skills.core import MycroftSkill
+from mycroft.util.log import getLogger
+
+__author__ = 'eward'
+
+LOGGER = getLogger(__name__)
+
+
+class WelcomeSkill(MycroftSkill):
+
+ def __init__(self):
+ super(WelcomeSkill, self).__init__(name="WelcomeSkill")
+
+ def initialize(self):
+ self.load_data_files(dirname(__file__))
+
+ welcome_intent = IntentBuilder("WelcIntent").require("WelcKey").build()
+ self.register_intent(welcome_intent, self.handle_welcome_intent)
+
+ def handle_welcome_intent(self, message):
+ self.speak_dialog('Welcome')
+
+ def stop(self):
+ pass
+
+
+def create_skill():
+ return WelcomeSkill()
diff --git a/mycroft/skills/welcome/dialog/en-us/Welcome.dialog b/mycroft/skills/welcome/dialog/en-us/Welcome.dialog
new file mode 100644
index 0000000000..855fb3e3bd
--- /dev/null
+++ b/mycroft/skills/welcome/dialog/en-us/Welcome.dialog
@@ -0,0 +1 @@
+You're welcome.
diff --git a/mycroft/skills/welcome/vocab/en-us/WelcKey.voc b/mycroft/skills/welcome/vocab/en-us/WelcKey.voc
new file mode 100644
index 0000000000..d280f1b73a
--- /dev/null
+++ b/mycroft/skills/welcome/vocab/en-us/WelcKey.voc
@@ -0,0 +1,2 @@
+thank you
+thanks
diff --git a/test/client/audio_consumer_test.py b/test/client/audio_consumer_test.py
index 8a6ba53843..db43531d22 100644
--- a/test/client/audio_consumer_test.py
+++ b/test/client/audio_consumer_test.py
@@ -190,3 +190,41 @@ class AudioConsumerTest(unittest.TestCase):
self.assertIsNotNone(utterances)
self.assertTrue(len(utterances) == 1)
self.assertEquals("what's the weather next week", utterances[0])
+
+ def test_stop(self):
+ self.queue.put(self.__create_sample_from_test_file('mycroft'))
+ self.consumer.read_audio()
+
+ self.queue.put(self.__create_sample_from_test_file('stop'))
+ self.recognizer.set_transcriptions(["stop"])
+ monitor = {}
+
+ def utterance_callback(message):
+ monitor['utterances'] = message.get('utterances')
+
+ self.loop.once('recognizer_loop:utterance', utterance_callback)
+ self.consumer.read_audio()
+
+ utterances = monitor.get('utterances')
+ self.assertIsNotNone(utterances)
+ self.assertTrue(len(utterances) == 1)
+ self.assertEquals("stop", utterances[0])
+
+ def test_record(self):
+ self.queue.put(self.__create_sample_from_test_file('mycroft'))
+ self.consumer.read_audio()
+
+ self.queue.put(self.__create_sample_from_test_file('record'))
+ self.recognizer.set_transcriptions(["record"])
+ monitor = {}
+
+ def utterance_callback(message):
+ monitor['utterances'] = message.get('utterances')
+
+ self.loop.once('recognizer_loop:utterance', utterance_callback)
+ self.consumer.read_audio()
+
+ utterances = monitor.get('utterances')
+ self.assertIsNotNone(utterances)
+ self.assertTrue(len(utterances) == 1)
+ self.assertEquals("record", utterances[0])
diff --git a/test/client/data/record.wav b/test/client/data/record.wav
new file mode 100644
index 0000000000..5062ebc0d3
Binary files /dev/null and b/test/client/data/record.wav differ
diff --git a/test/client/data/stop.wav b/test/client/data/stop.wav
new file mode 100644
index 0000000000..dcf10e6639
Binary files /dev/null and b/test/client/data/stop.wav differ