Beispiel #1
0
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()
Beispiel #2
0
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()
Beispiel #4
0
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)
Beispiel #7
0
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"))
Beispiel #8
0
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}))
Beispiel #10
0
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()
Beispiel #11
0
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()