예제 #1
0
def init_display_manager_bus_connection():
    """ Connects the display manager to the messagebus """
    LOG.info("Connecting display manager to messagebus")

    # Should remove needs to be an object so it can be referenced in functions
    # [https://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference]
    display_manager = DisplayManager()
    should_remove = [True]

    def check_flag(flag):
        if flag[0] is True:
            display_manager.remove_active()

    def set_delay(event=None):
        should_remove[0] = True
        Timer(2, check_flag, [should_remove]).start()

    def set_remove_flag(event=None):
        should_remove[0] = False

    def connect():
        bus.run_forever()

    def remove_wake_word():
        data = _read_data()
        if "active_skill" in data and data["active_skill"] == "wakeword":
            display_manager.remove_active()

    def set_wakeword_skill(event=None):
        display_manager.set_active("wakeword")
        Timer(10, remove_wake_word).start()

    bus = MessageBusClient()
    bus.on('recognizer_loop:audio_output_end', set_delay)
    bus.on('recognizer_loop:audio_output_start', set_remove_flag)
    bus.on('recognizer_loop:record_begin', set_wakeword_skill)

    event_thread = Thread(target=connect)
    event_thread.setDaemon(True)
    event_thread.start()
예제 #2
0
#import modules
import time
import serial  #import serial to talk to the arduino
from mycroft_bus_client import MessageBusClient, Message  #import messageBus to talk to Mycroft

#set up the serial port
ser = serial.Serial('/dev/ttyACM0', baudrate=9600, timeout=1)

print('Setting up client to connect to a local mycroft instance')
client = MessageBusClient()


def print_utterance(message):
    victor_response = (format(message.data.get('utterance'))
                       )  #get the latest output from Mycroft
    print(victor_response)  #print the output to the terminal
    ser.write(victor_response.encode())  #send the output over serial


print('Registering handler for speak message...')
client.on('speak', print_utterance)

