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"), ssl=str2bool(config.get("ssl"))) self.identity_manager = IdentityManager() self.identity = self.identity_manager.identity self.pairing_code = (pairing_code if pairing_code else generate_pairing_code()) def on_registration(self, message): # TODO: actually accept the configuration message and store it in # identity identity = self.identity_manager.get() register_payload = message.metadata if register_payload.get("device_id") == identity.device_id: identity.token = register_payload.get('token') 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", metadata={ "pairing_code": self.pairing_code, "device_id": self.identity.device_id }) self.ws_client.emit(msg) @staticmethod def print_error(message): print(repr(message)) def run(self): self.ws_client.on('registration', self.on_registration) self.ws_client.on('open', self.send_device_info) self.ws_client.on('error', self.print_error) self.ws_client.run_forever()
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"), ssl=str2bool(config.get("ssl"))) self.identity_manager = IdentityManager() self.identity = self.identity_manager.identity self.pairing_code = ( pairing_code if pairing_code else generate_pairing_code()) def on_registration(self, message): # TODO: actually accept the configuration message and store it in # identity identity = self.identity_manager.get() register_payload = message.metadata if register_payload.get("device_id") == identity.device_id: identity.token = register_payload.get('token') 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", metadata={ "pairing_code": self.pairing_code, "device_id": self.identity.device_id }) self.ws_client.emit(msg) @staticmethod def print_error(message): print(repr(message)) def run(self): self.ws_client.on('registration', self.on_registration) self.ws_client.on('open', self.send_device_info) self.ws_client.on('error', self.print_error) self.ws_client.run_forever()
class Enclosure(object): """ Serves as a communication interface between Arduino and Mycroft Core. ``Enclosure`` initializes and aggregates all enclosures implementation. E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` It also listens to the basis events in order to perform those core actions on the unit. E.g. Start and Stop talk animation """ _last_internet_notification = 0 def __init__(self): self.ws = WebsocketClient() ConfigurationManager.init(self.ws) self.config = ConfigurationManager.get().get("enclosure") self.__init_serial() self.reader = EnclosureReader(self.serial, self.ws) self.writer = EnclosureWriter(self.serial, self.ws) # Send a message to the Arduino across the serial line asking # for a reply with version info. self.writer.write("system.version") # When the Arduino responds, it will generate this message self.ws.on("enclosure.started", self.on_arduino_responded) self.arduino_responded = False # Start a 5 second timer. If the serial port hasn't received # any acknowledgement of the "system.version" within those # 5 seconds, assume there is nothing on the other end (e.g. # we aren't running a Mark 1 with an Arduino) Timer(5, self.check_for_response).start() # Notifications from mycroft-core self.ws.on("enclosure.notify.no_internet", self.on_no_internet) def on_arduino_responded(self, event=None): self.eyes = EnclosureEyes(self.ws, self.writer) self.mouth = EnclosureMouth(self.ws, self.writer) self.system = EnclosureArduino(self.ws, self.writer) self.weather = EnclosureWeather(self.ws, self.writer) self.__register_events() self.__reset() self.arduino_responded = True # 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.on_no_internet).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. # Handle the translation within that code. self.ws.emit(Message("speak", { 'utterance': "This device is not connected to the Internet. " "Either plug in a network cable or hold the button " "on top for two seconds, then select wifi from the " "menu"})) def __init_serial(self): try: self.port = self.config.get("port") self.rate = self.config.get("rate") self.timeout = self.config.get("timeout") self.serial = serial.serial_for_url( url=self.port, baudrate=self.rate, timeout=self.timeout) LOG.info("Connected to: %s rate: %s timeout: %s" % (self.port, self.rate, self.timeout)) except: LOG.error("Impossible to connect to serial port: " + self.port) raise def __register_events(self): self.ws.on('enclosure.mouth.events.activate', self.__register_mouth_events) self.ws.on('enclosure.mouth.events.deactivate', self.__remove_mouth_events) self.ws.on('enclosure.reset', self.__reset) self.__register_mouth_events() def __register_mouth_events(self, event=None): self.ws.on('recognizer_loop:record_begin', self.mouth.listen) self.ws.on('recognizer_loop:record_end', self.mouth.reset) self.ws.on('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.on('recognizer_loop:audio_output_end', self.mouth.reset) def __remove_mouth_events(self, event=None): self.ws.remove('recognizer_loop:record_begin', self.mouth.listen) self.ws.remove('recognizer_loop:record_end', self.mouth.reset) self.ws.remove('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.remove('recognizer_loop:audio_output_end', self.mouth.reset) def __reset(self, event=None): # Reset both the mouth and the eye elements to indicate the unit is # ready for input. self.writer.write("eyes.reset") self.writer.write("mouth.reset") def speak(self, text): self.ws.emit(Message("speak", {'utterance': text})) def run(self): try: self.ws.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop() def check_for_response(self): if not self.arduino_responded: # There is nothing on the other end of the serial port # close these serial-port readers and this process self.writer.stop() self.reader.stop() self.serial.close() self.ws.close()
class TestMessagebusMethods(unittest.TestCase): """This class is for testing the messsagebus. It currently only tests send and receive. The tests could include more. """ def setUp(self): """ This sets up for testing the message buss This requires starting the mycroft service and creating two WebsocketClient object to talk with eachother. Not this is threaded and will require cleanup """ # start the mycroft service. and get the pid of the script. self.pid = Popen(["python", "mycroft/messagebus/service/main.py"]).pid # delay to allow the service to start up. time.sleep(10) # Create the two web clients self.ws1 = WebsocketClient() self.ws2 = WebsocketClient() # init the flags for handler's self.handle1 = False self.handle2 = False # Start threads to handle websockets Thread(target=self.ws1.run_forever).start() Thread(target=self.ws2.run_forever).start() # Sleep to give the websockets to startup before adding handlers time.sleep(10) # Setup handlers for each of the messages. self.ws1.on('ws1.message', self.onHandle1) self.ws2.on('ws2.message', self.onHandle2) def onHandle1(self, event): """This is the handler for ws1.message This for now simply sets a flag to true when received. Args: event(Message): this is the message received """ self.handle1 = True def onHandle2(self, event): """This is the handler for ws2.message This for now simply sets a flag to true when received. Args: event(Message): this is the message received """ self.handle2 = True def tearDown(self): """This is the clean up for the tests This will close the websockets ending the threads then kill the mycroft service that was started in setUp. """ self.ws1.close() self.ws2.close() retcode = call(["kill", "-9", str(self.pid)]) def test_ClientServer(self): """This is the test to send a message from each of the websockets to the other. """ # Send the messages self.ws2.emit(Message('ws1.message')) self.ws1.emit(Message('ws2.message')) # allow time for messages to be processed time.sleep(10) # Check that both of the handlers were called. self.assertTrue(self.handle1) self.assertTrue(self.handle2)
class Enclosure(object): """ Serves as a communication interface between Arduino and Mycroft Core. ``Enclosure`` initializes and aggregates all enclosures implementation. E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` It also listens to the basis events in order to perform those core actions on the unit. E.g. Start and Stop talk animation """ def __init__(self): self.ws = WebsocketClient() ConfigurationManager.init(self.ws) self.config = ConfigurationManager.get().get("enclosure") self.__init_serial() self.reader = EnclosureReader(self.serial, self.ws) self.writer = EnclosureWriter(self.serial, self.ws) self.writer.write("system.version") self.ws.on("enclosure.start", self.start) self.started = False Timer(5, self.stop).start() # WHY? This at least # needs an explanation, this is non-obvious behavior def start(self, event=None): self.eyes = EnclosureEyes(self.ws, self.writer) self.mouth = EnclosureMouth(self.ws, self.writer) self.system = EnclosureArduino(self.ws, self.writer) self.weather = EnclosureWeather(self.ws, self.writer) self.__register_events() self.__reset() self.started = True def __init_serial(self): try: self.port = self.config.get("port") self.rate = self.config.get("rate") self.timeout = self.config.get("timeout") self.serial = serial.serial_for_url(url=self.port, baudrate=self.rate, timeout=self.timeout) LOG.info("Connected to: %s rate: %s timeout: %s" % (self.port, self.rate, self.timeout)) except: LOG.error("Impossible to connect to serial port: " + self.port) raise def __register_events(self): self.ws.on('enclosure.mouth.events.activate', self.__register_mouth_events) self.ws.on('enclosure.mouth.events.deactivate', self.__remove_mouth_events) self.ws.on('enclosure.reset', self.__reset) self.__register_mouth_events() def __register_mouth_events(self, event=None): self.ws.on('recognizer_loop:record_begin', self.mouth.listen) self.ws.on('recognizer_loop:record_end', self.mouth.reset) self.ws.on('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.on('recognizer_loop:audio_output_end', self.mouth.reset) def __remove_mouth_events(self, event=None): self.ws.remove('recognizer_loop:record_begin', self.mouth.listen) self.ws.remove('recognizer_loop:record_end', self.mouth.reset) self.ws.remove('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.remove('recognizer_loop:audio_output_end', self.mouth.reset) def __reset(self, event=None): # Reset both the mouth and the eye elements to indicate the unit is # ready for input. self.writer.write("eyes.reset") self.writer.write("mouth.reset") def speak(self, text): self.ws.emit(Message("speak", {'utterance': text})) def run(self): try: self.ws.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop() def stop(self): if not self.started: self.writer.stop() self.reader.stop() self.serial.close() self.ws.close()
class TestMessagebusMethods(unittest.TestCase): """This class is for testing the messsagebus. It currently only tests send and receive. The tests could include more. """ def setUp(self): """ This sets up for testing the message buss This requires starting the mycroft service and creating two WebsocketClient object to talk with eachother. Not this is threaded and will require cleanup """ # start the mycroft service. and get the pid of the script. self.pid = Popen(["python", "mycroft/messagebus/service/main.py"]).pid # Create the two web clients self.ws1 = WebsocketClient() self.ws2 = WebsocketClient() # init the flags for handler's self.handle1 = False self.handle2 = False # Start threads to handle websockets Thread(target=self.ws1.run_forever).start() Thread(target=self.ws2.run_forever).start() # Setup handlers for each of the messages. self.ws1.on('ws1.message', self.onHandle1) self.ws2.on('ws2.message', self.onHandle2) def onHandle1(self, event): """This is the handler for ws1.message This for now simply sets a flag to true when received. Args: event(Message): this is the message received """ self.handle1 = True def onHandle2(self, event): """This is the handler for ws2.message This for now simply sets a flag to true when received. Args: event(Message): this is the message received """ self.handle2 = True def tearDown(self): """This is the clean up for the tests This will close the websockets ending the threads then kill the mycroft service that was started in setUp. """ self.ws1.close() self.ws2.close() retcode = call(["kill", "-9", str(self.pid)]) def test_ClientServer(self): """This is the test to send a message from each of the websockets to the other. """ # Send the messages self.ws2.emit(Message('ws1.message')) self.ws1.emit(Message('ws2.message')) # allow time for messages to be processed time.sleep(0.2) # Check that both of the handlers were called. self.assertTrue(self.handle1) self.assertTrue(self.handle2)
class Enclosure(object): """ Serves as a communication interface between Arduino and Mycroft Core. ``Enclosure`` initializes and aggregates all enclosures implementation. E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` It also listens to the basis events in order to perform those core actions on the unit. E.g. Start and Stop talk animation """ _last_internet_notification = 0 def __init__(self): self.ws = WebsocketClient() self.ws.on("open", self.on_ws_open) Configuration.init(self.ws) global_config = Configuration.get() self.lang = global_config['lang'] self.config = global_config.get("enclosure") self.__init_serial() self.reader = EnclosureReader(self.serial, self.ws, self.lang) self.writer = EnclosureWriter(self.serial, self.ws) # initiates the web sockets on display manager # NOTE: this is a temporary place to initiate display manager sockets initiate_display_manager_ws() def on_ws_open(self, event=None): # Mark 1 auto-detection: # # Prepare to receive message when the Arduino responds to the # following "system.version" self.ws.on("enclosure.started", self.on_arduino_responded) self.arduino_responded = False # Send a message to the Arduino across the serial line asking # for a reply with version info. self.writer.write("system.version") # Start a 5 second timer. If the serial port hasn't received # any acknowledgement of the "system.version" within those # 5 seconds, assume there is nothing on the other end (e.g. # we aren't running a Mark 1 with an Arduino) Timer(5, self.check_for_response).start() # Notifications from mycroft-core self.ws.on("enclosure.notify.no_internet", self.on_no_internet) def on_arduino_responded(self, event=None): self.eyes = EnclosureEyes(self.ws, self.writer) self.mouth = EnclosureMouth(self.ws, self.writer) self.system = EnclosureArduino(self.ws, self.writer) self.weather = EnclosureWeather(self.ws, self.writer) self.__register_events() self.__reset() self.arduino_responded = True # 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() Timer(60, self._hack_check_for_duplicates).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.ws.emit( Message( "speak", { 'utterance': "This device is not connected to the Internet. " "Either plug in a network cable or hold the " "button on top for two seconds, then select " "wifi from the menu" })) else: # enter wifi-setup mode automatically self.ws.emit(Message('system.wifi.setup', {'lang': self.lang})) def __init_serial(self): try: self.port = self.config.get("port") self.rate = self.config.get("rate") self.timeout = self.config.get("timeout") self.serial = serial.serial_for_url(url=self.port, baudrate=self.rate, timeout=self.timeout) LOG.info("Connected to: %s rate: %s timeout: %s" % (self.port, self.rate, self.timeout)) except: LOG.error("Impossible to connect to serial port: " + str(self.port)) raise def __register_events(self): self.ws.on('enclosure.mouth.events.activate', self.__register_mouth_events) self.ws.on('enclosure.mouth.events.deactivate', self.__remove_mouth_events) self.ws.on('enclosure.reset', self.__reset) self.__register_mouth_events() def __register_mouth_events(self, event=None): self.ws.on('recognizer_loop:record_begin', self.mouth.listen) self.ws.on('recognizer_loop:record_end', self.mouth.reset) self.ws.on('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.on('recognizer_loop:audio_output_end', self.mouth.reset) def __remove_mouth_events(self, event=None): self.ws.remove('recognizer_loop:record_begin', self.mouth.listen) self.ws.remove('recognizer_loop:record_end', self.mouth.reset) self.ws.remove('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.remove('recognizer_loop:audio_output_end', self.mouth.reset) def __reset(self, event=None): # Reset both the mouth and the eye elements to indicate the unit is # ready for input. self.writer.write("eyes.reset") self.writer.write("mouth.reset") def speak(self, text): self.ws.emit(Message("speak", {'utterance': text})) def run(self): try: self.ws.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop() def check_for_response(self): if not self.arduino_responded: # There is nothing on the other end of the serial port # close these serial-port readers and this process self.writer.stop() self.reader.stop() self.serial.close() self.ws.close() def _handle_pairing_complete(self, Message): """ Handler for 'mycroft.paired', unmutes the mic after the pairing is complete. """ self.ws.emit(Message("mycroft.mic.unmute")) def _do_net_check(self): # TODO: This should live in the derived Enclosure, e.g. Enclosure_Mark1 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 hold the " "button on top for two seconds, then select " "wifi from the menu") else: # Begin the unit startup process, this is the first time it # is being run with factory defaults. # TODO: This logic should be in Enclosure_Mark1 # TODO: Enclosure/localization # Don't listen to mic during this out-of-box experience self.ws.emit(Message("mycroft.mic.mute")) # Setup handler to unmute mic at the end of on boarding # i.e. after pairing is complete self.ws.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.ws.emit(Message('system.wifi.setup', data)) def _hack_check_for_duplicates(self): # TEMPORARY HACK: Look for multiple instance of the # mycroft-speech-client and/or mycroft-skills services, which could # happen when upgrading a shipping Mark 1 from release 0.8.17 or # before. When found, force the unit to reboot. import psutil LOG.info("Hack to check for duplicate service instances") count_instances = 0 needs_reboot = False for process in psutil.process_iter(): if process.cmdline() == [ 'python2.7', '/usr/local/bin/mycroft-speech-client' ]: count_instances += 1 if (count_instances > 1): LOG.info("Duplicate mycroft-speech-client found") needs_reboot = True count_instances = 0 for process in psutil.process_iter(): if process.cmdline() == [ 'python2.7', '/usr/local/bin/mycroft-skills' ]: count_instances += 1 if (count_instances > 1): LOG.info("Duplicate mycroft-skills found") needs_reboot = True if needs_reboot: LOG.info("Hack reboot...") self.reader.process("unit.reboot") self.ws.emit(Message("enclosure.eyes.spin")) self.ws.emit(Message("enclosure.mouth.reset"))
class Enclosure(object): """ Serves as a communication interface between Arduino and Mycroft Core. ``Enclosure`` initializes and aggregates all enclosures implementation. E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` It also listens to the basis events in order to perform those core actions on the unit. E.g. Start and Stop talk animation """ _last_internet_notification = 0 def __init__(self): self.ws = WebsocketClient() self.ws.on("open", self.on_ws_open) Configuration.init(self.ws) global_config = Configuration.get() self.lang = global_config['lang'] self.config = global_config.get("enclosure") self.__init_serial() self.reader = EnclosureReader(self.serial, self.ws, self.lang) self.writer = EnclosureWriter(self.serial, self.ws) # initiates the web sockets on display manager # NOTE: this is a temporary place to initiate display manager sockets initiate_display_manager_ws() def on_ws_open(self, event=None): # Mark 1 auto-detection: # # Prepare to receive message when the Arduino responds to the # following "system.version" self.ws.on("enclosure.started", self.on_arduino_responded) self.arduino_responded = False # Send a message to the Arduino across the serial line asking # for a reply with version info. self.writer.write("system.version") # Start a 5 second timer. If the serial port hasn't received # any acknowledgement of the "system.version" within those # 5 seconds, assume there is nothing on the other end (e.g. # we aren't running a Mark 1 with an Arduino) Timer(5, self.check_for_response).start() # Notifications from mycroft-core self.ws.on("enclosure.notify.no_internet", self.on_no_internet) def on_arduino_responded(self, event=None): self.eyes = EnclosureEyes(self.ws, self.writer) self.mouth = EnclosureMouth(self.ws, self.writer) self.system = EnclosureArduino(self.ws, self.writer) self.weather = EnclosureWeather(self.ws, self.writer) self.__register_events() self.__reset() self.arduino_responded = True # 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() Timer(60, self._hack_check_for_duplicates).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.ws.emit(Message("speak", { 'utterance': "This device is not connected to the Internet. " "Either plug in a network cable or hold the " "button on top for two seconds, then select " "wifi from the menu"})) else: # enter wifi-setup mode automatically self.ws.emit(Message('system.wifi.setup', {'lang': self.lang})) def __init_serial(self): try: self.port = self.config.get("port") self.rate = self.config.get("rate") self.timeout = self.config.get("timeout") self.serial = serial.serial_for_url( url=self.port, baudrate=self.rate, timeout=self.timeout) LOG.info("Connected to: %s rate: %s timeout: %s" % (self.port, self.rate, self.timeout)) except: LOG.error("Impossible to connect to serial port: "+str(self.port)) raise def __register_events(self): self.ws.on('enclosure.mouth.events.activate', self.__register_mouth_events) self.ws.on('enclosure.mouth.events.deactivate', self.__remove_mouth_events) self.ws.on('enclosure.reset', self.__reset) self.__register_mouth_events() def __register_mouth_events(self, event=None): self.ws.on('recognizer_loop:record_begin', self.mouth.listen) self.ws.on('recognizer_loop:record_end', self.mouth.reset) self.ws.on('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.on('recognizer_loop:audio_output_end', self.mouth.reset) def __remove_mouth_events(self, event=None): self.ws.remove('recognizer_loop:record_begin', self.mouth.listen) self.ws.remove('recognizer_loop:record_end', self.mouth.reset) self.ws.remove('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.remove('recognizer_loop:audio_output_end', self.mouth.reset) def __reset(self, event=None): # Reset both the mouth and the eye elements to indicate the unit is # ready for input. self.writer.write("eyes.reset") self.writer.write("mouth.reset") def speak(self, text): self.ws.emit(Message("speak", {'utterance': text})) def run(self): try: self.ws.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop() def check_for_response(self): if not self.arduino_responded: # There is nothing on the other end of the serial port # close these serial-port readers and this process self.writer.stop() self.reader.stop() self.serial.close() self.ws.close() def _handle_pairing_complete(self, Message): """ Handler for 'mycroft.paired', unmutes the mic after the pairing is complete. """ self.ws.emit(Message("mycroft.mic.unmute")) def _do_net_check(self): # TODO: This should live in the derived Enclosure, e.g. Enclosure_Mark1 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 hold the " "button on top for two seconds, then select " "wifi from the menu") else: # Begin the unit startup process, this is the first time it # is being run with factory defaults. # TODO: This logic should be in Enclosure_Mark1 # TODO: Enclosure/localization # Don't listen to mic during this out-of-box experience self.ws.emit(Message("mycroft.mic.mute")) # Setup handler to unmute mic at the end of on boarding # i.e. after pairing is complete self.ws.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.ws.emit(Message('system.wifi.setup', data)) def _hack_check_for_duplicates(self): # TEMPORARY HACK: Look for multiple instance of the # mycroft-speech-client and/or mycroft-skills services, which could # happen when upgrading a shipping Mark 1 from release 0.8.17 or # before. When found, force the unit to reboot. import psutil LOG.info("Hack to check for duplicate service instances") count_instances = 0 needs_reboot = False for process in psutil.process_iter(): if process.cmdline() == ['python2.7', '/usr/local/bin/mycroft-speech-client']: count_instances += 1 if (count_instances > 1): LOG.info("Duplicate mycroft-speech-client found") needs_reboot = True count_instances = 0 for process in psutil.process_iter(): if process.cmdline() == ['python2.7', '/usr/local/bin/mycroft-skills']: count_instances += 1 if (count_instances > 1): LOG.info("Duplicate mycroft-skills found") needs_reboot = True if needs_reboot: LOG.info("Hack reboot...") self.reader.process("unit.reboot") self.ws.emit(Message("enclosure.eyes.spin")) self.ws.emit(Message("enclosure.mouth.reset"))
class Enclosure(object): """ Serves as a communication interface between Arduino and Mycroft Core. ``Enclosure`` initializes and aggregates all enclosures implementation. E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` It also listens to the basis events in order to perform those core actions on the unit. E.g. Start and Stop talk animation """ _last_internet_notification = 0 def __init__(self): self.ws = WebsocketClient() ConfigurationManager.init(self.ws) self.config = ConfigurationManager.instance().get("enclosure") self.__init_serial() self.reader = EnclosureReader(self.serial, self.ws) self.writer = EnclosureWriter(self.serial, self.ws) # Send a message to the Arduino across the serial line asking # for a reply with version info. self.writer.write("system.version") # When the Arduino responds, it will generate this message self.ws.on("enclosure.started", self.on_arduino_responded) self.arduino_responded = False # Start a 5 second timer. If the serial port hasn't received # any acknowledgement of the "system.version" within those # 5 seconds, assume there is nothing on the other end (e.g. # we aren't running a Mark 1 with an Arduino) Timer(5, self.check_for_response).start() # Notifications from mycroft-core self.ws.on("enclosure.notify.no_internet", self.on_no_internet) def on_arduino_responded(self, event=None): self.eyes = EnclosureEyes(self.ws, self.writer) self.mouth = EnclosureMouth(self.ws, self.writer) self.system = EnclosureArduino(self.ws, self.writer) self.weather = EnclosureWeather(self.ws, self.writer) self.__register_events() self.__reset() self.arduino_responded = True # 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.ws.emit(Message("speak", { 'utterance': "This device is not connected to the Internet. " "Either plug in a network cable or hold the " "button on top for two seconds, then select " "wifi from the menu"})) else: # enter wifi-setup mode automatically self.ws.emit(Message("mycroft.wifi.start")) def __init_serial(self): try: self.port = self.config.get("port") self.rate = self.config.get("rate") self.timeout = self.config.get("timeout") self.serial = serial.serial_for_url( url=self.port, baudrate=self.rate, timeout=self.timeout) LOG.info("Connected to: %s rate: %s timeout: %s" % (self.port, self.rate, self.timeout)) except: LOG.error("Impossible to connect to serial port: " + self.port) raise def __register_events(self): self.ws.on('enclosure.mouth.events.activate', self.__register_mouth_events) self.ws.on('enclosure.mouth.events.deactivate', self.__remove_mouth_events) self.ws.on('enclosure.reset', self.__reset) self.__register_mouth_events() def __register_mouth_events(self, event=None): self.ws.on('recognizer_loop:record_begin', self.mouth.listen) self.ws.on('recognizer_loop:record_end', self.mouth.reset) self.ws.on('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.on('recognizer_loop:audio_output_end', self.mouth.reset) def __remove_mouth_events(self, event=None): self.ws.remove('recognizer_loop:record_begin', self.mouth.listen) self.ws.remove('recognizer_loop:record_end', self.mouth.reset) self.ws.remove('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.remove('recognizer_loop:audio_output_end', self.mouth.reset) def __reset(self, event=None): # Reset both the mouth and the eye elements to indicate the unit is # ready for input. self.writer.write("eyes.reset") self.writer.write("mouth.reset") def speak(self, text): self.ws.emit(Message("speak", {'utterance': text})) def run(self): try: self.ws.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop() def check_for_response(self): if not self.arduino_responded: # There is nothing on the other end of the serial port # close these serial-port readers and this process self.writer.stop() self.reader.stop() self.serial.close() self.ws.close() def _do_net_check(self): # TODO: This should live in the derived Enclosure, e.g. Enclosure_Mark1 LOG.info("Checking internet connection") if not connected(): # and self.conn_monitor is None: if has_been_paired(): # TODO: Enclosure/localization self.ws.emit(Message("speak", { 'utterance': "This unit is not connected to the Internet." " Either plug in a network cable or hold the " "button on top for two seconds, then select " "wifi from the menu" })) else: # Begin the unit startup process, this is the first time it # is being run with factory defaults. # TODO: This logic should be in Enclosure_Mark1 # TODO: Enclosure/localization # Don't listen to mic during this out-of-box experience self.ws.emit(Message("mycroft.mic.mute", None)) # Kick off wifi-setup automatically self.ws.emit(Message("mycroft.wifi.start", {'msg': "Hello I am Mycroft, your new " "assistant. To assist you I need to be " "connected to the internet. You can " "either plug me in with a network cable," " or use wifi. To setup wifi ", 'allow_timeout': False}))
class Enclosure(object): """ Serves as a communication interface between Arduino and Mycroft Core. ``Enclosure`` initializes and aggregates all enclosures implementation. E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` It also listens to the basis events in order to perform those core actions on the unit. E.g. Start and Stop talk animation """ def __init__(self): self.ws = WebsocketClient() ConfigurationManager.init(self.ws) self.config = ConfigurationManager.get().get("enclosure") self.__init_serial() self.reader = EnclosureReader(self.serial, self.ws) self.writer = EnclosureWriter(self.serial, self.ws) self.writer.write("system.version") self.ws.on("enclosure.start", self.start) self.started = False Timer(5, self.stop).start() def start(self, event=None): self.eyes = EnclosureEyes(self.ws, self.writer) self.mouth = EnclosureMouth(self.ws, self.writer) self.system = EnclosureArduino(self.ws, self.writer) self.weather = EnclosureWeather(self.ws, self.writer) self.__register_events() #self.update() self.test() self.started = True def update(self): if self.config.get("update"): try: self.speak("Upgrading enclosure version") subprocess.check_call("/opt/enclosure/upload.sh") self.speak("Enclosure update completed") ConfigurationManager.save({"enclosure": {"update": False}}) except: self.speak("I cannot upgrade right now, I'll try later") def test(self): if self.config.get("test"): self.speak("Beginning hardware test") self.writer.write("test.begin") ConfigurationManager.save({"enclosure": {"test": False}}) def __init_serial(self): try: self.port = self.config.get("port") self.rate = self.config.get("rate") self.timeout = self.config.get("timeout") self.serial = serial.serial_for_url(url=self.port, baudrate=self.rate, timeout=self.timeout) LOG.info("Connected to: %s rate: %s timeout: %s" % (self.port, self.rate, self.timeout)) except: LOG.error("Impossible to connect to serial port: " + self.port) raise def __register_events(self): self.ws.on('enclosure.mouth.events.activate', self.__register_mouth_events) self.ws.on('enclosure.mouth.events.deactivate', self.__remove_mouth_events) self.__register_mouth_events() def __register_mouth_events(self, event=None): self.ws.on('recognizer_loop:record_begin', self.mouth.listen) self.ws.on('recognizer_loop:record_end', self.mouth.reset) self.ws.on('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.on('recognizer_loop:audio_output_end', self.mouth.reset) def __remove_mouth_events(self, event=None): self.ws.remove('recognizer_loop:record_begin', self.mouth.listen) self.ws.remove('recognizer_loop:record_end', self.mouth.reset) self.ws.remove('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.remove('recognizer_loop:audio_output_end', self.mouth.reset) def speak(self, text): self.ws.emit(Message("speak", {'utterance': text})) def run(self): try: self.ws.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop() def stop(self): if not self.started: self.writer.stop() self.reader.stop() self.serial.close() self.ws.close()
class Enclosure(object): """ Serves as a communication interface between Arduino and Mycroft Core. ``Enclosure`` initializes and aggregates all enclosures implementation. E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` It also listens to the basis events in order to perform those core actions on the unit. E.g. Start and Stop talk animation """ def __init__(self): self.ws = WebsocketClient() ConfigurationManager.init(self.ws) self.config = ConfigurationManager.get().get("enclosure") self.__init_serial() self.reader = EnclosureReader(self.serial, self.ws) self.writer = EnclosureWriter(self.serial, self.ws) self.writer.write("system.version") self.ws.on("enclosure.start", self.start) self.started = False Timer(5, self.stop).start() # WHY? This at least needs an explaination, this is non-obvious behavior def start(self, event=None): self.eyes = EnclosureEyes(self.ws, self.writer) self.mouth = EnclosureMouth(self.ws, self.writer) self.system = EnclosureArduino(self.ws, self.writer) self.weather = EnclosureWeather(self.ws, self.writer) self.__register_events() self.__reset() self.started = True def __init_serial(self): try: self.port = self.config.get("port") self.rate = self.config.get("rate") self.timeout = self.config.get("timeout") self.serial = serial.serial_for_url( url=self.port, baudrate=self.rate, timeout=self.timeout) LOG.info("Connected to: %s rate: %s timeout: %s" % (self.port, self.rate, self.timeout)) except: LOG.error("Impossible to connect to serial port: " + self.port) raise def __register_events(self): self.ws.on('enclosure.mouth.events.activate', self.__register_mouth_events) self.ws.on('enclosure.mouth.events.deactivate', self.__remove_mouth_events) self.ws.on('enclosure.reset', self.__reset) self.__register_mouth_events() def __register_mouth_events(self, event=None): self.ws.on('recognizer_loop:record_begin', self.mouth.listen) self.ws.on('recognizer_loop:record_end', self.mouth.reset) self.ws.on('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.on('recognizer_loop:audio_output_end', self.mouth.reset) def __remove_mouth_events(self, event=None): self.ws.remove('recognizer_loop:record_begin', self.mouth.listen) self.ws.remove('recognizer_loop:record_end', self.mouth.reset) self.ws.remove('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.remove('recognizer_loop:audio_output_end', self.mouth.reset) def __reset(self, event=None): # Reset both the mouth and the eye elements to indicate the unit is # ready for input. self.writer.write("eyes.reset") self.writer.write("mouth.reset") def speak(self, text): self.ws.emit(Message("speak", {'utterance': text})) def run(self): try: self.ws.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop() def stop(self): if not self.started: self.writer.stop() self.reader.stop() self.serial.close() self.ws.close()