client.run_forever()
예제 #3
0
class Enclosure:
    def __init__(self):
        # Load full config
        config = get_mycroft_compatible_config()
        self.lang = config['lang']
        self.config = config.get("enclosure")
        LOG.info(config)
        config["gui_websocket"] = config.get("gui_websocket", {
            "host": "0.0.0.0",
            "base_port": 18181,
            "route": "/gui",
            "ssl": False
        })
        config['gui_websocket']["base_port"] = config["gui_websocket"].get(
            "base_port", config["gui_websocket"].get("port", 18181))

        self.global_config = config

        # Create Message Bus Client
        self.bus = MessageBusClient()

        self.gui = create_gui_service(self, config['gui_websocket'])
        # This datastore holds the data associated with the GUI provider. Data
        # is stored in Namespaces, so you can have:
        # self.datastore["namespace"]["name"] = value
        # Typically the namespace is a meaningless identifier, but there is a
        # special "SYSTEM" namespace.
        self.datastore = {}

        # self.loaded is a list, each element consists of a namespace named
        # tuple.
        # The namespace namedtuple has the properties "name" and "pages"
        # The name contains the namespace name as a string and pages is a
        # mutable list of loaded pages.
        #
        # [Namespace name, [List of loaded qml pages]]
        # [
        # ["SKILL_NAME", ["page1.qml, "page2.qml", ... , "pageN.qml"]
        # [...]
        # ]
        self.loaded = []  # list of lists in order.
        self.explicit_move = True  # Set to true to send reorder commands

        # Listen for new GUI clients to announce themselves on the main bus
        self.active_namespaces = []
        self.bus.on("mycroft.gui.connected", self.on_gui_client_connected)
        self.register_gui_handlers()

        # First send any data:
        self.bus.on("gui.value.set", self.on_gui_set_value)
        self.bus.on("gui.page.show", self.on_gui_show_page)
        self.bus.on("gui.page.delete", self.on_gui_delete_page)
        self.bus.on("gui.clear.namespace", self.on_gui_delete_namespace)
        self.bus.on("gui.event.send", self.on_gui_send_event)
        self.bus.on("gui.status.request", self.handle_gui_status_request)

    def run(self):
        """Start the Enclosure after it has been constructed."""
        # Allow exceptions to be raised to the Enclosure Service
        # if they may cause the Service to fail.
        self.bus.run_in_thread()

    def stop(self):
        """Perform any enclosure shutdown processes."""
        pass

    ######################################################################
    # GUI client API
    @property
    def gui_connected(self):
        """Returns True if at least 1 gui is connected, else False"""
        return len(GUIWebsocketHandler.clients) > 0

    def handle_gui_status_request(self, message):
        """Reply to gui status request, allows querying if a gui is
        connected using the message bus"""
        self.bus.emit(
            message.reply("gui.status.request.response",
                          {"connected": self.gui_connected}))

    def send(self, msg_dict):
        """ Send to all registered GUIs. """
        for connection in GUIWebsocketHandler.clients:
            try:
                connection.send(msg_dict)
            except Exception as e:
                LOG.exception(repr(e))

    def on_gui_send_event(self, message):
        """ Send an event to the GUIs. """
        try:
            data = {
                'type': 'mycroft.events.triggered',
                'namespace': message.data.get('__from'),
                'event_name': message.data.get('event_name'),
                'params': message.data.get('params')
            }
            self.send(data)
        except Exception as e:
            LOG.error('Could not send event ({})'.format(repr(e)))

    def on_gui_set_value(self, message):
        data = message.data
        namespace = data.get("__from", "")

        # Pass these values on to the GUI renderers
        for key in data:
            if key not in RESERVED_KEYS:
                try:
                    self.set(namespace, key, data[key])
                except Exception as e:
                    LOG.exception(repr(e))

    def set(self, namespace, name, value):
        """ Perform the send of the values to the connected GUIs. """
        if namespace not in self.datastore:
            self.datastore[namespace] = {}
        if self.datastore[namespace].get(name) != value:
            self.datastore[namespace][name] = value

            # If the namespace is loaded send data to GUI
            if namespace in [l.name for l in self.loaded]:
                msg = {
                    "type": "mycroft.session.set",
                    "namespace": namespace,
                    "data": {
                        name: value
                    }
                }
                self.send(msg)

    def on_gui_delete_page(self, message):
        """ Bus handler for removing pages. """
        page, namespace, _ = _get_page_data(message)
        try:
            with namespace_lock:
                self.remove_pages(namespace, page)
        except Exception as e:
            LOG.exception(repr(e))

    def on_gui_delete_namespace(self, message):
        """ Bus handler for removing namespace. """
        try:
            namespace = message.data['__from']
            with namespace_lock:
                self.remove_namespace(namespace)
        except Exception as e:
            LOG.exception(repr(e))

    def on_gui_show_page(self, message):
        try:
            page, namespace, index = _get_page_data(message)
            # Pass the request to the GUI(s) to pull up a page template
            with namespace_lock:
                self.show(namespace, page, index)
        except Exception as e:
            LOG.exception(repr(e))

    def __find_namespace(self, namespace):
        for i, skill in enumerate(self.loaded):
            if skill[0] == namespace:
                return i
        return None

    def __insert_pages(self, namespace, pages):
        """ Insert pages into the namespace

        Args:
            namespace (str): Namespace to add to
            pages (list):    Pages (str) to insert
        """
        LOG.debug("Inserting new pages")
        if not isinstance(pages, list):
            raise ValueError('Argument must be list of pages')

        self.send({
            "type": "mycroft.gui.list.insert",
            "namespace": namespace,
            "position": len(self.loaded[0].pages),
            "data": [{
                "url": p
            } for p in pages]
        })
        # Insert the pages into local reprensentation as well.
        updated = Namespace(self.loaded[0].name, self.loaded[0].pages + pages)
        self.loaded[0] = updated

    def __remove_page(self, namespace, pos):
        """ Delete page.

        Args:
            namespace (str): Namespace to remove from
            pos (int):      Page position to remove
        """
        LOG.debug("Deleting {} from {}".format(pos, namespace))
        self.send({
            "type": "mycroft.gui.list.remove",
            "namespace": namespace,
            "position": pos,
            "items_number": 1
        })
        # Remove the page from the local reprensentation as well.
        self.loaded[0].pages.pop(pos)
        # Add a check to return any display to idle from position 0
        if (pos == 0 and len(self.loaded[0].pages) == 0):
            self.bus.emit(Message("mycroft.device.show.idle"))

    def __insert_new_namespace(self, namespace, pages):
        """ Insert new namespace and pages.

        This first sends a message adding a new namespace at the
        highest priority (position 0 in the namespace stack)

        Args:
            namespace (str):  The skill namespace to create
            pages (str):      Pages to insert (name matches QML)
        """
        LOG.debug("Inserting new namespace")
        self.send({
            "type": "mycroft.session.list.insert",
            "namespace": "mycroft.system.active_skills",
            "position": 0,
            "data": [{
                "skill_id": namespace
            }]
        })

        # Load any already stored Data
        data = self.datastore.get(namespace, {})
        for key in data:
            msg = {
                "type": "mycroft.session.set",
                "namespace": namespace,
                "data": {
                    key: data[key]
                }
            }
            self.send(msg)

        LOG.debug("Inserting new page")
        self.send({
            "type": "mycroft.gui.list.insert",
            "namespace": namespace,
            "position": 0,
            "data": [{
                "url": p
            } for p in pages]
        })
        # Make sure the local copy is updated
        self.loaded.insert(0, Namespace(namespace, pages))

    def __move_namespace(self, from_pos, to_pos):
        """ Move an existing namespace to a new position in the stack.

        Args:
            from_pos (int): Position in the stack to move from
            to_pos (int): Position to move to
        """
        LOG.debug("Activating existing namespace")
        # Seems like the namespace is moved to the top automatically when
        # a page change is done. Deactivating this for now.
        if self.explicit_move:
            LOG.debug("move {} to {}".format(from_pos, to_pos))
            self.send({
                "type": "mycroft.session.list.move",
                "namespace": "mycroft.system.active_skills",
                "from": from_pos,
                "to": to_pos,
                "items_number": 1
            })
        # Move the local representation of the skill from current
        # position to position 0.
        self.loaded.insert(to_pos, self.loaded.pop(from_pos))

    def __switch_page(self, namespace, pages):
        """ Switch page to an already loaded page.

        Args:
            pages (list): pages (str) to switch to
            namespace (str):  skill namespace
        """
        try:
            num = self.loaded[0].pages.index(pages[0])
        except Exception as e:
            LOG.exception(repr(e))
            num = 0

        LOG.debug('Switching to already loaded page at '
                  'index {} in namespace {}'.format(num, namespace))
        self.send({
            "type": "mycroft.events.triggered",
            "namespace": namespace,
            "event_name": "page_gained_focus",
            "data": {
                "number": num
            }
        })

    def show(self, namespace, page, index):
        """ Show a page and load it as needed.

        Args:
            page (str or list): page(s) to show
            namespace (str):  skill namespace
            index (int): ??? TODO: Unused in code ???

        TODO: - Update sync to match.
              - Separate into multiple functions/methods
        """

        LOG.debug("GUIConnection activating: " + namespace)
        pages = page if isinstance(page, list) else [page]

        # find namespace among loaded namespaces
        try:
            index = self.__find_namespace(namespace)
            if index is None:
                # This namespace doesn't exist, insert them first so they're
                # shown.
                self.__insert_new_namespace(namespace, pages)
                return
            else:  # Namespace exists
                if index > 0:
                    # Namespace is inactive, activate it by moving it to
                    # position 0
                    self.__move_namespace(index, 0)

                # Find if any new pages needs to be inserted
                new_pages = [p for p in pages if p not in self.loaded[0].pages]
                if new_pages:
                    self.__insert_pages(namespace, new_pages)
                else:
                    # No new pages, just switch
                    self.__switch_page(namespace, pages)
        except Exception as e:
            LOG.exception(repr(e))

    def remove_namespace(self, namespace):
        """ Remove namespace.

        Args:
            namespace (str): namespace to remove
        """
        index = self.__find_namespace(namespace)
        if index is None:
            return
        else:
            LOG.debug("Removing namespace {} at {}".format(namespace, index))
            self.send({
                "type": "mycroft.session.list.remove",
                "namespace": "mycroft.system.active_skills",
                "position": index,
                "items_number": 1
            })
            # Remove namespace from loaded namespaces
            self.loaded.pop(index)

    def remove_pages(self, namespace, pages):
        """ Remove the listed pages from the provided namespace.

        Args:
            namespace (str):    The namespace to modify
            pages (list):       List of page names (str) to delete
        """
        try:
            index = self.__find_namespace(namespace)
            if index is None:
                return
            else:
                # Remove any pages that doesn't exist in the namespace
                pages = [p for p in pages if p in self.loaded[index].pages]
                # Make sure to remove pages from the back
                indexes = [self.loaded[index].pages.index(p) for p in pages]
                indexes = sorted(indexes)
                indexes.reverse()
                for page_index in indexes:
                    self.__remove_page(namespace, page_index)
        except Exception as e:
            LOG.exception(repr(e))

    ######################################################################
    # GUI client socket
    #
    # The basic mechanism is:
    # 1) GUI client announces itself on the main messagebus
    # 2) Mycroft prepares a port for a socket connection to this GUI
    # 3) The port is announced over the messagebus
    # 4) The GUI connects on the socket
    # 5) Connection persists for graphical interaction indefinitely
    #
    # If the connection is lost, it must be renegotiated and restarted.
    def on_gui_client_connected(self, message):
        # GUI has announced presence
        LOG.info('GUI HAS ANNOUNCED!')
        port = self.global_config["gui_websocket"]["base_port"]
        LOG.debug("on_gui_client_connected")
        gui_id = message.data.get("gui_id")

        LOG.debug("Heard announcement from gui_id: {}".format(gui_id))

        # Announce connection, the GUI should connect on it soon
        self.bus.emit(
            Message("mycroft.gui.port", {
                "port": port,
                "gui_id": gui_id
            }))

    def register_gui_handlers(self):
        # TODO: Register handlers for standard (Mark 1) events
        # self.bus.on('enclosure.eyes.on', self.on)
        # self.bus.on('enclosure.eyes.off', self.off)
        # self.bus.on('enclosure.eyes.blink', self.blink)
        # self.bus.on('enclosure.eyes.narrow', self.narrow)
        # self.bus.on('enclosure.eyes.look', self.look)
        # self.bus.on('enclosure.eyes.color', self.color)
        # self.bus.on('enclosure.eyes.level', self.brightness)
        # self.bus.on('enclosure.eyes.volume', self.volume)
        # self.bus.on('enclosure.eyes.spin', self.spin)
        # self.bus.on('enclosure.eyes.timedspin', self.timed_spin)
        # self.bus.on('enclosure.eyes.reset', self.reset)
        # self.bus.on('enclosure.eyes.setpixel', self.set_pixel)
        # self.bus.on('enclosure.eyes.fill', self.fill)

        # self.bus.on('enclosure.mouth.reset', self.reset)
        # self.bus.on('enclosure.mouth.talk', self.talk)
        # self.bus.on('enclosure.mouth.think', self.think)
        # self.bus.on('enclosure.mouth.listen', self.listen)
        # self.bus.on('enclosure.mouth.smile', self.smile)
        # self.bus.on('enclosure.mouth.viseme', self.viseme)
        # self.bus.on('enclosure.mouth.text', self.text)
        # self.bus.on('enclosure.mouth.display', self.display)
        # self.bus.on('enclosure.mouth.display_image', self.display_image)
        # self.bus.on('enclosure.weather.display', self.display_weather)

        # self.bus.on('recognizer_loop:record_begin', self.mouth.listen)
        # self.bus.on('recognizer_loop:record_end', self.mouth.reset)
        # self.bus.on('recognizer_loop:audio_output_start', self.mouth.talk)
        # self.bus.on('recognizer_loop:audio_output_end', self.mouth.reset)
        pass
예제 #4
0
    print(message.data)

light.pulse(0.5, 40, None, 1)
light.pulse(0.7, 10, 40)

cbUnhook = pins.callback(4, pigpio.RISING_EDGE, unhook)
cbStop = pins.callback(24, pigpio.RISING_EDGE, stop)
cbReset = pins.callback(26, pigpio.RISING_EDGE, reset)

crank.create_callback('play', [-10, 10], play)
crank.create_callback('pause', [10, -10], pause)
crank.create_callback('next_track', [-5, 25], next_track)
crank.create_callback('previous_track', [5, -25], previous_track)
crank.create_callback('changeVolume', [0], change_volume)

client.on('recognizer_loop:wakeword', wakeword)
client.on('recognizer_loop:record_begin', begin_listening)
client.on('recognizer_loop:record_end', end_listening)
client.on('skill.mycrofttimer.expired', timer_done)
client.on('skill.mycrofttimer.cancelled', timer_stopped)
client.on('mycroft.ready', startup_alert)
client.on('recognizer_loop:utterance', uttered)
try:
    client.run_forever()
except KeyboardInterrupt:
    #Cleaning up
    cbUnhook.cancel()
    cbStop.cancel()
    cbReset.cancel()
    pins.stop()
예제 #5
0
# Processes original human message and gives extra info within specific context
# =============================================================================


def nobody_asked_this(message):
    # step 1: extract what human asked for out of a JSON object
    human_said = message.data.get('utterances')[0]
    print(f'Human said {human_said}')

    # step 2: extract keywords from the phrase and add search_context
    search_keys = extract_keywords(human_said) + " " + search_context
    print("Search query: " + search_keys)

    # step 3: retrieve page url from Google
    urls = retrieve_google_url(search_keys)

    # step 4: parse contents of the page
    supplementary_info = parse_article(urls)
    print(supplementary_info + '\n')

    # step 5: speak out supplementary info via Mycroft
    sleep(3)  # this is optional just to give time for mycroft to retrieve original results before supplementary ones
    client.emit(Message('speak', data={'utterance': supplementary_info}))


# waits for messages from Mycroft via websocket
client.on('recognizer_loop:utterance', nobody_asked_this)

# basically runs this script in a loop
client.run_forever()
    pixels.wakeup()
    pixels.listen()


def m_ready(message):
    pixels.off()


def record_end(message):
    pixels.think()
    global timer
    timer = threading.Timer(10.0, pixels.off)
    timer.start()


def audio_output_start(message):
    global timer
    timer.cancel()
    pixels.speak()


if __name__ == '__main__':
    pixels.wakeup()
    pixels.think()
    client.on('mycroft.ready', m_ready)
    client.on('recognizer_loop:wakeword', wakeword_dtected)
    client.on('recognizer_loop:record_end', record_end)
    client.on('recognizer_loop:audio_output_start', audio_output_start)
    client.on('recognizer_loop:audio_output_end', m_ready)
    client.on('recognizer_loop:sleep', m_ready)
    client.run_forever()
예제 #7
0
u.start()

# wait for Mycroft's tts engine to initialize properly
sleep(5)

# Check for new packets
observer = Observer()

# Directory monitoring
for folder_path in folder_paths:
    event_handler = SniffHandler()
    observer.schedule(event_handler, path=folder_path, recursive=False)
observer.start()

# Mycroft event listeners
client.on('recognizer_loop:wakeword', wakeword)  # if wakeword detected
client.on('recognizer_loop:record_end',
          pausing)  # if recording of user's message has ended
client.on('recognizer_loop:utterance',
          replying)  # if user has finished speaking

while True:
    try:
        # better keep clean here
        pass
    except KeyboardInterrupt:
        observer.stop()
        observer.join()

    observer.join()
예제 #8
0
class AppMainWindow(QtWidgets.QMainWindow):
    def __init__(self, ws):
        super().__init__()
        self.ws = ws
        self.user_utterance = ''
        self.mycroft_response = ''
        self.mic_muted = False
        self.active_skills = []
        self.unactive_skills = []
        self.title = 'UBUVoiceAssistant 1.2'
        self.top = 0
        self.left = 0
        self.width = 500
        self.height = 600
        self.next_message = 0
        self.intent_labels = []
        self.setup_ui()

    def setup_ui(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.top, self.left, self.width, self.height)
        self.center_on_screen()
        self.centralwidget = QtWidgets.QWidget(self)
        self.setCentralWidget(self.centralwidget)
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)

        spacer_item = QtWidgets.QSpacerItem(40, 20,
                                            QtWidgets.QSizePolicy.Expanding,
                                            QtWidgets.QSizePolicy.Minimum)
        self.gridLayout.addItem(spacer_item, 1, 2, 1, 3)

        self.verticalLayout_intents = QtWidgets.QVBoxLayout()
        self.gridLayout.addLayout(self.verticalLayout_intents, 2, 1, 1, 1)

        self.label_intent1 = self.create_intent_label()
        self.label_intent2 = self.create_intent_label()
        self.label_intent3 = self.create_intent_label()
        self.label_intent4 = self.create_intent_label()
        self.label_intent5 = self.create_intent_label()
        self.label_intent6 = self.create_intent_label()
        self.label_intent7 = self.create_intent_label()

        self.line = QtWidgets.QFrame(self.centralwidget)
        size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred,
                                            QtWidgets.QSizePolicy.Maximum)
        size_policy.setHorizontalStretch(0)
        size_policy.setVerticalStretch(0)
        size_policy.setHeightForWidth(
            self.line.sizePolicy().hasHeightForWidth())
        self.line.setSizePolicy(size_policy)
        self.line.setLineWidth(1)
        self.line.setMidLineWidth(0)
        self.line.setFrameShape(QtWidgets.QFrame.HLine)
        self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.gridLayout.addWidget(self.line, 3, 1, 1, 5)

        self.lineEdit_chat_message = QtWidgets.QLineEdit(self.centralwidget)
        size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,
                                            QtWidgets.QSizePolicy.Fixed)
        size_policy.setHorizontalStretch(0)
        size_policy.setVerticalStretch(0)
        size_policy.setHeightForWidth(
            self.lineEdit_chat_message.sizePolicy().hasHeightForWidth())
        self.lineEdit_chat_message.setSizePolicy(size_policy)
        self.gridLayout.addWidget(self.lineEdit_chat_message, 8, 1, 1, 4)

        self.label_intents_title = QtWidgets.QLabel(self.centralwidget)
        size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum,
                                            QtWidgets.QSizePolicy.Maximum)
        size_policy.setHorizontalStretch(0)
        size_policy.setVerticalStretch(0)
        size_policy.setHeightForWidth(
            self.label_intents_title.sizePolicy().hasHeightForWidth())
        self.label_intents_title.setSizePolicy(size_policy)
        font_questions_title = QtGui.QFont()
        font_questions_title.setPointSize(16)
        self.label_intents_title.setFont(font_questions_title)
        self.gridLayout.addWidget(self.label_intents_title, 1, 1, 1, 1)

        self.scrollArea = QtWidgets.QScrollArea(self.centralwidget)
        size_policy = QtWidgets.QSizePolicy(
            QtWidgets.QSizePolicy.MinimumExpanding,
            QtWidgets.QSizePolicy.MinimumExpanding)
        size_policy.setHorizontalStretch(0)
        size_policy.setVerticalStretch(0)
        size_policy.setHeightForWidth(
            self.scrollArea.sizePolicy().hasHeightForWidth())
        self.scrollArea.setSizePolicy(size_policy)
        self.scrollArea.setWidgetResizable(True)
        self.scrollAreaWidgetContents = QtWidgets.QWidget()
        self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 768, 410))
        self.verticalLayout = QtWidgets.QVBoxLayout(
            self.scrollAreaWidgetContents)
        self.gridLayout_conversation = QtWidgets.QGridLayout()
        self.verticalLayout.addLayout(self.gridLayout_conversation)
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
        self.gridLayout.addWidget(self.scrollArea, 7, 1, 1, 5)

        self.label_chat_title = QtWidgets.QLabel(self.centralwidget)
        size_policy = QtWidgets.QSizePolicy(
            QtWidgets.QSizePolicy.MinimumExpanding,
            QtWidgets.QSizePolicy.Maximum)
        size_policy.setHorizontalStretch(0)
        size_policy.setVerticalStretch(0)
        size_policy.setHeightForWidth(
            self.label_chat_title.sizePolicy().hasHeightForWidth())
        font_chat_title = QtGui.QFont()
        font_chat_title.setPointSize(14)
        self.label_chat_title.setSizePolicy(size_policy)
        self.label_chat_title.setFont(font_chat_title)
        self.gridLayout.addWidget(self.label_chat_title, 5, 1)

        self.pushButton_mic = QtWidgets.QPushButton(self.centralwidget)
        size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum,
                                            QtWidgets.QSizePolicy.Maximum)
        size_policy.setHorizontalStretch(0)
        size_policy.setVerticalStretch(0)
        size_policy.setHeightForWidth(
            self.pushButton_mic.sizePolicy().hasHeightForWidth())
        self.pushButton_mic.setSizePolicy(size_policy)
        self.mic_icon = QtGui.QIcon()
        self.mic_icon.addPixmap(QtGui.QPixmap("imgs/mic.svg"))
        self.mic_muted_icon = QtGui.QIcon()
        self.mic_muted_icon.addPixmap(QtGui.QPixmap("imgs/mic_muted.svg"))
        self.pushButton_mic.setIcon(self.mic_icon)
        palette = QtGui.QPalette()
        brush = QtGui.QBrush(QtGui.QColor(115, 210, 22))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Button, brush)
        self.pushButton_mic.setPalette(palette)
        self.pushButton_mic.clicked.connect(self.on_mic_pressed)
        self.gridLayout.addWidget(self.pushButton_mic, 5, 5)

        self.pushButton_send = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_send.setGeometry(QtCore.QRect(399, 550, 50, 30))
        self.pushButton_send.setPalette(palette)
        self.pushButton_send.clicked.connect(self.on_send_pressed)
        self.gridLayout.addWidget(self.pushButton_send, 8, 5, 1, 1)

        self.pushButton_logs = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_logs.setGeometry(QtCore.QRect(370, 10, 120, 40))
        self.logs_file_icon = QtGui.QIcon()
        self.logs_file_icon.addPixmap(QtGui.QPixmap("imgs/file.svg"))
        self.pushButton_logs.setIcon(self.logs_file_icon)
        self.pushButton_logs.clicked.connect(self.on_logs_pressed)
        self.gridLayout.addWidget(self.pushButton_logs, 1, 5, 1, 1)

        self.skills_dialog = QtWidgets.QDialog(self)
        self.skills_dialog.setWindowTitle('Mycroft Skills')
        self.skills_dialog.resize(600, 600)

        self.pushButton_skills = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_skills.setGeometry(QtCore.QRect(370, 60, 120, 40))
        self.skills_list_icon = QtGui.QIcon()
        self.skills_list_icon.addPixmap(QtGui.QPixmap("imgs/list.svg"))
        self.pushButton_skills.setIcon(self.skills_list_icon)
        self.pushButton_skills.clicked.connect(self.on_skills_pressed)
        self.gridLayout.addWidget(self.pushButton_skills, 2, 5, 1, 1)

        self.pushButton_manage_skills = QtWidgets.QPushButton(
            self.skills_dialog)
        self.pushButton_manage_skills.setGeometry(
            QtCore.QRect(470, 10, 120, 40))
        self.pushButton_manage_skills.clicked.connect(
            self.on_manage_skills_pressed)

        # List of the skills that the user should not interact with
        dangerous_skills = [
            'mycroft-volume.mycroftai', 'mycroft-stop.mycroftai',
            'fallback-unknown.mycroftai', 'fallback-query.mycroftai',
            'mycroft-configuration.mycroftai'
        ]

        # List of the skills in the /opt/mycroft/skills folder
        [self.active_skills.append(name) for name in listdir('/opt/mycroft/skills/') \
            if path.isdir('/opt/mycroft/skills/' + name) and name not in dangerous_skills]

        # Check if the chat needs to be updated every second
        self.timer = QtCore.QTimer(self)
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.check_for_chat_update)
        self.timer.start()

        self.retranslate_ui()

        # Send the webservice class to Mycroft
        server_socket = Thread(target=util.create_server_socket,
                               args=[self.ws])
        server_socket.setDaemon(True)
        server_socket.start()

        # Start Mycroft services
        subprocess.run([
            'bash',
            path.expanduser('~') + '/mycroft-core/start-mycroft.sh', 'all',
            'restart'
        ])

        # Wait until Mycroft services are started, there might be a better solution
        time.sleep(15)

        # Thread connected to Mycroft MessageBusClient
        self.bus = MessageBusClient()
        self.bus.run_in_thread()
        self.bus.on('speak', self.handle_speak)
        self.bus.on('recognizer_loop:utterance', self.handle_utterance)

        # Deactivate mycroft-volume.mycroftai skill, mic works weird when it's active
        self.bus.emit(
            Message('skillmanager.deactivate',
                    {'skill': 'mycroft-volume.mycroftai'}))

    def retranslate_ui(self):
        if environ['lang'] == 'es-es':
            self.lineEdit_chat_message.setPlaceholderText(
                "O puedes escribir tu pregunta")
            self.label_chat_title.setText("Conversacion")
            self.pushButton_send.setText("Enviar")
            self.pushButton_logs.setText("Abrir Logs")
            self.pushButton_skills.setText("Administrar Skills")
            self.pushButton_mic.setText('Mute')
            self.label_intents_title.setText(
                'Puedes preguntar: "Hey Mycroft...')
            self.label_intent1.setText('...abre el calendario"')
            self.label_intent2.setText('...dime los eventos de (asignatura)"')
            self.label_intent5.setText('...dime los foros de (asignatura)"')
            self.label_intent6.setText('...dime mis notas"')
            self.label_intent3.setText(
                '...dime los eventos del (dia) de (mes) de (año)"')
            self.label_intent4.setText('...dime los cambios en (asignatura)"')
            self.label_intent7.setText('...dime las notas de (asignatura)"')
            self.pushButton_manage_skills.setText("Guardar")
        elif environ['lang'] == 'en-us':
            self.lineEdit_chat_message.setPlaceholderText(
                "Or you can ask via text")
            self.label_chat_title.setText("Conversation")
            self.pushButton_send.setText("Send")
            self.pushButton_logs.setText("Open Logs")
            self.pushButton_skills.setText("Manage Skills")
            self.pushButton_mic.setText('Mute')
            self.label_intents_title.setText('You can ask: "Hey Mycroft...')
            self.label_intent1.setText('...open the calendar"')
            self.label_intent5.setText(
                '...tell me about the forums of (course)"')
            self.label_intent6.setText('...tell me my grades"')
            self.label_intent2.setText(
                '...tell me about the events of (course)"')
            self.label_intent3.setText(
                '...tell me about the events on (month) (day) (year)"')
            self.label_intent4.setText(
                '...tell me about the changes of (course)"')
            self.label_intent7.setText('...tell me the grades of (course)')
            self.pushButton_manage_skills.setText("Save")

    def create_intent_label(self):
        intent_label = QtWidgets.QLabel(self.centralwidget)
        size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum,
                                            QtWidgets.QSizePolicy.Maximum)
        size_policy.setHorizontalStretch(0)
        size_policy.setVerticalStretch(0)
        size_policy.setHeightForWidth(
            intent_label.sizePolicy().hasHeightForWidth())
        intent_label.setSizePolicy(size_policy)
        self.verticalLayout_intents.addWidget(intent_label)
        return intent_label

    def update_chat(self, source):
        """ Adds a new label to the chat's gridLayout to the corresponding side
        ---
            Parameters:
                - Char source: 'r' to add to the right side (the response)
                               'u' to add to the left side (the user)
        """
        tmp_label = QtWidgets.QLabel(self.scrollAreaWidgetContents)
        tmp_label.setWordWrap(True)
        if source == 'r':
            self.gridLayout.addWidget(tmp_label, self.next_message, 1)
            self.gridLayout_conversation.addWidget(tmp_label,
                                                   self.next_message, 1)
            tmp_label.setText(self.mycroft_response)
            self.mycroft_response = ''
        elif source == 'u':
            self.gridLayout_conversation.addWidget(tmp_label,
                                                   self.next_message, 0)
            tmp_label.setText(self.user_utterance)
            self.user_utterance = ''
        self.next_message += 1

    def handle_speak(self, message):
        self.mycroft_response = message.data.get('utterance')

    def handle_utterance(self, message):
        self.user_utterance = message.data['utterances'][0]

    def check_for_chat_update(self):
        """ Checks if there's a new message either in self.user_utterance or
            self.mycroft_response and updates the chat if so
        """
        if self.user_utterance:
            self.update_chat('u')
        if self.mycroft_response:
            self.update_chat('r')

    def on_send_pressed(self):
        self.user_utterance = self.lineEdit_chat_message.text()
        self.bus.emit(
            Message('recognizer_loop:utterance',
                    {'utterances': [self.user_utterance]}))
        self.lineEdit_chat_message.setText('')

    def on_mic_pressed(self):
        # Switch between muted and unmuted when the mic is pressed
        if self.mic_muted:
            self.mic_muted = False
            self.pushButton_mic.setIcon(self.mic_icon)
            self.pushButton_mic.setText('Mute')
            palette = QtGui.QPalette()
            brush = QtGui.QBrush(QtGui.QColor(115, 210, 22))
            brush.setStyle(QtCore.Qt.SolidPattern)
            palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Button,
                             brush)
            self.pushButton_mic.setPalette(palette)
            self.bus.emit(Message('mycroft.mic.unmute'))
        else:
            self.mic_muted = True
            self.pushButton_mic.setIcon(self.mic_muted_icon)
            self.pushButton_mic.setText('Unmute')
            palette = QtGui.QPalette()
            brush = QtGui.QBrush(QtGui.QColor(255, 35, 35))
            brush.setStyle(QtCore.Qt.SolidPattern)
            palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Button,
                             brush)
            self.pushButton_mic.setPalette(palette)
            self.bus.emit(Message('mycroft.mic.mute'))

    def on_logs_pressed(self):
        self.log_dialog = LogDialog()
        self.log_dialog.show()

    def on_skills_pressed(self):

        scroll_area_skills = QtWidgets.QScrollArea(self.skills_dialog)
        scroll_area_skills.setGeometry(QtCore.QRect(10, 10, 450, 580))
        scroll_area_skills.setWidgetResizable(True)
        scroll_area_widget_skills = QtWidgets.QWidget()
        scroll_area_skills.setWidget(scroll_area_widget_skills)

        skills_grid_layout = QtWidgets.QGridLayout(scroll_area_widget_skills)
        skills_grid_layout.setGeometry(QtCore.QRect(10, 10, 450, 580))

        self.active_skills_checkBoxes = []
        self.unactive_skills_checkBoxes = []

        # Create checkboxes for every skill in self.active_skills
        for count, name in enumerate(self.active_skills):
            check_box = QtWidgets.QCheckBox(scroll_area_widget_skills)
            spacer = QtWidgets.QSpacerItem(40, 20,
                                           QtWidgets.QSizePolicy.Expanding,
                                           QtWidgets.QSizePolicy.Minimum)
            check_box.setText(name)
            check_box.setChecked(True)
            logo = QtWidgets.QLabel(scroll_area_widget_skills)
            if 'ubu' in name:
                logo.setPixmap(
                    QtGui.QPixmap('imgs/ubu_logo.jpg').scaled(20, 20))
            else:
                logo.setPixmap(
                    QtGui.QPixmap('imgs/Mycroft_logo.png').scaled(20, 20))
            self.active_skills_checkBoxes.append(check_box)
            skills_grid_layout.addWidget(logo, count, 0)
            skills_grid_layout.addWidget(check_box, count, 1)
            skills_grid_layout.addItem(spacer, count, 2, QtCore.Qt.AlignLeft)

        # Create checkboxes for every skill in self.unactive_skills
        for count, name in enumerate(self.unactive_skills,
                                     len(self.active_skills)):
            check_box = QtWidgets.QCheckBox(scroll_area_widget_skills)
            check_box.setText(name)
            logo = QtWidgets.QLabel(scroll_area_widget_skills)
            if 'ubu' in name:
                logo.setPixmap(
                    QtGui.QPixmap('imgs/ubu_logo.jpg').scaled(20, 20))
            else:
                logo.setPixmap(
                    QtGui.QPixmap('imgs/Mycroft_logo.png').scaled(20, 20))
            self.unactive_skills_checkBoxes.append(check_box)
            skills_grid_layout.addWidget(logo, count, 0)
            skills_grid_layout.addWidget(check_box, count, 1)
            skills_grid_layout.addItem(spacer, count, 2, QtCore.Qt.AlignLeft)

        self.skills_dialog.show()

    def on_manage_skills_pressed(self):
        """ Adds the checked skills to self.active_skills and the unchecked to
            self.unactive_skills and activates or deactivates those skills.
        """
        deactivated = []
        activated = []
        for cb in self.active_skills_checkBoxes:
            if not cb.isChecked():
                self.bus.emit(
                    Message('skillmanager.deactivate', {'skill': cb.text()}))
                deactivated.append(cb.text())

        for cb in self.unactive_skills_checkBoxes:
            if cb.isChecked():
                self.bus.emit(
                    Message('skillmanager.activate', {'skill': cb.text()}))
                activated.append(cb.text())

        self.active_skills = [
            skill for skill in self.active_skills if skill not in deactivated
        ]
        self.active_skills.extend(activated)

        self.unactive_skills = [
            skill for skill in self.unactive_skills if skill not in activated
        ]
        self.unactive_skills.extend(deactivated)

        self.skills_dialog.hide()
        self.on_skills_pressed()

    def keyPressEvent(self, event):
        if event.key() in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
            self.on_send_pressed()

    def center_on_screen(self):
        resolution = QtWidgets.QDesktopWidget().screenGeometry()
        self.move((resolution.width() / 2) - (self.frameSize().width() / 2),
                  (resolution.height() / 2) - (self.frameSize().height() / 2))

    def closeEvent(self, event):
        self.close = QtWidgets.QMessageBox()
        self.close.setStandardButtons(QtWidgets.QMessageBox.Yes
                                      | QtWidgets.QMessageBox.Cancel)
        self.close.setWindowTitle(self.title)
        if environ['lang'] == 'es-es':
            self.close.setText("¿Estas seguro?")
        elif environ['lang'] == 'en-us':
            self.close.setText("Are you sure?")
        self.close = self.close.exec()

        if self.close == QtWidgets.QMessageBox.Yes:
            self.timer.stop()
            subprocess.run([
                'bash',
                path.expanduser('~') + '/mycroft-core/stop-mycroft.sh'
            ])
            event.accept()
        else:
            event.ignore()