Exemplo n.º 1
0
    def initialize(self):
        self.load_data_files(dirname(__file__))

        welcome_intent = IntentBuilder("WelcIntent").require("WelcKey").build()
        self.register_intent(welcome_intent, self.handle_welcome_intent)
Exemplo n.º 2
0
class MeshSkill(MycroftSkill):

    # The constructor of the skill, which calls Mycroft Skill's constructor
    def __init__(self):
        super(MeshSkill, self).__init__(name="MeshSkill")
        # Initialize settings values
        self._is_setup = False
        self.notifier_bool = True
        self.deviceUUID = ''  # This is the unique ID based on the Mac of this unit
        self.targetDevice = ''  # This is the targed device_id obtained through mycroft dialog
        self.base_topic = ''
        self.MQTT_Enabled = ''
        self.broker_address = ''
        self.broker_port = ''
        self.broker_uname = ''
        self.broker_pass = ''
        self.location_id = ''  # ToDo This will be retrieved from The the DeviceApi
        self.response_location = ''

    def on_connect(self, mqttc, obj, flags, rc):
        LOG.info("Connection Verified")
        LOG.info("This device location is: " +
                 DeviceApi().get()["description"])
        mqtt_path = self.base_topic + "/RemoteDevices/" + str(self.location_id)
        qos = 0
        mqttc.subscribe(mqtt_path, qos)
        LOG.info('Mesh-Skill Subscribing to: ' + mqtt_path)

    def on_disconnect(self, mqttc, obj, flags, rc):
        self._is_setup = False
        LOG.info("MQTT has Dis-Connected")

    def on_message(self, mqttc, obj,
                   msg):  # called when a new MQTT message is received
        # Sample Payload {"source":"basement", "message":"is dinner ready yet"}
        LOG.info('message received for location id: ' + str(self.location_id))
        LOG.info("This device location is: " +
                 DeviceApi().get()["description"])
        try:
            mqtt_message = msg.payload.decode('utf-8')
            LOG.info(msg.topic + " " + str(msg.qos) + ", " + mqtt_message)
            new_message = json.loads(mqtt_message)
            if "command" in new_message:
                # example: {"source":"kitchen", "command":"what time is it"}
                LOG.info('Command Received! - ' + new_message["command"] +
                         ', From: ' + new_message["source"])
                self.response_location = new_message["source"]
                self.send_message(new_message["command"])
            elif "message" in new_message:
                # example: {"source":"kitchen", "message":"is dinner ready yet"}
                self.response_location = ''
                LOG.info('Message Received! - ' + new_message["message"] +
                         ', From: ' + new_message["source"])
                self.speak_dialog('location',
                                  data={"result": new_message["source"]},
                                  expect_response=False)
                wait_while_speaking()
                self.speak_dialog('message',
                                  data={"result": new_message["message"]},
                                  expect_response=False)
            else:
                LOG.info('Unable to decode the MQTT Message')
        except Exception as e:
            LOG.error('Error: {0}'.format(e))

    # This method loads the files needed for the skill's functioning, and
    # creates and registers each intent that the skill uses
    def initialize(self):
        self.load_data_files(dirname(__file__))
        #  Check and then monitor for credential changes
        self.settings.set_changed_callback(self.on_websettings_changed)
        self.on_websettings_changed()
        self.deviceUUID = self.get_mac_address()
        self.add_event('recognizer_loop:utterance',
                       self.handle_utterances)  # should be "utterances"
        self.add_event('speak', self.handle_speak)  # should be "utterance"
        mqttc.on_connect = self.on_connect
        mqttc.on_message = self.on_message
        mqttc.on_disconnect = self.on_disconnect
        if self._is_setup:
            self.mqtt_init()

    def on_websettings_changed(self):  # called when updating mycroft home page
        self._is_setup = False
        self.MQTT_Enabled = self.settings.get(
            "MQTT_Enabled", False)  # used to enable / disable mqtt
        self.broker_address = self.settings.get("broker_address", "127.0.0.1")
        self.base_topic = self.settings.get("base_topic", "Mycroft")
        self.broker_port = self.settings.get("broker_port", 1883)
        self.broker_uname = self.settings.get("broker_uname", "")
        self.broker_pass = self.settings.get("broker_pass", "")
        # self.location_id = self.settings.get("location_id", "basement")  # This is the device_id of this device
        this_location_id = str(DeviceApi().get()["description"])
        self.location_id = this_location_id.lower()
        LOG.info("This device location is: " + str(self.location_id))
        try:
            mqttc
            LOG.info('Client exist')
            mqttc.loop_stop()
            mqttc.disconnect()
            LOG.info('Stopped old client loop')
        except NameError:
            mqttc = mqtt.Client()
            LOG.info('Client re-created')
        LOG.info("Websettings Changed! " + self.broker_address + ", " +
                 str(self.broker_port))
        self.mqtt_init()
        self._is_setup = True

    def mqtt_init(
        self
    ):  # initializes the MQTT configuration and subscribes to its own topic
        if self.MQTT_Enabled:
            LOG.info('MQTT Is Enabled')
            try:
                LOG.info("Connecting to host: " + self.broker_address +
                         ", on port: " + str(self.broker_port))
                if self.broker_uname and self.broker_pass:
                    LOG.info("Using MQTT Authentication")
                    mqttc.username_pw_set(username=self.broker_uname,
                                          password=self.broker_pass)
                mqttc.connect_async(self.broker_address, self.broker_port, 60)
                mqttc.loop_start()
                LOG.info("MQTT Loop Started Successfully")
                # LOG.info("This device location is: " + DeviceApi().get()["description"])
            except Exception as e:
                LOG.error('Error: {0}'.format(e))

    def id_generator(self,
                     size=6,
                     chars=string.ascii_uppercase + string.digits):
        return ''.join(random.choice(chars) for _ in range(size))

    def get_mac_address(
            self):  #used to create a unique UUID for this device that
        node = uuid.getnode()
        mac = uuid.UUID(int=node).hex[-12:]
        LOG.info("MQTT using UUID: " + mac)
        return mac

    # utterance event used for notifications ***This is what the user requests***
    def handle_utterances(self, message):
        mqtt_path = self.base_topic + "/RemoteDevices/" + self.deviceUUID + "/request"
        # LOG.info(mqtt_path)
        voice_payload = str(message.data.get('utterances')[0])
        if self.notifier_bool:
            try:
                # LOG.info(voice_payload)
                self.send_MQTT(mqtt_path, voice_payload)
            except Exception as e:
                LOG.error(e)
                self.on_websettings_changed()

    # mycroft speaking event used for notificatons ***This is what mycroft says***
    def handle_speak(self, message):
        mqtt_path = self.base_topic + "/RemoteDevices/" + self.deviceUUID + "/response"
        # LOG.info(mqtt_path)
        voice_payload = message.data.get('utterance')
        if self.notifier_bool:
            try:
                self.send_MQTT(mqtt_path, voice_payload)
                LOG.info("Response Location Length: " +
                         str(len(self.response_location)))
                if len(self.response_location) == 0:
                    self.response_location = ''
                else:
                    reply_payload = {
                        "source": str(self.location_id),
                        "message": voice_payload
                    }
                    reply_path = self.base_topic + "/RemoteDevices/" + self.response_location
                    self.response_location = ''
                    self.send_MQTT(reply_path, reply_payload)
            except Exception as e:
                LOG.error(e)
                self.on_websettings_changed()

    def send_MQTT(self, my_topic, my_message):  # Sends MQTT Message
        # LOG.info("This device location is: " + DeviceApi().get()["description"])
        if self.MQTT_Enabled and self._is_setup:
            LOG.info("MQTT: " + my_topic + ", " + json.dumps(my_message))
            # myID = self.id_generator()
            LOG.info("address: " + self.broker_address + ", Port: " +
                     str(self.broker_port))
            publish.single(my_topic,
                           json.dumps(my_message),
                           hostname=self.broker_address)
        else:
            LOG.info(
                "MQTT has been disabled in the websettings at https://home.mycroft.ai"
            )

    def send_message(
            self,
            message):  # Sends the remote received commands to the messagebus
        LOG.info("Sending a command to the message bus: " + message)
        payload = json.dumps({
            "type": "recognizer_loop:utterance",
            "context": "",
            "data": {
                "utterances": [message]
            }
        })
        uri = 'ws://localhost:8181/core'
        ws = create_connection(uri)
        ws.send(payload)
        ws.close()

    # First step in the dialog is to receive the initial request to "send a message/command"
    @intent_handler(
        IntentBuilder("SendMessageIntent").require("SendKeyword").require(
            "MessageTypeKeyword").optionally("RemoteKeyword").build())
    def handle_send_message_intent(self, message):
        message_json = {}  # create json object
        message_json['source'] = str(self.location_id)
        # LOG.info("This device location is: " + DeviceApi().get()["description"])
        msg_type = message.data.get("MessageTypeKeyword")
        if self.config_core.get('lang') == 'nl-nl':
            if message.data.get("MessageTypeKeyword") == 'commando':
                msg_type = 'command'
            elif message.data.get("MessageTypeKeyword") == 'bericht':
                msg_type = 'message'
        self.targetDevice = self.get_response('request.location',
                                              data={"result": msg_type})
        message_json[msg_type] = self.get_response('request.details',
                                                   data={"result": msg_type})
        LOG.info("Preparing to Send a message to " + self.targetDevice)
        self.speak_dialog('sending.message',
                          data={
                              "message": msg_type,
                              "location": self.targetDevice
                          },
                          expect_response=False)
        mqtt_path = self.base_topic + "/RemoteDevices/" + str(
            self.targetDevice).lower()
        self.send_MQTT(mqtt_path, message_json)

    def stop(self):
        pass
    def initialize(self):
    	"""
	Mycroft Google Calendar Intents
	"""

        self.load_data_files(dirname(__file__))
	self.load_regex_files(join(dirname(__file__), 'regex', 'en-us'))

        self.emitter.on(self.name + '.google_calendar',self.google_calendar)
        self.emitter.emit(Message(self.name + '.google_calendar'))

        intent = IntentBuilder('WhenEventsFutureIntent')\
            .require('WhenKeyword') \
            .require('IsKeyword') \
	    .require('Ask') \
            .build()
        self.register_intent(intent, self.handle_when_event_future)
	"""

        intent = IntentBuilder('WhenEventsFutureIntent')\
            .require('DateKeyword') \
            .require('OfKeyword') \
            .require('TheKeyword') \
            .build()
        self.register_intent(intent, self.handle_when_event_future)
	"""

	# *****************************************************
	# Add event Intent to Calendar Section
	# ***************************************************** //  .optionally('OnKeyword') \

        intent = IntentBuilder('AddMonthDayIntent')\
            .require('ScheduleKeyword') \
	    .optionally('CalendarGroupKeyword') \
            .require('EventKeyword') \
	    .require('MonthKeyword') \
	    .require('XDaysKeyword') \
	    .require('AtKeyword') \
	    .require('HoursKeyword') \
	    .optionally('AMPMKeyword') \
	    .optionally('Description') \
            .build()
        self.register_intent(intent, self.handle_add_month_day_event)

        intent = IntentBuilder('AddEventIntent')\
            .require('SetKeyword') \
	    .optionally('CalendarGroupKeyword') \
            .require('EventKeyword') \
	    .optionally('DateEventKeyword') \
	    .require('AtKeyword') \
	    .require('HoursKeyword') \
	    .optionally('AMPMKeyword') \
	    .optionally('Description') \
            .build()
        self.register_intent(intent, self.handle_add_event)

        intent = IntentBuilder('AddMonthDayStartEndIntent')\
            .require('ScheduleKeyword') \
	    .optionally('CalendarGroupKeyword') \
            .require('EventKeyword') \
	    .optionally('OnKeyword') \
	    .require('MonthKeyword') \
	    .require('XDaysKeyword') \
	    .require('FromKeyword') \
	    .require('HoursKeyword') \
	    .optionally('AMPMKeyword') \
	    .require('ToKeyword') \
	    .require('HoursEndKeyword') \
	    .optionally('AMPMEndKeyword') \
	    .optionally('Description') \
            .build()
        self.register_intent(intent, self.handle_add_month_start_end_event)

        intent = IntentBuilder('AddEventStartEndIntent')\
            .require('SetKeyword') \
    	    .optionally('CalendarGroupKeyword') \
            .require('EventKeyword') \
	    .optionally('DateEventKeyword') \
	    .require('FromKeyword') \
	    .require('HoursKeyword') \
	    .optionally('AMPMKeyword') \
	    .require('HoursEndKeyword') \
	    .optionally('AMPMEndKeyword') \
	    .optionally('Description') \
            .build()
        self.register_intent(intent, self.handle_add_start_end_event)

	# *****************************************************
	# Get events Intent from Calendar Section
	# *****************************************************
        intent = IntentBuilder('NextEventIntent')\
            .require('NextKeyword') \
            .require('EventKeyword') \
	    .optionally('WhereKeyword') \
            .build()
        self.register_intent(intent, self.handle_next_event)

        intent = IntentBuilder('WithEventIntent')\
            .require('EventKeyword') \
	    .require('Person') \
            .build()
        self.register_intent(intent, self.handle_with_event)

        intent = IntentBuilder('TodaysEventsIntent')\
	    .require('ForKeyword') \
            .require('TodayKeyword') \
            .require('EventKeyword') \
            .build()
        self.register_intent(intent, self.handle_today_events)

        intent = IntentBuilder('TomorrowEventsIntent')\
	    .require('ForKeyword') \
            .require('TomorrowKeyword') \
            .require('EventKeyword') \
            .build()
        self.register_intent(intent, self.handle_tomorrow_events)

        intent = IntentBuilder('UntilTomorrowEventsIntent')\
            .require('UntilTomorrowKeyword') \
            .require('EventKeyword') \
            .build()
        self.register_intent(intent, self.handle_until_tomorrow_events)

        intent = IntentBuilder('EventsForWeekDayIntent')\
	    .require('ForKeyword') \
            .require('WeekdayKeyword') \
            .require('EventKeyword') \
            .build()
        self.register_intent(intent, self.handle_weekday_events)

        intent = IntentBuilder('EventsForXDaysIntent')\
	    .require('FollowingsKeyword') \
	    .require('EventKeyword') \
            .require('XDaysKeyword') \
            .build()
        self.register_intent(intent, self.handle_xdays_events)
Exemplo n.º 4
0
    def _connect(self, message):
        url = "http://localhost:6680"
        try:
            self.mopidy = Mopidy(url)
        except:
            logger.info('Could not connect to server, retrying in 10 sec')
            time.sleep(10)
            self.emitter.emit(Message(self.name + '.connect'))
            return

        self.albums = {}
        self.artists = {}
        self.genres = {}
        self.playlists = {}
        self.radios = {}

        logger.info('Loading content')
        self.albums['gmusic'] = self.mopidy.get_gmusic_albums()
        self.artists['gmusic'] = self.mopidy.get_gmusic_artists()
        self.genres['gmusic'] = self.mopidy.get_gmusic_radio()
        self.playlists['gmusic'] = {}

        self.albums['local'] = self.mopidy.get_local_albums()
        self.artists['local'] = self.mopidy.get_local_artists()
        self.genres['local'] = self.mopidy.get_local_genres()
        self.playlists['local'] = self.mopidy.get_local_playlists()

        self.albums['spotify'] = {}
        self.artists['spotify'] = {}
        self.genres['spotify'] = {}
        self.playlists['spotify'] = self.mopidy.get_spotify_playlists()

        self.playlist = {}
        for loc in ['local', 'gmusic', 'spotify']:
            logger.info(loc)
            self.playlist.update(self.playlists[loc])
            logger.info(loc)
            self.playlist.update(self.genres[loc])
            logger.info(loc)
            self.playlist.update(self.artists[loc])
            logger.info(loc)
            self.playlist.update(self.albums[loc])

        self.register_vocabulary(self.name, 'NameKeyword')
        for p in self.playlist.keys():
            logger.debug("Playlist: " + p)
            self.register_vocabulary(p, 'PlaylistKeyword' + self.name)
        intent = IntentBuilder('PlayPlaylistIntent' + self.name)\
            .require('PlayKeyword')\
            .require('PlaylistKeyword' + self.name)\
            .build()
        self.register_intent(intent, self.handle_play_playlist)
        intent = IntentBuilder('PlayFromIntent' + self.name)\
            .require('PlayKeyword')\
            .require('PlaylistKeyword')\
            .require('NameKeyword')\
            .build()
        self.register_intent(intent, self.handle_play_playlist)

        self.register_regex("for (?P<Source>.*)")
        intent = IntentBuilder('SearchSpotifyIntent' + self.name)\
            .require('SearchKeyword')\
            .optionally('Source')\
            .require('SpotifyKeyword')\
            .build()
        self.register_intent(intent, self.search_spotify)
Exemplo n.º 5
0
stop_keywords = [
    'stop',
    'terminate',
    'end',
    'quit',
    'disable',
    'unenroll',
]

for enroll_keyword in enroll_keywords:
    engine.register_entity(enroll_keyword, "EnrollKeywords")

for stop_keyword in stop_keywords:
    engine.register_entity(stop_keyword, "StopKeywords")

enroll_intent = IntentBuilder(
    MessageIntent.ENROLL.value).require("EnrollKeywords").build()
stop_intent = IntentBuilder(
    MessageIntent.STOP.value).require("StopKeywords").build()

engine.register_intent_parser(enroll_intent)
engine.register_intent_parser(stop_intent)


def identify_message(message: str) -> MessageIntent:
    for intent in engine.determine_intent(utterance=message):
        if intent and intent.get('confidence') > 0:
            return MessageIntent(intent.get('intent_type'))
    return MessageIntent.OTHER
Exemplo n.º 6
0
class PlaybackControlSkill(MycroftSkill):
    def __init__(self):
        super(PlaybackControlSkill, self).__init__('Playback Control Skill')
        self.query_replies = {}  # cache of received replies
        self.query_extensions = {}  # maintains query timeout extensions
        self.has_played = False
        self.lock = Lock()

    # TODO: Make this an option for voc_match()?  Only difference is the
    #       comparison using "==" instead of "in"
    def voc_match_exact(self, utt, voc_filename, lang=None):
        """ Determine if the given utterance contains the vocabulary provided

        Checks for vocabulary match in the utterance instead of the other
        way around to allow the user to say things like "yes, please" and
        still match against "Yes.voc" containing only "yes". The method first
        checks in the current skill's .voc files and secondly the "res/text"
        folder of mycroft-core. The result is cached to avoid hitting the
        disk each time the method is called.

        Args:
            utt (str): Utterance to be tested
            voc_filename (str): Name of vocabulary file (e.g. 'yes' for
                                'res/text/en-us/yes.voc')
            lang (str): Language code, defaults to self.long

        Returns:
            bool: True if the utterance has the given vocabulary it
        """
        lang = lang or self.lang
        cache_key = lang + voc_filename

        if cache_key not in self.voc_match_cache:
            # Check for both skill resources and mycroft-core resources
            voc = self.find_resource(voc_filename + '.voc', 'vocab')
            if not voc:
                voc = self.resolve_resource_file(
                    join('text', lang, voc_filename + '.voc'))

            if not voc or not exists(voc):
                raise FileNotFoundError(
                    'Could not find {}.voc file'.format(voc_filename))

            with open(voc) as f:
                self.voc_match_cache[cache_key] = f.read().splitlines()

        # Check for exact match
        if utt and any(i.strip() == utt
                       for i in self.voc_match_cache[cache_key]):
            return True
        return False

    def initialize(self):
        self.audio_service = AudioService(self.bus)
        self.add_event('play:query.response', self.handle_play_query_response)
        self.add_event('play:status', self.handle_song_info)
        self.gui.register_handler('next', self.handle_next)
        self.gui.register_handler('prev', self.handle_prev)

        self.clear_gui_info()

    # Handle common audio intents.  'Audio' skills should listen for the
    # common messages:
    #   self.add_event('mycroft.audio.service.next', SKILL_HANDLER)
    #   self.add_event('mycroft.audio.service.prev', SKILL_HANDLER)
    #   self.add_event('mycroft.audio.service.pause', SKILL_HANDLER)
    #   self.add_event('mycroft.audio.service.resume', SKILL_HANDLER)

    def clear_gui_info(self):
        """Clear the gui variable list."""
        # Initialize track info variables
        for k in STATUS_KEYS:
            self.gui[k] = ''

    @intent_handler(IntentBuilder('').require('Next').require("Track"))
    def handle_next(self, message):
        self.audio_service.next()

    @intent_handler(IntentBuilder('').require('Prev').require("Track"))
    def handle_prev(self, message):
        self.audio_service.prev()

    @intent_handler(IntentBuilder('').require('Pause'))
    def handle_pause(self, message):
        self.audio_service.pause()

    @intent_handler(IntentBuilder('').one_of('PlayResume', 'Resume'))
    def handle_play(self, message):
        """Resume playback if paused"""
        self.audio_service.resume()

    def stop(self, message=None):
        self.clear_gui_info()

        self.log.info('Audio service status: '
                      '{}'.format(self.audio_service.track_info()))
        if self.audio_service.is_playing:
            self.audio_service.stop()
            return True
        else:
            return False

    def converse(self, utterances, lang="en-us"):
        if (utterances and self.has_played
                and self.voc_match_exact(utterances[0], "converse_resume")):
            # NOTE:  voc_match() will overmatch (e.g. it'll catch "play next
            #        song" or "play Some Artist")
            self.audio_service.resume()
            return True
        else:
            return False

    @intent_handler(IntentBuilder('').require('Play').require('Phrase'))
    def play(self, message):
        self.speak_dialog("just.one.moment")

        # Remove everything up to and including "Play"
        # NOTE: This requires a Play.voc which holds any synomyms for 'Play'
        #       and a .rx that contains each of those synonyms.  E.g.
        #  Play.voc
        #      play
        #      bork
        #  phrase.rx
        #      play (?P<Phrase>.*)
        #      bork (?P<Phrase>.*)
        # This really just hacks around limitations of the Adapt regex system,
        # which will only return the first word of the target phrase
        utt = message.data.get('utterance')
        phrase = re.sub('^.*?' + message.data['Play'], '', utt).strip()
        self.log.info("Resolving Player for: " + phrase)
        wait_while_speaking()
        self.enclosure.mouth_think()

        # Now we place a query on the messsagebus for anyone who wants to
        # attempt to service a 'play.request' message.  E.g.:
        #   {
        #      "type": "play.query",
        #      "phrase": "the news" / "tom waits" / "madonna on Pandora"
        #   }
        #
        # One or more skills can reply with a 'play.request.reply', e.g.:
        #   {
        #      "type": "play.request.response",
        #      "target": "the news",
        #      "skill_id": "<self.skill_id>",
        #      "conf": "0.7",
        #      "callback_data": "<optional data>"
        #   }
        # This means the skill has a 70% confidence they can handle that
        # request.  The "callback_data" is optional, but can provide data
        # that eliminates the need to re-parse if this reply is chosen.
        #
        self.query_replies[phrase] = []
        self.query_extensions[phrase] = []
        self.bus.emit(message.forward('play:query', data={"phrase": phrase}))

        self.schedule_event(self._play_query_timeout,
                            1,
                            data={"phrase": phrase},
                            name='PlayQueryTimeout')

    def handle_play_query_response(self, message):
        with self.lock:
            search_phrase = message.data["phrase"]

            if ("searching" in message.data
                    and search_phrase in self.query_extensions):
                # Manage requests for time to complete searches
                skill_id = message.data["skill_id"]
                if message.data["searching"]:
                    # extend the timeout by 5 seconds
                    self.cancel_scheduled_event("PlayQueryTimeout")
                    self.schedule_event(self._play_query_timeout,
                                        5,
                                        data={"phrase": search_phrase},
                                        name='PlayQueryTimeout')

                    # TODO: Perhaps block multiple extensions?
                    if skill_id not in self.query_extensions[search_phrase]:
                        self.query_extensions[search_phrase].append(skill_id)
                else:
                    # Search complete, don't wait on this skill any longer
                    if skill_id in self.query_extensions[search_phrase]:
                        self.query_extensions[search_phrase].remove(skill_id)
                        if not self.query_extensions[search_phrase]:
                            self.cancel_scheduled_event("PlayQueryTimeout")
                            self.schedule_event(self._play_query_timeout,
                                                0,
                                                data={"phrase": search_phrase},
                                                name='PlayQueryTimeout')

            elif search_phrase in self.query_replies:
                # Collect all replies until the timeout
                self.query_replies[message.data["phrase"]].append(message.data)

    def _play_query_timeout(self, message):
        with self.lock:
            # Prevent any late-comers from retriggering this query handler
            search_phrase = message.data["phrase"]
            self.query_extensions[search_phrase] = []
            self.enclosure.mouth_reset()

            # Look at any replies that arrived before the timeout
            # Find response(s) with the highest confidence
            best = None
            ties = []
            self.log.debug("CommonPlay Resolution: {}".format(search_phrase))
            for handler in self.query_replies[search_phrase]:
                self.log.debug("    {} using {}".format(
                    handler["conf"], handler["skill_id"]))
                if not best or handler["conf"] > best["conf"]:
                    best = handler
                    ties = []
                elif handler["conf"] == best["conf"]:
                    ties.append(handler)

            if best:
                if ties:
                    # TODO: Ask user to pick between ties or do it
                    # automagically
                    pass

                # invoke best match
                self.gui.show_page("controls.qml", override_idle=True)
                self.log.info("Playing with: {}".format(best["skill_id"]))
                start_data = {
                    "skill_id": best["skill_id"],
                    "phrase": search_phrase,
                    "callback_data": best.get("callback_data")
                }
                self.bus.emit(message.forward('play:start', start_data))
                self.has_played = True
            elif self.voc_match(search_phrase, "Music"):
                self.speak_dialog("setup.hints")
            else:
                self.log.info("   No matches")
                self.speak_dialog("cant.play", data={"phrase": search_phrase})

            if search_phrase in self.query_replies:
                del self.query_replies[search_phrase]
            if search_phrase in self.query_extensions:
                del self.query_extensions[search_phrase]

    def handle_song_info(self, message):
        changed = False
        for key in STATUS_KEYS:
            val = message.data.get(key, '')
            changed = changed or self.gui[key] != val
            self.gui[key] = val

        if changed:
            self.log.info('\n-->Track: {}\n-->Artist: {}\n-->Image: {}'
                          ''.format(self.gui['track'], self.gui['artist'],
                                    self.gui['image']))
Exemplo n.º 7
0
class PianobarSkill(MycroftSkill):
    def __init__(self):
        super(PianobarSkill, self).__init__(name="PianobarSkill")
        self.process = None
        self.piano_bar_state = None  # 'playing', 'paused', 'autopause'
        self.current_station = None
        self._is_setup = False
        self.vocabs = []  # keep a list of vocabulary words
        self.pianobar_path = expanduser('~/.config/pianobar')
        self._pianobar_initated = False
        self.debug_mode = False
        self.idle_count = 0

        # Initialize settings values
        self.settings["email"] = ""
        self.settings["password"] = ""
        self.settings["song_artist"] = ""
        self.settings["song_title"] = ""
        self.settings["song_album"] = ""
        self.settings["station_name"] = ""
        self.settings["station_count"] = 0
        self.settings["stations"] = []
        self.settings["last_played"] = None
        self.settings['first_init'] = True  # True = first run ever

        subprocess.call(["killall", "-9", "pianobar"])

    def initialize(self):
        self._load_vocab_files()

        # Check and then monitor for credential changes
        self.settings.set_changed_callback(self.on_websettings_changed)
        self.on_websettings_changed()
        self.add_event('mycroft.stop', self.stop)

    ######################################################################
    # 'Auto ducking' - pause playback when Mycroft wakes

    def handle_listener_started(self, message):
        if self.piano_bar_state == "playing":
            self.handle_pause()
            self.piano_bar_state = "autopause"

    def handle_listener_ended(self, message):
        if self.piano_bar_state == "autopause":
            # Start idle check
            self.idle_count = 0
            self.cancel_scheduled_event('IdleCheck')
            self.schedule_repeating_event(self.check_for_idle,
                                          None,
                                          1,
                                          name='IdleCheck')

    def check_for_idle(self):
        if not self.piano_bar_state == "autopause":
            self.cancel_scheduled_event('IdleCheck')
            return
        active = DisplayManager.get_active()
        # LOG.info("active: " + str(active))
        if active == "PianobarSkill" or not active:
            # No activity, start to fall asleep
            self.idle_count += 1

            if self.idle_count >= 2:
                # Resume playback after 2 seconds of being idle
                self.cancel_scheduled_event('IdleCheck')
                self.handle_resume_song()
        else:
            self.idle_count = 0

    ######################################################################

    def _register_all_intents(self):
        """ Intents should only be registered once settings are inputed
            to avoid conflicts with other music skills
        """
        next_station_intent = IntentBuilder("PandoraNextStationIntent"). \
            require("Next").require("Station").build()
        self.register_intent(next_station_intent, self.handle_next_station)

        list_stations_intent = IntentBuilder("PandoraListStationIntent"). \
            optionally("Pandora").require("Query").require("Station").build()
        self.register_intent(list_stations_intent, self.handle_list)

        play_stations_intent = IntentBuilder("PandoraChangeStationIntent"). \
            require("Change").require("Station").build()
        self.register_intent(play_stations_intent, self.play_station)

        # Messages from the skill-playback-control / common Audio service
        self.add_event('mycroft.audio.service.pause', self.handle_pause)
        self.add_event('mycroft.audio.service.resume', self.handle_resume_song)
        self.add_event('mycroft.audio.service.next', self.handle_next_song)

    def on_websettings_changed(self):
        if not self._is_setup:
            email = self.settings.get("email", "")
            password = self.settings.get("password", "")
            try:
                if email and password:
                    self._configure_pianobar()
                    self._init_pianobar()
                    self._register_all_intents()
                    self._is_setup = True
            except Exception as e:
                LOG.error(e)

    def _configure_pianobar(self):
        # Initialize the Pianobar configuration file
        if not exists(self.pianobar_path):
            makedirs(self.pianobar_path)

        config_path = join(self.pianobar_path, 'config')
        with open(config_path, 'w+') as f:

            # grabs the tls_key needed
            tls_key = subprocess.check_output(
                "openssl s_client -connect tuner.pandora.com:443 \
                < /dev/null 2> /dev/null | openssl x509 -noout \
                -fingerprint | tr -d ':' | cut -d'=' -f2",
                shell=True)

            config = 'audio_quality = medium\n' + \
                     'tls_fingerprint = {}\n' + \
                     'user = {}\n' + \
                     'password = {}\n' + \
                     'event_command = {}'

            f.write(
                config.format(tls_key, self.settings["email"],
                              self.settings["password"],
                              self._dir + '/event_command.py'))

        # Raspbian requires adjustments to audio output to use PulseAudio
        platform = self.config_core['enclosure'].get('platform')
        if platform == 'picroft' or platform == 'mycroft_mark_1':
            libao_path = expanduser('~/.libao')
            if not isfile(libao_path):
                with open(libao_path, 'w') as f:
                    f.write('dev=0\ndefault_driver=pulse')
                self.speak_dialog("configured.please.reboot")
                wait_while_speaking()
                self.emitter.emit(Message('system.reboot'))

    def _load_vocab_files(self):
        # Keep a list of all the vocabulary words for this skill.  Later
        # these words will be removed from utterances as part of the station
        # name.
        vocab_dir = join(dirname(__file__), 'vocab', self.lang)
        if path.exists(vocab_dir):
            for vocab_type in listdir(vocab_dir):
                if vocab_type.endswith(".voc"):
                    with open(join(vocab_dir, vocab_type), 'r') as voc_file:
                        for line in voc_file:
                            parts = line.strip().split("|")
                            vocab = parts[0]
                            self.vocabs.append(vocab)
        else:
            LOG.error('No vocab loaded, ' + vocab_dir + ' does not exist')

    def start_monitor(self):
        # Clear any existing event
        self.stop_monitor()

        # Schedule a new one every second to monitor/update display
        self.schedule_repeating_event(self._poll_for_pianobar_update,
                                      None,
                                      1,
                                      name='MonitorPianobar')
        self.add_event('recognizer_loop:record_begin',
                       self.handle_listener_started)
        self.add_event('recognizer_loop:utterance', self.handle_listener_ended)

    def stop_monitor(self):
        # Clear any existing event
        self.cancel_scheduled_event('MonitorPianobar')

    def _poll_for_pianobar_update(self, message):
        # Checks once a second for feedback from Pianobar

        # 'info_ready' file is created by the event_command.py
        # script when Pianobar sends new track information.
        info_ready_path = join(self.pianobar_path, 'info_ready')
        if isfile(info_ready_path):
            self._load_current_info()
            try:
                remove(info_ready_path)
            except Exception as e:
                LOG.error(e)

            # Update the "Now Playing song"
            LOG.info("State: " + str(self.piano_bar_state))
            if self.piano_bar_state == "playing":
                self.enclosure.mouth_text(self.settings["song_artist"] + ": " +
                                          self.settings["song_title"])

    def cmd(self, s):
        self.process.stdin.write(s.encode())
        self.process.stdin.flush()

    def _init_pianobar(self):
        if self.settings.get('first_init') is False:
            return

        # Run this exactly one time to prepare pianobar for usage
        # by Mycroft.
        try:
            LOG.info("INIT PIANOBAR")
            subprocess.call(["killall", "-9", "pianobar"])
            self.process = subprocess.Popen(["pianobar"],
                                            stdin=subprocess.PIPE,
                                            stdout=subprocess.PIPE)
            time.sleep(3)
            self.cmd("0\n")
            self.cmd("S")
            time.sleep(0.5)
            self.process.kill()
            self.settings['first_init'] = False
            self._load_current_info()
        except Exception as e:
            LOG.exception('Failed to connect to Pandora')
            self.speak_dialog('wrong.credentials')

        self.process = None

    def _load_current_info(self):
        # Load the 'info' file created by Pianobar when changing tracks
        info_path = join(self.pianobar_path, 'info')

        # this is a hack to remove the info_path
        # if it's a directory. An earlier version
        # of code may sometimes create info_path as
        # a directory instead of a file path
        # date: 02-18
        if isdir(info_path):
            shutil.rmtree(info_path)

        if not exists(info_path):
            with open(info_path, 'w+'):
                pass

        with open(info_path, 'r') as f:
            info = json.load(f)

        # Save the song info for later display
        self.settings["song_artist"] = info["artist"]
        self.settings["song_title"] = info["title"]
        self.settings["song_album"] = info["album"]

        self.settings["station_name"] = info["stationName"]
        if self.debug_mode:
            LOG.info("Station name: " + str(self.settings['station_name']))
        self.settings["station_count"] = int(info["stationCount"])
        self.settings["stations"] = []
        for index in range(self.settings["station_count"]):
            station = "station" + str(index)
            self.settings["stations"].append(
                (info[station].replace("Radio", ""), index))
        if self.debug_mode:
            LOG.info("Stations: " + str(self.settings["stations"]))
        # self.settings.store()

    def _launch_pianobar_process(self):
        try:
            LOG.info("Starting Pianobar process")
            subprocess.call(["killall", "-9", "pianobar"])
            time.sleep(1)

            # start pandora
            if self.debug_mode:
                self.process = \
                    subprocess.Popen(["pianobar"], stdin=subprocess.PIPE)
            else:
                self.process = subprocess.Popen(["pianobar"],
                                                stdin=subprocess.PIPE,
                                                stdout=subprocess.PIPE)
            self.current_station = "0"
            self.cmd("0\n")
            self.handle_pause()
            time.sleep(2)
            self._load_current_info()
            LOG.info("Pianobar process initialized")
        except Exception:
            LOG.exception('Failed to connect to Pandora')
            self.speak_dialog('wrong.credentials')
            self.process = None

    def _extract_station(self, utterance):
        """
            parse the utterance for station names
            and return station with highest probability
        """
        try:
            # TODO: Internationalize

            common_words = [" to ", " on ", " pandora", " play"]
            for vocab in self.vocabs:
                utterance = utterance.replace(vocab, "")

            # strip out other non important words
            for words in common_words:
                utterance = utterance.replace(words, "")

            utterance.lstrip()
            stations = [station[0] for station in self.settings["stations"]]
            probabilities = fuzz_process.extractOne(utterance,
                                                    stations,
                                                    scorer=fuzz.ratio)
            if self.debug_mode:
                LOG.info("Probabilities: " + str(probabilities))
            if probabilities[1] > 70:
                station = probabilities[0]
                return station
            else:
                return None
        except Exception as e:
            LOG.info(e)
            return None

    def _play_station(self, station, dialog=None):
        LOG.info("Starting: " + str(station))
        self._launch_pianobar_process()

        if not self.process:
            return

        if dialog:
            self.speak_dialog(dialog, {"station": station})
        else:
            if station:
                self.speak_dialog("playing.station", {"station": station})

        self.enclosure.mouth_think()
        if station:
            for channel in self.settings.get("stations"):
                if station == channel[0]:
                    self.cmd("s")
                    self.current_station = str(channel[1])
                    station_number = str(channel[1]) + "\n"
                    self.cmd(station_number)
                    self.piano_bar_state = "playing"
                    self.settings["last_played"] = channel
                    self.start_monitor()
        else:
            time.sleep(2)  # wait for pianobar to loading
            if self.debug_mode:
                LOG.info(self.settings.get('stations'))
            # try catch block because some systems
            # may not load pianobar info in time
            try:
                channel = self.settings.get("stations")[0]
                if self.debug_mode:
                    LOG.info(channel)
                if channel:
                    self.speak_dialog("playing.station",
                                      {"station": channel[0]})
                    station_number = str(channel[1]) + "\n"
                    if self.debug_mode:
                        LOG.info(station_number)
                    self.cmd(station_number)
                    self.settings["last_played"] = channel
                else:
                    raise ValueError
            except Exception as e:
                LOG.info(e)
                self.speak_dialog("playing.station", {"station": "pandora"})
                self.current_station = "0"
                self.cmd("0\n")
            self.handle_resume_song()
            self.piano_bar_state = "playing"
            self.start_monitor()

    @intent_handler(IntentBuilder("").require("Play").require("Pandora"))
    def play_pandora(self, message=None):
        if self._is_setup:
            # Examine the whole utterance to see if the user requested a
            # station by name
            station = self._extract_station(message.data["utterance"])
            if self.debug_mode:
                LOG.info("Station request:" + str(station))

            dialog = None
            if not station:
                last_played = self.settings.get("last_played")
                if last_played:
                    station = last_played[0]
                    dialog = "resuming.last.station"
                else:
                    # default to the first station in the list
                    if self.settings.get("stations"):
                        station = self.settings["stations"][0][0]

            # Play specified station
            self._play_station(station, dialog)
        else:
            # Lead user to setup for Pandora
            self.speak_dialog("please.register.pandora")

    def handle_next_song(self, message=None):
        if self.process:
            self.enclosure.mouth_think()
            self.cmd("n")
            self.piano_bar_state = "playing"
            self.start_monitor()

    def handle_next_station(self, message=None):
        if self.process and self.settings.get("stations"):
            new_station = int(self.current_station) + 1
            if new_station >= int(self.settings.get("station_count", 0)):
                new_station = 0
            new_station = self.settings["stations"][new_station][0]
            self._play_station(new_station)

    def handle_pause(self, message=None):
        if self.process:
            self.cmd("S")
            self.process.stdin.flush()
            self.piano_bar_state = "paused"
            self.stop_monitor()

    def handle_resume_song(self, message=None):
        if self.process:
            self.cmd("P")
            self.piano_bar_state = "playing"
            self.start_monitor()

    def play_station(self, message=None):
        if self._is_setup:
            # Examine the whole utterance to see if the user requested a
            # station by name
            station = self._extract_station(message.data["utterance"])

            if station is not None:
                self._play_station(station)
            else:
                self.speak_dialog("no.matching.station")
        else:
            # Lead user to setup for Pandora
            self.speak_dialog("please.register.pandora")

    def handle_list(self, message=None):
        is_playing = self.piano_bar_state == "playing"
        if is_playing:
            self.handle_pause()

        # build the list of stations
        l = []
        for station in self.settings.get("stations"):
            l.append(station[0])  # [0] = name
        if len(l) == 0:
            self.speak_dialog("no.stations")
            return

        # read the list
        if len(l) > 1:
            list = ', '.join(l[:-1]) + " " + \
                   self.translate("and") + " " + l[-1]
        else:
            list = str(l)
        self.speak_dialog("subscribed.to.stations", {"stations": list})

        if is_playing:
            wait_while_speaking()
            self.handle_resume_song()

    @intent_handler(
        IntentBuilder("PandoraLoveSongIntent").require("Love").require("Song"))
    def handle_love(self, message=None):
        if self.process:
            utterance = message.data["utterance"]
            if "don't" in utterance or "not" in utterance:
                self.handle_ban(message)
                return
            self.cmd("+")
            self.speak_dialog("love.song")
            # if self.piano_bar_state != "playing":
            #     self.handle_resume_song()

    @intent_handler(
        IntentBuilder("PandoraBanSongIntent").require("Ban").require("Song"))
    def handle_ban(self, message=None):
        if self.process:
            self.cmd("-")
            self.speak_dialog("ban.song")

    @intent_handler(
        IntentBuilder("PandoraTiredSongIntent").require("Tired").require(
            "Song"))
    def handle_tired(self, message=None):
        if self.process:
            self.cmd("t")
            self.speak_dialog("tired.song")

    @intent_handler(
        IntentBuilder("PandoraVolumeRaiseIntent").require("Raise").require(
            "Pandora").require("Volume"))
    def handle_volume_raise(self, message=None):
        if self.process:
            self.cmd(")")
            self.speak_dialog("raised.pandora.volume")

    @intent_handler(
        IntentBuilder("PandoraVolumeLowerIntent").require("Lower").require(
            "Pandora").require("Volume"))
    def handle_volume_lower(self, message=None):
        if self.process:
            self.cmd("(")
            self.speak_dialog("lowered.pandora.volume")

    @intent_handler(
        IntentBuilder("PandoraVolumeResetIntent").require("Reset").require(
            "Pandora").require("Volume"))
    def handle_volume_reset(self, message=None):
        if self.process:
            self.cmd("^")
            self.speak_dialog("reset.pandora.volume")

    @intent_handler(
        IntentBuilder("PandoraTrackInfo").require("Song").require("Playing"))
    def handle_track_info(self, message=None):
        if self.process:
            song = self.settings["song_title"]
            artist = self.settings["song_artist"]
            album = self.settings["song_album"]
            self.speak_dialog("track.info", {
                "song": song,
                "artist": artist,
                "album": album
            })

    def stop(self):
        LOG.info('STOPPING PANDORA')
        if not self.piano_bar_state == "paused":
            LOG.info('YES')
            self.handle_pause()
            self.enclosure.mouth_reset()
            return True

    @intent_handler(
        IntentBuilder("").require("Pandora").require("Debug").require("On"))
    def debug_on_intent(self, message=None):
        if not self.debug_mode:
            self.debug_mode = True
            self.speak_dialog("entering.debug.mode")

    @intent_handler(
        IntentBuilder("").require("Pandora").require("Debug").require("Off"))
    def debug_off_intent(self, message=None):
        if self.debug_mode:
            self.debug_mode = False
            self.speak_dialog("leaving.debug.mode")

    def shutdown(self):
        self.stop_monitor()

        # Clean up before shutting down the skill
        if self.piano_bar_state == "playing":
            self.enclosure.mouth_reset()

        if self.process:
            self.cmd("q")
        super(PianobarSkill, self).shutdown()
Exemplo n.º 8
0
 def initialize(self):
     what_my_heart_rate = IntentBuilder("HeartMonitor"). \
         require("HeartMonitor").build()
     self.register_intent(what_my_heart_rate, self.handle_what_my_heart_rate)
Exemplo n.º 9
0
 def initialize(self):
     self.load_data_files(dirname(__file__))
     next_launch_intent = IntentBuilder("NextLaunchIntent").\
         require("NextLaunchKeyword").build()
     self.register_intent(next_launch_intent, self.handle_next_launch_intent)
Exemplo n.º 10
0
class YoutubeMpvSkill(MycroftSkill):
    def __init__(self):
        super(YoutubeMpvSkill, self).__init__(name="YoutubeMpvSkill")

        self.search = ""
        self.url = ""
        self.volume = 80
        self.mpv_start = "mpv --volume={}\
            --input-ipc-server=/tmp/mpvsocket {} &"

        self.pause_state = "true"
        self.mpv_process = "ps cax | grep mpv"

        # if you want add more options use the mpv manpages
        self.mpv_echo = "echo '{}' | socat - /tmp/mpvsocket"

        # setter for unix socket
        self.mpv_pause = '"command": ["set_property", "pause", {}]'
        self.mpv_volume = '"command": ["set_property", "volume", {}]'
        self.mpv_speed = '"command": ["set_property", "speed", {}]'
        self.mpv_seek = '"command": [ "seek", "{}" ]'

        # getter from unix socket
        self.mpv_duration = '"command": ["get_property", "duration"]'
        self.mpv_time_pos = '"command": ["get_property", "time-pos"]'
        self.mpv_time_remaining = '"command": ["get_property", \
            "time-remaining"]'

        self.mpv_get_volume = '"command": ["get_property", "volume"]'
        self.mpv_get_speed = '"command": ["get_property", "speed"]'

        self.mpv_stop = "killall mpv"

    def getResults(self, search, pos=0):
        query = urllib.parse.quote(search)
        link = "https://www.youtube.com/results?search_query=" + query
        response = urllib.request.urlopen(link)
        html = response.read()
        soup = BeautifulSoup(html, 'html.parser')
        vids = soup.findAll(attrs={'class': 'yt-uix-tile-link'})
        url = 'https://www.youtube.com' + vids[pos]['href']
        return url

    def mpvExists(self):
        cmd = subprocess.Popen("command -v mpv",
                               stdout=subprocess.PIPE,
                               shell=True).stdout.read()
        cmd = cmd.decode("utf-8")
        if (cmd != ''):
            return True
        else:
            return False

    def isMpvRunning(self):
        exists = subprocess.Popen(self.mpv_process,
                                  stdout=subprocess.PIPE,
                                  shell=True).stdout.read()
        exists = exists.decode("utf-8")

        if (exists == '' or exists is None):
            return False
        else:
            return True

    def mpvStart(self):
        if (not self.isMpvRunning()):
            subprocess.call(self.mpv_start.format(self.volume, self.url),
                            shell=True)
            self.speak_dialog("ytmpv.start", data={"search": self.search})
        else:
            self.speak_dialog("ytmpv.running")

    def mpvPause(self, state):
        self.pause_state = state
        pause = self.mpv_pause.format(self.pause_state)
        cmd = self.mpv_echo.format("{" + pause + "}")
        subprocess.call(cmd, shell=True)

    def mpvStop(self):
        subprocess.call("killall mpv", shell=True)
        subprocess.call("rm /tmp/mpvsocket", shell=True)
        self.speak_dialog("ytmpv.stop", data={"search": self.search})
        self.pause_state = "true"

    def mpvChangeVol(self, volume):
        if (volume <= 0):
            volume = 0
        elif (volume >= 100):
            volume = 100

        self.volume = volume
        cmd = self.mpv_echo.format("{" + self.mpv_volume.format(volume) + "}")
        subprocess.call(cmd, shell=True)

    def mpvChangeSpeed(self, speed):
        if (speed <= 0):
            speed = 0
        elif (speed >= 5):
            speed = 5

        self.speed = speed
        cmd = self.mpv_echo.format("{" + self.mpv_speed.format(speed) + "}")
        subprocess.call(cmd, shell=True)

    def mpvSeek(self, secs):
        cmd = self.mpv_echo.format("{" + self.mpv_seek.format(secs) + "}")
        subprocess.call(cmd, shell=True)

    def getDuration(self):
        if (self.isMpvRunning()):
            cmd = self.mpv_echo.format("{" + self.mpv_duration + "}")
            data = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                                    shell=True).stdout.read()
            j = json.loads(data)
            secs = int(str(j["data"]).split(".")[0])
            m = int(secs / 60)
            rest = secs % 60
            self.speak_dialog("ytmpv.duration", data={"min": m, "sec": rest})

    def getPosition(self):
        cmd = self.mpv_echo.format("{" + self.mpv_time_pos + "}")
        data = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                                shell=True).stdout.read()
        j = json.loads(data)
        secs = int(str(j["data"]).split(".")[0])
        m = int(secs / 60)
        rest = secs % 60
        self.speak_dialog("ytmpv.position", data={"min": m, "sec": rest})

    def getRemaining(self):
        cmd = self.mpv_echo.format("{" + self.mpv_time_remaining + "}")
        data = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                                shell=True).stdout.read()
        j = json.loads(data)
        secs = int(str(j["data"]).split(".")[0])
        m = int(secs / 60)
        rest = secs % 60
        self.speak_dialog("ytmpv.remaining", data={"min": m, "sec": rest})

    def getVolume(self):
        cmd = self.mpv_echo.format('{' + self.mpv_get_volume + '}')
        data = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                                shell=True).stdout.read()
        j = json.loads(data)
        vol = str(j["data"]).split('.')[0]
        self.speak_dialog("ytmpv.volume", data={"vol": vol})

    def getSpeed(self):
        cmd = self.mpv_echo.format('{' + self.mpv_get_speed + '}')
        data = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                                shell=True).stdout.read()
        j = json.loads(data)
        num = str(j["data"]).split(".")[0]
        point = str(j["data"]).split(".")[1]
        self.speak_dialog("ytmpv.get.speed", data={"num": num, "point": point})

    @intent_handler(IntentBuilder("").require("Start"))
    def handle_youtubempv_intent(self, message):
        try:
            cmd = str(message.data.get('Start'))
            msg = str(message.data.get('utterance')).replace(cmd + " ", "", 1)
            if (self.mpvExists()):
                # TODO adding translations in voc
                self.search = msg
                self.url = self.getResults(self.search)
                self.mpvStart()
            else:
                self.speak_dialog("ytmpv.not.exists")
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("Pause"))
    def handle_youtubempv_pause_intent(self, message):
        try:
            if (self.mpvExists()):
                self.mpvPause("true")
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("Resume"))
    def handle_youtubempv_play_intent(self, message):
        try:
            if (self.mpvExists()):
                self.mpvPause("false")
            else:
                self.speak_dialog("ytmpv.not.exists")
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("Exit"))
    def handle_youtubempv_exit_intent(self, message):
        try:
            if (self.mpvExists()):
                self.mpvStop()
            else:
                self.speak_dialog("ytmpv.not.exists")
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("Volume"))
    def handle_youtubempv_volume_intent(self, message):
        try:
            if (self.mpvExists()):
                msg = str(message.data.get("utterance")).split(" ")[2]
                if (msg != ''):
                    num = int(msg)
                    self.mpvChangeVol(num)
            else:
                self.speak_dialog("ytmpv.not.exists")
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("VolumeDown"))
    def handle_youtubempv_volume_down_intent(self, message):
        try:
            if (self.mpvExists()):
                self.mpvChangeVol(self.volume - 10)
            else:
                self.speak_dialog("ytmpv.not.exists")
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("VolumeUp"))
    def handle_youtubempv_volume_up_intent(self, message):
        try:
            if (self.mpvExists()):
                self.mpvChangeVol(self.volume + 10)
            else:
                self.speak_dialog("ytmpv.not.exists")
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("Speed"))
    def handle_youtubempv_speed_intent(self, message):
        try:
            if (self.mpvExists()):
                msg = str(message.data.get("utterance")).split(" ")[2]
                if (msg != ''):
                    num = float(msg)
                    self.mpvChangeSpeed(num)
            else:
                self.speak_dialog("ytmpv.not.exists")
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("SpeedDown"))
    def handle_youtubempv_speed_down_intent(self, message):
        try:
            if (self.mpvExists()):
                self.mpvChangeSpeed(self.speed - 0.2)
            else:
                self.speak_dialog("ytmpv.not.exists")
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("SpeedUp"))
    def handle_youtubempv_speed_up_intent(self, message):
        try:
            if (self.mpvExists()):
                self.mpvChangeSpeed(self.volume + 0.2)
            else:
                self.speak_dialog("ytmpv.not.exists")
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("Seek"))
    def handle_youtubempv_seek_intent(self, message):
        try:
            if (self.mpvExists()):
                msg = str(message.data.get("utterance")).split(" ")[2]
                if (msg != ''):
                    secs = int(msg)
                    self.mpvSeek(secs)
                # self.mpvChangeSpeed(self.volume+0.2)
            else:
                self.speak_dialog("ytmpv.not.exists")
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("SeekForward"))
    def handle_youtubempv_seek_forward_intent(self, message):
        try:
            if (self.mpvExists()):
                msg = str(message.data.get("utterance")).split(" ")[3]
                if (msg != ''):
                    secs = int(msg)
                    self.mpvSeek(secs)
                else:
                    self.mpvSeek(20)
                # self.mpvChangeSpeed(self.volume+0.2)
            else:
                self.speak_dialog("ytmpv.not.exists")
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("SeekBackward"))
    def handle_youtubempv_seek_backward_intent(self, message):
        try:
            if (self.mpvExists()):
                msg = str(message.data.get("utterance")).split(" ")[3]
                if (msg != ''):
                    secs = int(msg) * -1
                    self.mpvSeek(secs)
                else:
                    self.mpvSeek(-20)
                # self.mpvChangeSpeed(self.volume+0.2)
            else:
                self.speak_dialog("ytmpv.not.exists")
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("GetDuration"))
    def handle_youtubempv_get_duration_intent(self, message):
        try:
            if (self.mpvExists()):
                self.getDuration()
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("GetPosition"))
    def handle_youtubempv_get_position_intent(self, message):
        try:
            if (self.mpvExists()):
                self.getPosition()
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("GetRemaining"))
    def handle_youtubempv_get_remaining_intent(self, message):
        try:
            if (self.mpvExists()):
                self.getRemaining()
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("GetSpeed"))
    def handle_youtubempv_get_speed_intent(self, message):
        try:
            if (self.mpvExists()):
                self.getSpeed()
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")

    @intent_handler(IntentBuilder("").require("GetVolume"))
    def handle_youtubempv_get_volume_intent(self, message):
        try:
            if (self.mpvExists()):
                self.getVolume()
        except Exception as e:
            LOG.exception("YoutubeMpv Error: " + e.message)
            self.speak_dialog("ytmpv.error")
Exemplo n.º 11
0
 def initialize(self):
     intent = IntentBuilder("QuoteIntent"). \
         require("QuoteKeyword").build()
     self.register_intent(intent, self.handle_intent)
Exemplo n.º 12
0
class VolumeSkill(MycroftSkill):
    """
    Control the audio volume for the Mycroft system

    Terminology:
       "Level" =  Mycroft volume levels, from 0 to 11
       "Volume" = ALSA mixer setting, from 0 to 100
    """

    MIN_LEVEL = 0
    MAX_LEVEL = 11

    # TODO: Translation layer (have to match word in Level.voc)
    VOLUME_WORDS = {'loud': 9, 'normal': 6, 'quiet': 3}

    def __init__(self):
        super(VolumeSkill, self).__init__("VolumeSkill")
        self.default_level = self.config.get('default_level')
        self.min_volume = self.config.get('min_volume')
        self.max_volume = self.config.get('max_volume')
        self.volume_sound = join(dirname(__file__), "blop-mark-diangelo.wav")
        try:
            Mixer()
        except:
            self.log.warning('HACK: first access to Mixer() error workaround')

    def initialize(self):
        self.log.info("********** Reeg handlers")

        intent = IntentBuilder("IncreaseVolume").require("Volume").require(
            "Increase").build()
        self.register_intent(intent, self.handle_increase_volume)

        intent = IntentBuilder("DecreaseVolume").require("Volume").require(
            "Decrease").build()
        self.register_intent(intent, self.handle_decrease_volume)

        intent = IntentBuilder("MuteVolume").require("Volume").require(
            "Mute").build()
        self.register_intent(intent, self.handle_mute_volume)

        intent = IntentBuilder("UnmuteVolume").require("Volume").require(
            "Unmute").build()
        self.register_intent(intent, self.handle_unmute_volume)

        try:
            # Register handlers for messagebus events
            self.add_event('mycroft.volume.increase',
                           self.handle_increase_volume)
            self.add_event('mycroft.volume.decrease',
                           self.handle_decrease_volume)
            self.add_event('mycroft.volume.mute', self.handle_mute_volume)
            self.add_event('mycroft.volume.unmute', self.handle_unmute_volume)
            self.log.info("********** Handlers registered")
        except:
            pass

    @intent_handler(
        IntentBuilder("SetVolume").require("Volume").require("Level"))
    def handle_set_volume(self, message):
        mixer = Mixer()
        level = self.__get_volume_level(message, mixer.getvolume()[0])
        mixer.setvolume(self.__level_to_volume(level))
        self.speak_dialog('set.volume', data={'volume': level})

    @intent_handler(
        IntentBuilder("QueryVolume").require("Volume").require("Query"))
    def handle_query_volume(self, message):
        level = self.__get_volume_level(message, mixer.getvolume()[0])
        self.speak_dialog('volume.is', data={'volume': level})

    def __communicate_volume_change(self, message, dialog, code, changed):
        play_sound = message.data.get('play_sound', False)
        if play_sound:
            if changed:
                play_wav(self.volume_sound)
        else:
            if not changed:
                dialog = 'already.max.volume'
            self.speak_dialog(dialog, data={'volume': code})

#    @intent_handler(IntentBuilder("IncreaseVolume").require(
#        "Volume").require("Increase"))

    def handle_increase_volume(self, message):
        self.__communicate_volume_change(message, 'increase.volume',
                                         *self.__update_volume(+1))

#    @intent_handler(IntentBuilder("DecreaseVolume").require(
#        "Volume").require("Decrease"))

    def handle_decrease_volume(self, message):
        self.__communicate_volume_change(message, 'decrease.volume',
                                         *self.__update_volume(-1))

#    @intent_handler(IntentBuilder("MuteVolume").require(
#        "Volume").require("Mute"))

    def handle_mute_volume(self, message):
        speak_message = message.data.get('speak_message', True)
        if speak_message:
            self.speak_dialog('mute.volume')
            wait_while_speaking()
        Mixer().setvolume(0)


#    @intent_handler(IntentBuilder("UnmuteVolume").require(
#        "Volume").require("Unmute"))

    def handle_unmute_volume(self, message):
        Mixer().setvolume(self.__level_to_volume(self.default_level))
        speak_message = message.data.get('speak_message', True)
        if speak_message:
            self.speak_dialog('reset.volume',
                              data={'volume': self.default_level})

    def __volume_to_level(self, volume):
        """
        Convert a 'volume' to a 'level'

        Args:
            volume (int): min_volume..max_volume
        Returns:
            int: the equivalent level
        """
        range = self.MAX_LEVEL - self.MIN_LEVEL
        prop = float(volume - self.min_volume) / self.max_volume
        level = int(round(self.MIN_LEVEL + range * prop))
        if level > self.MAX_LEVEL:
            level = self.MAX_LEVEL
        elif level < self.MIN_LEVEL:
            level = self.MIN_LEVEL
        return level

    def __level_to_volume(self, level):
        """
        Convert a 'level' to a 'volume'

        Args:
            level (int): 0..MAX_LEVEL
        Returns:
            int: the equivalent volume
        """
        range = self.max_volume - self.min_volume
        prop = float(level) / self.MAX_LEVEL
        volume = int(round(self.min_volume + int(range) * prop))

        return volume

    @staticmethod
    def __bound_level(level):
        if level > VolumeSkill.MAX_LEVEL:
            level = VolumeSkill.MAX_LEVEL
        elif level < VolumeSkill.MIN_LEVEL:
            level = VolumeSkill.MIN_LEVEL
        return level

    def __update_volume(self, change=0):
        """
        Attempt to change audio level

        Args:
            change (int): +1 or -1; the step to change by

        Returns:
            int: new level code (0..11)
            bool: whether level changed
        """
        mixer = Mixer()
        old_level = self.__volume_to_level(mixer.getvolume()[0])
        new_level = self.__bound_level(old_level + change)
        self.enclosure.eyes_volume(new_level)
        mixer.setvolume(self.__level_to_volume(new_level))
        return new_level, new_level != old_level

    def __get_volume_level(self, message, default=None):
        level_str = message.data.get('Level', default)
        level = self.default_level

        try:
            level = self.VOLUME_WORDS[level_str]
        except KeyError:
            try:
                level = int(level_str)
                if (level > self.MAX_LEVEL):
                    # Guess that the user said something like 100 percent
                    # so convert that into a level value
                    level = self.MAX_LEVEL * level / 100
            except ValueError:
                pass

        level = self.__bound_level(level)
        return level
Exemplo n.º 13
0
    def initialize(self):
	self.load_data_files(dirname(__file__))

        Door_command_intent = IntentBuilder("DoorCommandIntent").require("DoorKeyword").require("Action").build()
        self.register_intent(Door_command_intent, self.handle_Door_command_intent)
Exemplo n.º 14
0
class SoundcloudSkill(MycroftSkill):

    # The constructor of the skill, which calls MycroftSkill's constructor
    def __init__(self):
        super(SoundcloudSkill, self).__init__(name="SoundcloudSkill")

        # Initialize working variables used within the skill.
        self.count = 0
        self.player = SoundCloudPlayer()

    # The "handle_xxxx_intent" function is triggered by Mycroft when the
    # skill's intent is matched.  The intent is defined by the IntentBuilder()
    # pieces, and is triggered when the user's utterance matches the pattern
    # defined by the keywords.  In this case, the match occurs when one word
    # is found from each of the files:
    #    vocab/en-us/Hello.voc
    #    vocab/en-us/World.voc
    # In this example that means it would match on utterances like:
    #   'Hello world'
    #   'Howdy you great big world'
    #   'Greetings planet earth'
    @intent_handler(IntentBuilder("").require("Play").require("Soundcloud"))
    def handle_soundcloud_intent(self, message):
        try:
            # In this case, respond by simply speaking a canned response.
            # Mycroft will randomly speak one of the lines from the file
            #    dialogs/en-us/hello.world.dialog
            utterance = message.data['utterance']
            LOGGER.info("utterance is " + utterance)
            play = message.data.get("Play")
            soundcloud = message.data.get("Soundcloud")
            to_word = ' ' + self.translate('To')
            on_word = ' ' + self.translate('On')
            query = utterance.replace(play, "")
            query = query.replace(play.lower(), "")
            query = query.replace(soundcloud, "")
            query = query.replace(soundcloud.lower(), "")
            query = query.replace(to_word, "")
            query = query.replace(to_word.lower(), "")
            query = query.replace(on_word, "")
            query = query.replace(on_word.lower(), "")
            trackName = query.strip()
            LOGGER.info("Finding some tracks for " + trackName)
            message.data['track'] = trackName
            self.play_song(message)
        except Exception as e:
            LOGGER.error("Error: {0}".format(e))

    @intent_handler(IntentBuilder("").require("Stop").require("Soundcloud"))
    def handle_soundcloud_stop_intent(self, message):
        self.stop()

    # The "stop" method defines what Mycroft does when told to stop during
    # the skill's execution. In this case, since the skill's functionality
    # is extremely simple, there is no need to override it.  If you DO
    # need to implement stop, you should return True to indicate you handled
    # it.
    #
    def stop(self):
        self.player.stop()
        return True

    def play_song(self, message):
        """
        When the user wants to hear a song, optionally with artist and/or
        album information attached.
        play the song <song>
        play the song <song> by <artist>
        play the song <song> off <album>
        play <song> by <artist> off the album <album>
        etc.

        Args:
            message (Dict): The utterance as interpreted by Padatious
        """
        song = message.data.get('track')

        # create a client object with your app credentials
        client = soundcloud.Client(
            client_id='bK2tF3BXmEa5vVQCI1xTZTA9NSwA5NPv')

        # find all sounds
        tracks = client.get('/tracks', q=song)
        urls = []
        name = tracks[0].title
        LOGGER.info("First track to be played is " + name)
        for track in tracks:
            urls.append(
                client.get(tracks[0].stream_url, allow_redirects=False).url)

        self.speak_dialog("play.soundcloud", data={"track": name})
        self.player.play(urls)
Exemplo n.º 15
0
 def build_intent_delete(self):
     return IntentBuilder(
         self.name + 'DeleteIntent').require(self.name + 'DeleteVerb') \
         .optionally(self.name + 'Amount').require(self.name + 'Keyword')
Exemplo n.º 16
0
            if match:
                skills = match.group(1)
            else:
                skills = ""

            # read the list for followup
            self.speak_dialog("choose", data={'skills': ", ".join(skills)})
        elif rc == 202:
            # Not found
            self.speak_dialog("not.found", data={'skill': name})
        else:
            # Other installation error, just read code
            self.speak_dialog("installation.error", data={'skill': name,
                                                          'error': rc})

    @intent_handler(IntentBuilder("UninstallIntent").require("Uninstall"))
    def uninstall(self, message):
        utterance = message.data.get('utterance').lower()
        name = utterance.replace(message.data.get('Uninstall'), '')
        self.speak_dialog("removing")

        # Invoke MSM to perform removal
        try:
            cmd = ' '.join([BIN, 'remove', '"' + name.strip() + '"'])
            output = subprocess.check_output(cmd, shell=True)
            self.log.info("MSM output: " + str(output))
            rc = 0
        except subprocess.CalledProcessError, e:
            output = e.output
            rc = e.returncode
Exemplo n.º 17
0
 def initialize(self):
     self.load_data_files(dirname(__file__))
     intent = IntentBuilder("PairingIntent") \
         .require("PairingKeyword").require("DeviceKeyword").build()
     self.register_intent(intent, self.handle_pairing)
     self.emitter.on("mycroft.not.paired", self.not_paired)
Exemplo n.º 18
0
 def initialize(self):
     super(AlarmSkill, self).initialize()
     intent = IntentBuilder(
         'AlarmSkillStopIntent').require('AlarmSkillStopVerb') \
         .require('AlarmSkillKeyword').build()
     self.register_intent(intent, self.__handle_stop)
Exemplo n.º 19
0
class GetEvent(MycroftSkill):
    def __init__(self):
        MycroftSkill.__init__(self)

    @intent_handler(IntentBuilder("").require("event.by.name"))
    def handle_get_event_by_name(self):
        # Getting credentials for Google Calendar API
        creds = None
        # The file token.pickle stores the user's access and refresh tokens, and is
        # created automatically when the authorization flow completes for the first
        # time.
        if os.path.exists('token.pickle'):
            with open('token.pickle', 'rb') as token:
                creds = pickle.load(token)
        # If there are no (valid) credentials available, let the user log in.
        if not creds or not creds.valid:
            if creds and creds.expired and creds.refresh_token:
                creds.refresh(Request())
            else:
                flow = InstalledAppFlow.from_client_secrets_file(
                    'credentials.json', SCOPES)
                creds = flow.run_local_server()
            # Save the credentials for the next run
            with open('token.pickle', 'wb') as token:
                pickle.dump(creds, token)

        service = build('calendar', 'v3', credentials=creds)

        # Query to get the event by name
        title = self.get_response('what\'s the name of the event')
        events_result = service.events().list(calendarId='primary',
                                              maxResults=1,
                                              singleEvents=True,
                                              orderBy='startTime',
                                              q=title).execute()
        events = events_result.get('items', [])

        if not events:
            self.speak('event not found')
        for event in events:
            start = event['start'].get('dateTime', event['start'].get('date'))
            location = event['location']
            description = event['description']
            self.speak_dialog('get.event',
                              data={
                                  'start': start,
                                  'title': title,
                                  'location': location,
                                  'description': description
                              })

    @intent_handler(IntentBuilder("").require("attendees.by.event"))
    def handle_get_attendees_by_event(self):
        # Getting credentials for Google Calendar API
        creds = None
        # The file token.pickle stores the user's access and refresh tokens, and is
        # created automatically when the authorization flow completes for the first
        # time.
        if os.path.exists('token.pickle'):
            with open('token.pickle', 'rb') as token:
                creds = pickle.load(token)
        # If there are no (valid) credentials available, let the user log in.
        if not creds or not creds.valid:
            if creds and creds.expired and creds.refresh_token:
                creds.refresh(Request())
            else:
                flow = InstalledAppFlow.from_client_secrets_file(
                    'credentials.json', SCOPES)
                creds = flow.run_local_server()
            # Save the credentials for the next run
            with open('token.pickle', 'wb') as token:
                pickle.dump(creds, token)

        service = build('calendar', 'v3', credentials=creds)

        # Query to get the event by name
        title = self.get_response('what\'s the name of the event')
        events_result = service.events().list(calendarId='primary',
                                              maxResults=1,
                                              singleEvents=True,
                                              orderBy='startTime',
                                              q=title).execute()
        events = events_result.get('items', [])

        attendemail = []
        attendname = []
        attendstatus = []
        i = 0

        if not events:
            print('event not found.')
        for event in events:
            start = event['start'].get('dateTime', event['start'].get('date'))

            eventid = event['id']

            attendees = event['attendees']
            l = len(attendees)
            while i != l:
                attendemail.append(attendees[i]['email'])
                attendstatus.append(attendees[i]['responseStatus'])
                attendname.append(attendees[i].get('displayName'))
                i = i + 1
        self.speak_dialog('attendees.list', data={'att': attendname})

    @intent_handler(
        IntentBuilder("").require("attendees.status").require('status'))
    def handle_get__attendees_status_by_event(self, message):
        # Getting credentials for Google Calendar API
        creds = None
        # The file token.pickle stores the user's access and refresh tokens, and is
        # created automatically when the authorization flow completes for the first
        # time.
        if os.path.exists('token.pickle'):
            with open('token.pickle', 'rb') as token:
                creds = pickle.load(token)
        # If there are no (valid) credentials available, let the user log in.
        if not creds or not creds.valid:
            if creds and creds.expired and creds.refresh_token:
                creds.refresh(Request())
            else:
                flow = InstalledAppFlow.from_client_secrets_file(
                    'credentials.json', SCOPES)
                creds = flow.run_local_server()
            # Save the credentials for the next run
            with open('token.pickle', 'wb') as token:
                pickle.dump(creds, token)

        service = build('calendar', 'v3', credentials=creds)

        # Query to get the event by name
        title = self.get_response('what\'s the name of the event')
        events_result = service.events().list(calendarId='primary',
                                              maxResults=1,
                                              singleEvents=True,
                                              orderBy='startTime',
                                              q=title).execute()
        events = events_result.get('items', [])

        attendemail = []
        attendname = []
        attendstatus = []
        confattend = []
        notyetattend = []
        declattend = []
        tentattend = []
        i = 0
        j = 0
        k = 0
        if not events:
            print('event not found.')
        for event in events:
            start = event['start'].get('dateTime', event['start'].get('date'))

            eventid = event['id']

            attendees = event['attendees']
            l = len(attendees)
            while i != l:
                attendemail.append(attendees[i]['email'])
                attendstatus.append(attendees[i]['responseStatus'])
                attendname.append(attendees[i].get('displayName'))
                i = i + 1
            while k != l:
                if attendname[k] is None:
                    attendname[k] = attendemail[k]
                    k = k + 1
            while j != l:
                if attendees[j]['responseStatus'] == 'accepted':
                    confattend.append(attendees[j].get('displayName'))
                elif attendees[j]['responseStatus'] == 'needsAction':
                    notyetattend.append(attendees[j].get('displayName'))
                elif attendees[j]['responseStatus'] == 'declined':
                    declattend.append(attendees[j].get('displayName'))
                elif attendees[j]['responseStatus'] == 'tentative':
                    tentattend.append(attendees[j].get('displayName'))
                j = j + 1

        if message.data["status"] == "confirmed":
            self.speak_dialog('confirmed.attendees.list',
                              data={'att': confattend})
        elif message.data["status"] == "did not take action":
            self.speak_dialog('notyet.attendees.list',
                              data={'att': notyetattend})
        elif message.data["status"] == "decline":
            self.speak_dialog('declined.attendees.list',
                              data={'att': declattend})
        elif message.data["status"] == "are tentative":
            self.speak_dialog('tentative.attendees.list',
                              data={'att': tentattend})

    @intent_handler(IntentBuilder("").require("first.event"))
    def handle_get_first_event(self):
        creds = None
        # The file token.pickle stores the user's access and refresh tokens, and is
        # created automatically when the authorization flow completes for the first
        # time.
        if os.path.exists('token.pickle'):
            with open('token.pickle', 'rb') as token:
                creds = pickle.load(token)
        # If there are no (valid) credentials available, let the user log in.
        if not creds or not creds.valid:
            if creds and creds.expired and creds.refresh_token:
                creds.refresh(Request())
            else:
                flow = InstalledAppFlow.from_client_secrets_file(
                    'credentials.json', SCOPES)
                creds = flow.run_local_server()
            # Save the credentials for the next run
            with open('token.pickle', 'wb') as token:
                pickle.dump(creds, token)

        service = build('calendar', 'v3', credentials=creds)

        # Call the Calendar API
        now = datetime.datetime.utcnow().isoformat(
        ) + 'Z'  # 'Z' indicates UTC time
        self.speak('Getting the first upcoming event')
        events_result = service.events().list(calendarId='primary',
                                              timeMin=now,
                                              maxResults=1,
                                              singleEvents=True,
                                              orderBy='startTime').execute()
        events = events_result.get('items', [])

        if not events:
            self.speak('No upcoming event found.')
        for event in events:
            start = event['start'].get('dateTime', event['start'].get('date'))
            title = event['summary']
            self.speak_dialog("ten.upcoming.events",
                              data={
                                  "title": title,
                                  'start': start
                              })

    @intent_handler(IntentBuilder("").require("upcoming.events"))
    def handle_get_upcoming_ten_events(self):
        """Shows basic usage of the Google Calendar API.
           Prints the start and name of the next 10 events on the user's calendar.
           """
        creds = None
        # The file token.pickle stores the user's access and refresh tokens, and is
        # created automatically when the authorization flow completes for the first
        # time.
        if os.path.exists('token.pickle'):
            with open('token.pickle', 'rb') as token:
                creds = pickle.load(token)
        # If there are no (valid) credentials available, let the user log in.
        if not creds or not creds.valid:
            if creds and creds.expired and creds.refresh_token:
                creds.refresh(Request())
            else:
                flow = InstalledAppFlow.from_client_secrets_file(
                    'credentials.json', SCOPES)
                creds = flow.run_local_server()
            # Save the credentials for the next run
            with open('token.pickle', 'wb') as token:
                pickle.dump(creds, token)

        service = build('calendar', 'v3', credentials=creds)

        # Call the Calendar API
        now = datetime.datetime.utcnow().isoformat(
        ) + 'Z'  # 'Z' indicates UTC time
        self.speak('Getting the upcoming events')
        events_result = service.events().list(calendarId='primary',
                                              timeMin=now,
                                              maxResults=10,
                                              singleEvents=True,
                                              orderBy='startTime').execute()
        events = events_result.get('items', [])

        if not events:
            self.speak('No upcoming events found.')
        for event in events:
            start = event['start'].get('dateTime', event['start'].get('date'))
            # end = event['end'].get('dateTime', event['end'].get('date'))
            # description = event['description']
            title = event['summary']
            self.speak_dialog("ten.upcoming.events",
                              data={
                                  "title": title,
                                  'start': start
                              })
Exemplo n.º 20
0
class DictionarySkill(MycroftSkill):
    def __init__(self):
        super(DictionarySkill, self).__init__(name="DictionarySkill")

    @intent_handler(IntentBuilder("DefineIntent").require("Define")
                    .require("Word"))
    def handle_define_intent(self, message):
        try:
            config = self.config_core.get("DictionarySkill", {})

            if not config == {}:
                base_url = str(config.get("base_url"))
                language = str(config.get("language"))
                app_id = str(config.get("app_id"))
                app_key = str(config.get("app_key"))

            else:
                base_url = str(self.settings.get("base_url"))
                language = str(self.settings.get("language"))
                app_id = str(self.settings.get("app_id"))
                app_key = str(self.settings.get("app_key"))

            if not base_url or not language or not app_id or not app_key:
                raise Exception("None found.")

        except Exception as e:
            self.speak_dialog("settings.error")
            self.log.error(e)
            return

        word = message.data.get("Word")

        if not word:
            return

        api_url = "{0}/entries/{1}/{2}".format(base_url, language, word)

        api_headers = {'Content-Type': 'application/json',
                       'Accept': 'application/json',
                       'app_id': app_id,
                       'app_key': app_key}

        try:
            response = requests.get(api_url, headers=api_headers)

        except Exception as e:
            self.speak_dialog("connection.error")
            self.log.error(e)
            return

        if response.status_code == 200:
            try:
                definition = response.json()['results'][0][
                                                'lexicalEntries'][0][
                                                    'entries'][0]['senses'][0][
                                                        'definitions'][0]

                self.speak_dialog("definition",
                                  {"word": word, "definition": definition})

            except KeyError:
                definition = response.json()['results'][0][
                                                'lexicalEntries'][0][
                                                    'entries'][0]['senses'][0][
                                                        'crossReferenceMarkers'
                                                                           ][0]

                self.speak_dialog("grammer",
                                  {"word": word, "definition": definition})

        elif response.status_code == 404:
            self.speak_dialog("invalid", {"word": word})

    def stop(self):
        pass
Exemplo n.º 21
0
class NPROneSkill(CommonPlaySkill):
    def __init__(self):
        super(NPROneSkill, self).__init__(name="NPROneSkill")
        self.curl = None
        self.now_playing = None
        self.last_message = None
        self.STREAM = '{}/stream'.format(get_cache_directory('NPROneSkill'))

        # read api key for NPR One from settings and pass to api managing cnstructor
        self.apiKey = self.settings.get('api_key')
        self.nprone = NPROne(self.apiKey)
        
        # change setting if modified on web
        self.settings_change_callback = self.websettings_callback

    def CPS_match_query_phrase(self, phrase):
        """ check if skill can play input phrase
            Returns: tuple (matched phrase(str),
                            match level(CPSMatchLevel),
                            optional data(dict))
                     or None if no match was found.
        """
        matched_feed = { 'key': None, 'conf': 0.0}

        # Remove "the" as it matches too well will "other"
        search_phrase = phrase.lower().replace('the', '')

        # Catch any short explicit phrases eg "play the news"
        npr1_phrases = self.translate_list("PlayNPROne") or []
        if search_phrase.strip() in npr1_phrases:
            station_key = self.settings.get("station", "not_set")
            if station_key == "not_set":
                station_key = self.get_default_station()
            matched_feed = { 'key': station_key, 'conf': 1.0 }

        # could add func here for finding specific shows/sources from pre-defined list

        # if no specific show match but utterance contains npr one, return low-ish confidence
        if matched_feed['conf'] == 0.0 and self.voc_match(search_phrase, "NPR one"):
            matched_feed = {'key': None, 'conf': 0.5 }
            match_level = CPSMatchLevel.CATEGORY
        else:
            match_level = None
            return match_level

        return (None, match_level, None )

    def CPS_start(self, phrase, data):
        """ starts playback of npr one"""
        # use default npr one news feed
        self.handle_latest_news()
        pass

    def websettings_callback(self):
        self.apiKey = self.settings.get('api_key')
        self.log.info('NPR One skill api set to ' + self.apiKey)
        self.nprone.setApiKey(self.apiKey)
    
    def setApiKey(self,key):
        self.apiKey = key

    @intent_file_handler("PlayNPROne.intent")
    def handle_npr_one_alt(self, message):
        """ capture alternatives for request """"
        utt = message.data["utterance"]
        match = self.CPS_match_query_phrase(utt)

        # feed them to skill if valid
        if match and len(match) > 2:
            feed = match[2]["feed"]
        else:
            feed = None

        self.handle_latest_news(message, feed)

    @intent_handler(IntentBuilder("").one_of("Give", "Latest").require("News"))
    def handle_latest_news(self, message=None, feed=None):
        try:
            self.stop()
            
            # speak intro while downloading feed
            self.speak_dialog('npr1', data={})
            
            # TODO: get news feed

            # Show news title if exists
            wait_while_speaking()
            # Begin the news stream
            self.log.info('Feed: {}'.format(feed))
            self.CPS_play(('file://' + self.STREAM, mime))
            self.CPS_send_status(image=image or image_path('generic.png'),
                                 track=self.now_playing)
            self.last_message = (True, message)
            self.enable_intent('restart_playback')

        except Exception as e:
            self.log.error("Error: {0}".format(e))
            self.log.info("Traceback: {}".format(traceback.format_exc()))
            self.speak_dialog("could.not.start.the.news.feed")

    @intent_handler(IntentBuilder('').require('Restart'))
    def restart_playback(self, message):
        self.log.debug('Restarting last message')
        if self.last_message:
            self.handle_latest_news(self.last_message[1])

    def stop(self):
        # Disable restarting when stopped
        if self.last_message:
            self.disable_intent('restart_playback')
            self.last_message = None
            # Stop download process if it's running.
        if self.curl:
            try:
                self.curl.kill()
                self.curl.communicate()
            except Exception as e:
                self.log.error('Could not stop curl: {}'.format(repr(e)))
            finally:
                self.curl = None
            self.CPS_send_status()
            return True

    def CPS_send_status(self, artist='', track='', image=''):
        data = {'skill': self.name,
                'artist': artist,
                'track': track,
                'image': image,
                'status': None  # TODO Add status system
                }
        self.bus.emit(Message('play:status', data))
Exemplo n.º 22
0
class SkillGuiExample(MycroftSkill):
    """
    Example Skill Showcasing All Delegates
    """
    def __init__(self):
        super().__init__("SkillGuiExample")
        self.html_resources = "file://" + dirname(__file__) + '/res/'

    def initialize(self):
        # Handle Menu and Navigation
        self.gui.register_handler('SkillGuiExample.simpleText',
                                  self.handle_gui_example_simpleText_intent)
        self.gui.register_handler('SkillGuiExample.simpleImage',
                                  self.handle_gui_example_simpleImage_intent)
        self.gui.register_handler('SkillGuiExample.paginatedText',
                                  self.handle_gui_example_paginatedText_intent)
        self.gui.register_handler('SkillGuiExample.slidingImage',
                                  self.handle_gui_example_slidingImage_intent)
        self.gui.register_handler(
            'SkillGuiExample.proportionalDelegate',
            self.handle_gui_example_proportionalDelegate_intent)
        self.gui.register_handler(
            'SkillGuiExample.proportionalDelegateWrapText',
            self.handle_gui_example_proportionalDelegateWrapText_intent)
        self.gui.register_handler('SkillGuiExample.listView',
                                  self.handle_gui_example_listView_intent)
        self.gui.register_handler('SkillGuiExample.eventsExample',
                                  self.handle_gui_example_events_intent)
        self.gui.register_handler('SkillGuiExample.audioDelegateExample',
                                  self.handle_gui_example_audioDelegate_intent)
        self.gui.register_handler('SkillGuiExample.htmlUrlExample',
                                  self.handle_gui_example_showHTMLUrl_intent)
        self.gui.register_handler('SkillGuiExample.htmlRawExample',
                                  self.handle_gui_example_showHTMLRaw_intent)
        self.gui.register_handler('SkillGuiExample.menu',
                                  self.handle_gui_example_menu_intent)

        # Handle example events
        self.gui.register_handler('SkillGuiExample.colorChange',
                                  self.change_color_event)

    @intent_handler(
        IntentBuilder('handle_gui_example_simpleText_intent').require(
            'gui.example.one'))
    def handle_gui_example_simpleText_intent(self, message):
        """
        Example Intent Showcasing Basic UI Text
        """
        self.gui.clear()
        self.enclosure.display_manager.remove_active()
        self.gui.show_text("Lorem ipsum dolor sit amet booom baka kakakaka.")

    @intent_handler(
        IntentBuilder('handle_gui_example_simpleImage_intent').require(
            'gui.example.two'))
    def handle_gui_example_simpleImage_intent(self, message):
        """
        Example Intent Showcasing Basic UI Image
        """
        self.gui.clear()
        self.enclosure.display_manager.remove_active()
        self.gui.show_image(
            "https://source.unsplash.com/1920x1080/?+random",
            "Example Long Caption That Needs Wrapping Very Long Long Text Text Example That Is",
            "Example Title")

    @intent_handler(
        IntentBuilder('handle_gui_example_paginatedText_intent').require(
            'gui.example.three'))
    def handle_gui_example_paginatedText_intent(self, message):
        """
        Example Intent Showcasing Paginated UI Text
        """
        self.gui.clear()
        self.enclosure.display_manager.remove_active()
        self.gui[
            'sampleText'] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Egestas sed tempus urna et pharetra pharetra massa massa ultricies. Aliquam sem et tortor consequat id porta nibh. Amet est placerat in egestas erat imperdiet sed. Ut ornare lectus sit amet est placerat in egestas erat. Iaculis eu non diam phasellus vestibulum lorem sed risus ultricies. Hac habitasse platea dictumst vestibulum rhoncus est pellentesque. Vulputate eu scelerisque felis imperdiet proin fermentum. Neque convallis a cras semper auctor neque. Pharetra magna ac placerat vestibulum lectus mauris ultrices eros in. Phasellus faucibus scelerisque eleifend donec pretium vulputate. Malesuada bibendum arcu vitae elementum curabitur vitae nunc. Tellus id interdum velit laoreet id donec. Diam donec adipiscing tristique risus nec. Nisi lacus sed viverra tellus in hac habitasse platea. Amet venenatis urna cursus eget nunc scelerisque viverra mauris in. Sit amet nisl suscipit adipiscing bibendum est ultricies. Nec ultrices dui sapien eget mi proin sed. Egestas dui id ornare arcu odio ut sem nulla. Rhoncus aenean vel elit scelerisque. Neque gravida in fermentum et sollicitudin. Pellentesque massa placerat duis ultricies lacus sed. Nunc id cursus metus aliquam eleifend mi. Eu feugiat pretium nibh ipsum consequat nisl. Aenean euismod elementum nisi quis eleifend quam adipiscing vitae. Est ante in nibh mauris cursus mattis. Sagittis eu volutpat odio facilisis mauris sit amet. At consectetur lorem donec massa sapien faucibus. Odio facilisis mauris sit amet. Quis ipsum suspendisse ultrices gravida dictum fusce. Sagittis nisl rhoncus mattis rhoncus urna neque viverra justo nec. Eget mi proin sed libero enim sed faucibus. Interdum velit euismod in pellentesque massa. Et netus et malesuada fames. Velit aliquet sagittis id consectetur purus. Condimentum lacinia quis vel eros donec ac odio tempor orci. Amet consectetur adipiscing elit pellentesque habitant. Eleifend mi in nulla posuere sollicitudin aliquam ultrices sagittis orci. Nisi porta lorem mollis aliquam ut porttitor leo a diam. Egestas integer eget aliquet nibh praesent tristique. Velit scelerisque in dictum non. Id volutpat lacus laoreet non curabitur gravida arcu ac. Suspendisse interdum consectetur libero id faucibus nisl tincidunt eget. Ipsum a arcu cursus vitae congue mauris. Duis at consectetur lorem donec massa. Orci sagittis eu volutpat odio facilisis mauris. Eget mauris pharetra et ultrices neque ornare. Commodo nulla facilisi nullam vehicula ipsum a. Arcu risus quis varius quam quisque. Gravida in fermentum et sollicitudin. Lacus laoreet non curabitur gravida arcu ac tortor dignissim. Netus et malesuada fames ac turpis. Ipsum dolor sit amet consectetur adipiscing. Tellus elementum sagittis vitae et leo duis ut diam quam. Vitae et leo duis ut diam quam nulla. Risus pretium quam vulputate dignissim. Justo laoreet sit amet cursus sit amet dictum sit. Blandit libero volutpat sed cras. Lacus sed viverra tellus in. Ornare lectus sit amet est placerat in egestas erat. Tortor dignissim convallis aenean et tortor at. Tempus quam pellentesque nec nam aliquam. Nisi scelerisque eu ultrices vitae auctor eu augue ut lectus. Consequat id porta nibh venenatis cras sed felis eget. Massa enim nec dui nunc mattis enim ut. Dignissim enim sit amet venenatis urna. Ac tincidunt vitae semper quis lectus nulla at. Sed felis eget velit aliquet sagittis. Vel turpis nunc eget lorem dolor sed viverra. Non consectetur a erat nam at lectus. Iaculis eu non diam phasellus vestibulum. Dolor sit amet consectetur adipiscing elit ut aliquam purus sit. Libero justo laoreet sit amet cursus sit. Tellus pellentesque eu tincidunt tortor. Maecenas volutpat blandit aliquam etiam erat velit scelerisque in. Semper risus in hendrerit gravida rutrum quisque non tellus orci. Diam in arcu cursus euismod quis viverra nibh cras pulvinar. Habitasse platea dictumst quisque sagittis purus sit amet volutpat consequat. Elit ut aliquam purus sit."
        self.gui.show_page("paginationExample.qml")

    @intent_handler(
        IntentBuilder('handle_gui_example_slidingImage_intent').require(
            'gui.example.four'))
    def handle_gui_example_slidingImage_intent(self, message):
        """
        Example Intent Showcasing Sliding Image UI
        """
        self.gui.clear()
        self.enclosure.display_manager.remove_active()
        self.gui[
            'sampleImage'] = "https://source.unsplash.com/1920x1080/?+random"
        self.gui.show_page("slidingExample.qml")

    @intent_handler(
        IntentBuilder('handle_gui_example_proportionalDelegate_intent').
        require('gui.example.five'))
    def handle_gui_example_proportionalDelegate_intent(self, message):
        """
        Example Intent Showcasing Proportional Delegate and Autofit Label
        """
        self.gui.clear()
        self.enclosure.display_manager.remove_active()
        self.gui['sampleText'] = "Loading.."
        self.gui.show_page("proportionalDelegateExample.qml")

    @intent_handler(
        IntentBuilder('handle_gui_example_proportionalDelegateWrapText_intent'
                      ).require('gui.example.five.wrapText'))
    def handle_gui_example_proportionalDelegateWrapText_intent(self, message):
        """
        Example Intent Showcasing Proportional Delegate and Autofit Label
        """
        self.gui.clear()
        self.enclosure.display_manager.remove_active()
        self.gui['sampleText'] = "Incomprehensibilities"
        self.gui.show_page("proportionalDelegateWrapExample.qml")

    @intent_handler(
        IntentBuilder('handle_gui_example_listView_intent').require(
            'gui.example.six'))
    def handle_gui_example_listView_intent(self, message):
        """
        Example Intent Showcasing Advanced QML Skills with List and JSON Models
        """
        self.gui.clear()
        self.enclosure.display_manager.remove_active()
        sampleObject = {}
        sampleList = [{
            "text":
            "Praesent id leo felis",
            "image":
            "https://c1.staticflickr.com/8/7246/13792463963_817450e973_b.jpg"
        }, {
            "text":
            "Cras egestas tempus tempus",
            "image":
            "https://c1.staticflickr.com/8/7246/13792463963_817450e973_b.jpg"
        }, {
            "text":
            "Habitasse platea dictumst",
            "image":
            "https://c1.staticflickr.com/8/7246/13792463963_817450e973_b.jpg"
        }]
        sampleObject['lorem'] = sampleList
        self.gui['sampleBlob'] = sampleObject
        self.gui[
            'background'] = "https://source.unsplash.com/1920x1080/?+random"
        self.gui.show_page("listViewExample.qml")

    @intent_handler(
        IntentBuilder('handle_gui_example_events_intent').require(
            'gui.example.seven'))
    def handle_gui_example_events_intent(self, message):
        """
        Example Intent Showcasing Events Between Skill and Display
        """
        self.gui.clear()
        self.enclosure.display_manager.remove_active()
        self.gui.show_page("eventsExample.qml")

    def change_color_event(self, message):
        """
        Change Color Event
        """
        self.gui['fooColor'] = message.data['color']
        self.gui.show_page("eventsExample.qml")

    @intent_handler(
        IntentBuilder('handle_gui_example_audioDelegate_intent').require(
            'gui.example.eight'))
    def handle_gui_example_audioDelegate_intent(self, message):
        self.gui.clear()
        self.enclosure.display_manager.remove_active()
        self.gui[
            "audioSource"] = "https://www.free-stock-music.com/music/serge-narcissoff-background-theme.mp3"
        self.gui["audioTitle"] = "Background Theme by Serge Narcissoff "
        self.gui[
            "audioThumb"] = "https://www.free-stock-music.com/thumbnails/serge-narcissoff-background-theme.jpg"
        self.gui.show_page("audioPlayerExample.qml")

    @intent_handler(
        IntentBuilder('handle_gui_example_showHTMLUrl_intent').require(
            'gui.example.nine'))
    def handle_gui_example_showHTMLUrl_intent(self, message):
        self.gui.clear()
        self.enclosure.display_manager.remove_active()
        self.gui.show_url("https://mycroft.ai/", override_idle=True)

    @intent_handler(
        IntentBuilder('handle_gui_example_showHTMLRaw_intent').require(
            'gui.example.ten'))
    def handle_gui_example_showHTMLRaw_intent(self, message):
        self.gui.clear()
        self.enclosure.display_manager.remove_active()
        rawhtmlexample = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
<body>
<h1> HTML Example </h1>
<img src="apple.png" width=150px height=100px>
<p> This is an example of an HTML webpage. </p>
<p> <b>Tags</b> can be wrapped <i>inside other tags!</i> </p>

<p>
	HTML doesn't care about extra spaces, tabs or newlines,
	so we can use indentation and spacing to keep everything
	lined up nicely.
</p>

<ul>
	<li> This is how you create a bulleted list! </li>
	<li> Item 2 </li>
	<li> Item 3 </li>
</ul>
</body>
</html>
"""
        self.gui.show_html(rawhtmlexample,
                           resource_url=self.html_resources,
                           override_idle=True)

    @intent_handler(
        IntentBuilder('handle_gui_example_menu_intent').require(
            'gui.example.menu'))
    def handle_gui_example_menu_intent(self, message):
        """
        Build and Show Skill Example Menu To Run Test
        """
        self.gui.clear()
        self.enclosure.display_manager.remove_active()
        menuObject = {}
        menuList = [{
            "exampleIcon": "beamerblock",
            "exampleLabel": "Simple Text Example",
            "exampleEvent": "SkillGuiExample.simpleText"
        }, {
            "exampleIcon": "beamerblock",
            "exampleLabel": "Simple Image Example",
            "exampleEvent": "SkillGuiExample.simpleImage"
        }, {
            "exampleIcon": "beamerblock",
            "exampleLabel": "Paginated Text Example",
            "exampleEvent": "SkillGuiExample.paginatedText"
        }, {
            "exampleIcon": "beamerblock",
            "exampleLabel": "Sliding Image Example",
            "exampleEvent": "SkillGuiExample.slidingImage"
        }, {
            "exampleIcon": "beamerblock",
            "exampleLabel": "Proportion Delegate & Autofit Label",
            "exampleEvent": "SkillGuiExample.proportionalDelegate"
        }, {
            "exampleIcon":
            "beamerblock",
            "exampleLabel":
            "Proportion Delegate Word Wrap",
            "exampleEvent":
            "SkillGuiExample.proportionalDelegateWrapText"
        }, {
            "exampleIcon": "beamerblock",
            "exampleLabel": "Cards ListView",
            "exampleEvent": "SkillGuiExample.listView"
        }, {
            "exampleIcon": "beamerblock",
            "exampleLabel": "Events Example",
            "exampleEvent": "SkillGuiExample.eventsExample"
        }, {
            "exampleIcon": "beamerblock",
            "exampleLabel": "Audio Player Example",
            "exampleEvent": "SkillGuiExample.audioDelegateExample"
        }, {
            "exampleIcon": "beamerblock",
            "exampleLabel": "Html Url Example",
            "exampleEvent": "SkillGuiExample.htmlUrlExample"
        }, {
            "exampleIcon": "beamerblock",
            "exampleLabel": "Html Raw Example",
            "exampleEvent": "SkillGuiExample.htmlRawExample"
        }]
        menuObject['menuItems'] = menuList
        self.gui['menuBlob'] = menuObject
        self.gui.show_page("exampleMenu.qml")

    def stop(self):
        pass
Exemplo n.º 23
0
class YoutubeSkill(
        CommonPlaySkill
):  # info: https://mycroft.ai/documentation/skills/common-play-framework/
    def __init__(self):
        super(YoutubeSkill, self).__init__(name="YoutubeAudioAndVideo")
        self.process = None
        self.current_video_info = None

        self.past_videos = []
        self.next_videos = []

        self.is_playing = False
        """True if the current process is playing. If you want to know if something is playing, you should also make
        sure self.process is active. It's recommended to use self._get_process()"""
        self.is_auto_paused = False
        self.is_stopped = False
        """A bool that when True, stops automatic playing of the next song"""
        self.is_monitoring = False

        self.ignore_stop = False
        self.was_play_success = False

        # initialize settings values
        self.settings["show_video_by_default"] = True

    def _voc_match(self, utt_or_message, voc_filename, lang=None):
        if not isinstance(utt_or_message, str):
            return utt_or_message.data.get(voc_filename)
        utt_or_message = utt_or_message.lower()

        lang = lang or self.lang
        cache_key = lang + voc_filename
        self.voc_match(utt_or_message, voc_filename, lang)
        if utt_or_message:
            # Check for matches against complete words
            return next((i for i in self.voc_match_cache[cache_key]
                         if re.match(r'.*\b' + i + r'\b.*', utt_or_message)),
                        None)
        else:
            return None

    def start_monitor(self):
        if self.is_monitoring:
            return
        self.is_monitoring = True
        # useful: https://mycroft.ai/documentation/message-bus/
        self.add_event("recognizer_loop:record_begin", self.auto_pause_begin)
        self.add_event("recognizer_loop:record_end", self.auto_play_end)
        self.add_event("recognizer_loop:audio_output_start",
                       self.auto_pause_begin)
        self.add_event("recognizer_loop:audio_output_end", self.auto_play_end)

        self.add_event("mycroft.audio.service.play", self.play)
        self.add_event("mycroft.audio.service.resume", self.play)
        self.add_event(
            "mycroft.audio.service.pause",
            lambda: self.pause())  # in lambda so unwanted args aren't passed
        self.add_event("mycroft.audio.service.next",
                       lambda: self.next(say_no_videos=True))
        self.add_event("mycroft.audio.service.prev", lambda: self.previous())

        self.schedule_repeating_event(self.periodic_execute,
                                      datetime.datetime.now(),
                                      1,
                                      name="repeating_event")

    def stop_monitor(self):
        if not self.is_monitoring:
            return
        self.is_monitoring = False
        for event in [
                "recognizer_loop:record_begin", "recognizer_loop:record_end",
                "recognizer_loop:audio_output_start",
                "recognizer_loop:audio_output_end",
                "mycroft.audio.service.play", "mycroft.audio.service.resume",
                "mycroft.audio.service.pause", "mycroft.audio.service.next",
                "mycroft.audio.service.prev"
        ]:
            self.cancel_scheduled_event(event)

        self.cancel_scheduled_event("repeating_event")

    def auto_pause_begin(self):
        if self.is_playing:
            self.pause(auto_paused=True)

    def auto_play_end(self):
        if self.is_auto_paused:
            self.play()

    def play(self):
        if not self.is_playing:
            self.toggle_pause()
        self.is_auto_paused = False

    def pause(self, auto_paused=False):
        if self.is_playing:
            self.toggle_pause()

        self.is_auto_paused = auto_paused

    def _get_process(self):
        process = self.process
        if process and process.poll() is None:
            return process
        return None

    def _replace_process(self, process, video_info=None):
        """
        :param process: The new Process to replace the old one or None
        :return: True if there was a previous process, False otherwise
        """

        r = False
        if self.process:
            self.process.terminate()
            self.process.wait()
            r = True

        self.process = process
        self.current_video_info = video_info
        if process:
            self.start_monitor()
            self.is_playing = True
            self.is_stopped = False

        return r

    def toggle_pause(self):
        process = self._get_process()
        if process:
            toggle_pause(process)
            self.is_playing = not self.is_playing
            return True
        return False

    def skip_10_seconds(self, number_of_skips, forward=True):
        process = self._get_process()
        if process:
            skip_10_seconds(process, number_of_skips, forward=forward)
            self.is_playing = True
            return True
        return False

    def play_video(self, video_info):
        self._replace_process(create_player(
            video_info.path,
            show_video=video_info.show_video,
            start_fullscreen=video_info.start_fullscreen),
                              video_info=video_info)
        self.speak_dialog("playing")

    def next(self, say_no_videos=True):
        if self.next_videos:
            video_info = self.current_video_info
            if video_info:
                self.past_videos.append(video_info)
            next_video = self.next_videos.pop(0)
            self.play_video(next_video)
        elif say_no_videos:
            self.speak_dialog("no.next.videos")

    def previous(self):
        if self.past_videos:
            video_info = self.current_video_info
            if video_info:
                self.next_videos.insert(0, video_info)

            new_video = self.past_videos.pop(-1)
            self.play_video(new_video)
        else:
            self.speak_dialog("no.past.videos")

    def periodic_execute(self):
        if not self.is_stopped and not self._get_process():
            self.next(say_no_videos=False)

    def do_video_search_and_play(self, search, schedule_next, show_video,
                                 start_fullscreen):
        def success(path, info):
            video_info = VideoInfo(path, info, show_video, start_fullscreen)
            self.is_stopped = False
            if schedule_next:
                self.next_videos.append(video_info)
                self.speak_dialog("downloaded")
            else:
                current_video = self.current_video_info
                if current_video:
                    self.past_videos.append(current_video)
                self.play_video(video_info)

        def fail():
            self.speak_dialog("failed")
            self.enclosure.mouth_text("Failed to Download")
            self._replace_process(None)

        LOG.info("using: " + search)
        self.speak_dialog("downloading")
        self.enclosure.mouth_text("Downloading...")
        path_str = join(self.file_system.path, ".download-cache")
        download(search, success, fail, path_str=path_str)

    def get_query_data(self, utt_or_message):
        video_supported = bool(os.environ.get("DESKTOP_SESSION"))

        youtube_word = self._voc_match(utt_or_message, "Youtube")

        next_word = self._voc_match(utt_or_message, "Next")
        is_next = bool(next_word)

        without_video = self._voc_match(utt_or_message, "WithoutVideo")
        is_without_video = bool(without_video)

        with_video = self._voc_match(utt_or_message, "WithVideo")
        is_with_video = bool(with_video)

        start_fullscreen = self._voc_match(utt_or_message, "StartFullscreen")
        is_fullscreen = bool(start_fullscreen) and not without_video

        search = utt_or_message if isinstance(
            utt_or_message, str) else utt_or_message.data["utterance"]
        if youtube_word:
            search = search.replace(youtube_word, "")
        if is_next:
            search = search.replace(next_word, "")
        if is_without_video:
            search = search.replace(without_video, "")
            is_fullscreen = False
        elif is_with_video:
            search = search.replace(with_video, "")
        if is_fullscreen:
            search = search.replace(start_fullscreen, "")

        # search, schedule next, show video, start full screen
        return (search, is_next,
                ((not is_without_video and
                  self.settings.get("show_video_by_default")) or with_video)
                and video_supported, is_fullscreen and video_supported)

    def on_start(self, data):
        """
        :param data: The data from get_query_data()
        """
        self.is_stopped = False
        self.start_monitor()
        self.do_video_search_and_play(data[0], *data[1:])

    def CPS_match_query_phrase(self, phrase):
        self.ignore_stop = True  # self.stop() will be called because of a stop broadcast from CommonPlaySkill
        self.was_play_success = False
        data = self.get_query_data(phrase)
        if self.voc_match(phrase, "Youtube"):
            return phrase, CPSMatchLevel.MULTI_KEY, data
        else:
            return phrase, CPSMatchLevel.GENERIC, data

    def CPS_start(self, phrase, data):
        self.was_play_success = True
        self.on_start(data)

    @intent_handler(
        IntentBuilder("YoutubeIntent").require("Youtube").optionally(
            "WithoutVideo").optionally("WithVideo").optionally(
                "StartFullscreen").optionally("Next"))
    def handle_youtube(self, message):
        data = self.get_query_data(message)
        self.on_start(data)

    def handle_skip(self, message, forward):
        is_minute = bool(message.data.get("Minute"))
        number = extract_number(message.data["utterance"], self.lang)
        if is_minute:
            number *= 60

        if number <= 5:
            self.speak_dialog("must.choose.multiple.of.ten")
        else:
            amount = int(round(number / 10.0))
            if not self.skip_10_seconds(amount, forward=forward):
                self.speak_dialog("no.song.playing")

    @intent_handler(
        IntentBuilder("YoutubeSkipForwardIntent").require(
            "SkipForward").optionally("Second").optionally("Minute"))
    def handle_skip_forward(self, message):
        self.handle_skip(message, True)

    @intent_handler(
        IntentBuilder("YoutubeSkipBackwardIntent").require(
            "SkipBackward").optionally("Second").optionally("Minute"))
    def handle_skip_backward(self, message):
        self.handle_skip(message, False)

    @intent_handler(
        IntentBuilder("YoutubeVideoInfo").require("Youtube").require("Info"))
    def handle_video_info(self, message):
        current_video = self.current_video_info
        if not current_video:
            if self.is_playing and self._get_process():  # no info available
                self.speak_dialog("no.info.available")
            else:  # nothing playing
                self.speak_dialog("no.song.playing")
            return
        info = current_video.info
        self.speak_dialog("currently.playing", {
            "title": get_track(info),
            "artist": get_artist(info)
        })

    @intent_handler(
        IntentBuilder("YoutubeFullscreen").require("Youtube").require(
            "ToggleFullscreen"))
    def handle_toggle_fullscreen(self, message):
        process = self._get_process()
        if process:
            toggle_fullscreen(process)
        else:
            self.speak_dialog("no.song.playing")

    def stop(self, force_stop=False):
        if not isinstance(force_stop, bool):
            raise ValueError("force_stop is: {}".format(force_stop))

        if self.ignore_stop and not force_stop:
            self.ignore_stop = False
            if not self.was_play_success:
                LOG.info("scheduling event")
                self.schedule_event(
                    lambda: self.stop() if not self.was_play_success else None,
                    datetime.datetime.now() + datetime.timedelta(seconds=1))
            else:
                LOG.info("Not scheduling")
            return False
        self.was_play_success = False
        LOG.info("youtube skill received stop")
        self.is_stopped = True
        self.stop_monitor()
        return self._replace_process(None)

    def shutdown(self):
        super(YoutubeSkill, self).shutdown()
        self.stop(force_stop=True)
Exemplo n.º 24
0
import json
import socket
import sys
from xml.dom.minidom import parse
from adapt.intent import IntentBuilder
from adapt.engine import IntentDeterminationEngine

engine = IntentDeterminationEngine()

DOMTree = parse('config.xml')
for intent in DOMTree.getElementsByTagName("intent"):
    builder = IntentBuilder(intent.attributes["name"].value)
    for node in intent.getElementsByTagName("keyword"):
        keyword = node.attributes["name"].value
        required = node.attributes["required"].value
        ktype = node.attributes["type"].value
        if ktype == 'normal':
            for child in node.getElementsByTagName("item"):
                engine.register_entity(child.childNodes[0].nodeValue, keyword)
        else:
            engine.register_regex_entity(node.childNodes[0].nodeValue)
        if required == 'true':
            builder.require(keyword)
        else:
            builder.optionally(keyword)
    engine.register_intent_parser(builder.build())

# create and register weather vocabulary
'''weather_keyword = [
    "weather"
]
Exemplo n.º 25
0
class WebLauncher(MycroftSkill):
    def __init__(self):
        MycroftSkill.__init__(self)
        self.sites = self.translate_namedvalues("sites")

    def initialize(self):
        self.settings_change_callback = self.on_settings_changed
        self.on_settings_changed()
        if self.gui.connected:
            self.register_intent("CloseSite.intent", self.handle_close_site)

    def on_settings_changed(self):
        """Load all custom site settings and register names as vocab.

        If expanding number of custom url settings available remember to
        increment range stop.
        """
        for num in range(1, 6):
            name = self.settings.get("name_" + str(num))
            url = self.settings.get("url_" + str(num))
            if name and url:
                self.sites[name] = url
        self.register_sites_as_vocab()

    def register_sites_as_vocab(self):
        """Registers site names as vocab for Adapt intents."""
        for site_name in self.sites:
            self.register_vocabulary(site_name.lower(), "SiteName")

    @intent_handler(
        IntentBuilder("LaunchSite").require("SiteName").require("Launch"))
    def handle_launch_site(self, message):
        """Launch known site using xdg-open url.

        Primary intent handler for this Skill. Uses Adapt so that only
        utterances that include a known site are triggered. Prevents conflicts
        with other intents that also use vocabularly like "launch" or "open".
        """
        requested_site = message.data.get("SiteName")
        if requested_site in self.sites:
            site_url = self.sites[requested_site]
            if self.gui.connected:
                self.gui.show_url(site_url, override_idle=True)
            else:
                args = ["xdg-open", site_url]
                current_process = subprocess.Popen(args)
            self.bus.emit(
                Message(
                    "skill.weblauncher.opening",
                    {
                        "site_name": requested_site,
                        "site_url": site_url
                    },
                ))
        else:
            # This should never actually be triggered
            self.speak_dialog("not.found")

    def handle_close_site(self, message):
        """Close the website if opened on a Mycroft based GUI."""
        self.gui.release()
        self.acknowledge()
Exemplo n.º 26
0
 def build_intent_create(self):
     return IntentBuilder(
         self.name + 'CreateIntent').require(self.name + 'CreateVerb')
Exemplo n.º 27
0
class MeatThermometerSkill(MycroftSkill):

    # The constructor of the skill, which calls MycroftSkill's constructor
    def __init__(self):
        super(MeatThermometerSkill, self).__init__(name="MeatThermometerSkill")

    @intent_handler(
        IntentBuilder("").require("Temperature").require("Cook").require(
            "Meat").optionally("Modifier"))
    def handle_count_intent(self, message):
        meat = message.data["Meat"]
        if message.data.get("Modifier") is None:
            modifier = ""
        else:
            modifier = message.data.get("Modifier")

        if (meat == "turkey") or \
                (meat == "chicken") or \
                (meat == "poultry"):
            temperature = 165
            self.speak_dialog("cooking.temperature.is",
                              data={
                                  "temperature": temperature,
                                  "meat": meat,
                                  "modifier": modifier
                              })
            return

        elif (meat == "beef") or \
                (meat == "steak"):
            if modifier is "":
                temperature = 145
            elif modifier == "ground":
                temperature = 160
            elif modifier == "rare":
                temperature = 135
            elif modifier == "medium rare":
                temperature = 140
            elif modifier == "medium":
                temperature = 155
            elif modifier == "well done":
                temperature = 165
            self.speak_dialog("cooking.temperature.with.rest",
                              data={
                                  "temperature": temperature,
                                  "meat": meat,
                                  "modifier": modifier
                              })
            return

        elif (meat == "fish") or \
                (meat == "tuna") or \
                (meat == "salmon") or \
                (meat == "shell fish") or \
                (meat == "clams") or \
                (meat == "scallop") or \
                (meat == "crab"):
            temperature = 145
            self.speak_dialog("cooking.temperature.is",
                              data={
                                  "temperature": temperature,
                                  "meat": meat,
                                  "modifier": ""
                              })
            return

        else:
            self.speak_dialog("do.not.know.cooking.temperature",
                              data={"meat": meat})
            return
Exemplo n.º 28
0
 def build_intent_list(self):
     return IntentBuilder(
         self.name + 'ListIntent').require(self.name + 'ListVerb') \
         .optionally(self.name + 'Amount').require(self.name + 'Keyword')
Exemplo n.º 29
0
 def initialize(self):
     i = IntentBuilder('a').require('Keyword').build()
     self.register_intent(i, self.handler)
class MbtaBusTracking(MycroftSkill):
    def __init__(self):
        MycroftSkill.__init__(self)
        super(MbtaBusTracking, self).__init__(name="MbtaBusTracking")

        self.apiKey = None

        # using api key?
        self.useownkey = self.settings.get('useownkey')

        #   yes, read it from settings
        if self.useownkey:
            self.apiKey = self.settings.get('api_key')

        # create MBTA object to handle api calls
        self.t = MBTA(self.apiKey, self.settings.get('maxTrack', 3))

        self.routeName = None  # bus route
        self.requestTracking = False  # True => last request was for tracking, not arrivals
        self.directions = None  # direction name, terminus tuple for route
        self.stopName = None  # bus stop
        self.dirName = None  # direction of travel
        self.destName = None  # terminus for direction
        self.savedRoutes = dict()  # routes save to disk
        self.trackingInterval = max(30, (self.settings.get(
            'trackingUpateFreq', 30)))  # enforce min tracking updates

        # watch for changes on HOME
        self.settings.set_changed_callback(self.on_websettings_changed)

    def initialize(self):

        # try to read saved routes
        try:
            with self.file_system.open(ROUTE_FILE, 'rb') as f:
                self.savedRoutes = pickle.load(f)

            # make a vocabulary from saved routes
            if self.savedRoutes:
                for s in self.savedRoutes:
                    self.register_vocabulary(s, 'SavedRouteNames')

        except:
            pass

    # handle change of setting on home
    def on_websettings_changed(self):

        # using api key?
        self.useownkey = self.settings.get('useownkey')

        #   yes, read it from settings
        if self.useownkey:
            self.apiKey = self.settings.get('api_key')
            LOGGER.info('MBTA skill API key set to ' + self.apiKey)
        else:
            self.apiKey = None
            LOGGER.info('MBTA skill not use an API key')

        # update MBTA object with new settings
        self.t.updateSettings(self.apiKey, self.settings.get('maxTrack', 3))

        # get tracking interval
        self.trackingInterval = max(30, (self.settings.get(
            'trackingUpateFreq', 30)))  # enforce min tracking updates

    # speak list of passed arrival times
    def announceArrivals(self, eta):

        # get current datetime for east coast without timecode
        currentTime = datetime.datetime.now(
            (timezone('America/New_York'))).replace(tzinfo=None)

        # build datetime objets from strings in predection list
        # strip timezone first since we cannot count on fromisoformat() being available (3.7+)
        eta = [
            datetime.datetime.strptime(x[0:TZ_STR_IDX], '%Y-%m-%dT%H:%M:%S')
            for x in eta
        ]

        # calculate waiting times
        wt = [x - currentTime for x in eta if x > currentTime]

        arrivalCount = len(wt)

        # speak prefix if necessary
        if arrivalCount > 0:

            # add stop to prefix string
            prefix = {'stop': self.stopName}

            # might be updating arrivals while Mycroft is speaking, so wait
            wait_while_speaking()

            self.speak_dialog("Bus.Arrival.Prefix", prefix)

            for waitTime in wt:

                # wait for previous arrival announcement to finish
                wait_while_speaking()

                # calculate hour and minutes from datetime timedelta seconds
                arrival = {
                    'hour': waitTime.seconds // 3600,
                    'minutes': (waitTime.seconds % 3600) // 60
                }

                #  one hour or longer
                if (arrival['hour'] > 0):

                    # round down to one hour if only one minute over
                    if (arrival['minutes'] < 2):

                        self.speak_dialog("Arriving.Hour", arrival)

                    else:

                        # arrives in 1 hour and some minutes

                        self.speak_dialog("Arriving.Hour.Minutes", arrival)

                elif arrival['minutes'] > 1:

                    # arrives in more than one minute

                    self.speak_dialog("Arriving.Minutes", arrival)

                elif arrival['minutes'] == 1:

                    # arrives in one minute

                    self.speak_dialog("Arriving.Minute", arrival)

                else:

                    # arrives in less than a minute
                    self.speak_dialog("Arriving.Now")

            return arrivalCount

    # call to stop tracking
    def endTracking(self):

        # stop updates
        self.cancel_scheduled_event('BusTracker')

        # tell T object that we are no longer tracking
        self.t.stopTracking()

    # calllback for tracking updates
    def updateTracking(self):

        # get predictions
        eta = self.t.updateTracking()

        # if any arrivals predicted for our stop
        if eta != None:

            # speak times
            self.announceArrivals(eta)

        else:

            # last tracked bus has passed the stop, end updates
            self.endTracking()

    # call to start tracking arrivals
    # afer route, direction and stop have been set
    def startTracking(self):

        # get predictions
        eta = self.t.startTracking()

        # if any arrivals predicted for our stop
        if eta != None:

            # speak times
            self.announceArrivals(eta)

            # schedule updates
            self.schedule_repeating_event(self.updateTracking,
                                          None,
                                          self.trackingInterval,
                                          name='BusTracker')
        else:

            # no busses running
            stopInfo = {'name': self.stopName}
            self.speak_dialog("No.Busses.Found", stopInfo)

    # call when an arrival route, direction and
    # stop have been set
    def getArrivals(self):

        # ask API for arrival times
        eta = self.t.getArrivals()

        # begin arrival announcments
        announcement = {'route': self.routeName, 'dest': self.destName}
        self.speak_dialog("Service.Announcement", announcement)

        # speak arrival times if we got any
        if eta != None:
            self.announceArrivals(eta)
        else:
            stopInfo = {'name': self.stopName}
            self.speak_dialog("No.Busses.Found", stopInfo)

    # write route to file
    def writeRoutes(self):

        # open file in /home/pi/.mycroft/skills
        with self.file_system.open(ROUTE_FILE, 'wb') as f:

            # serialize dictionary
            pickle.dump(self.savedRoutes, f, pickle.HIGHEST_PROTOCOL)

    # save the current route as a shortcut
    def saveRoute(self, name):

        # add current route to saved routes dictionary
        self.savedRoutes[name] = self.t.getRouteSettings()

        # wrtie it to disk
        self.writeRoutes()

        # add to vocabulary
        self.register_vocabulary(name, 'SavedRouteNames')

    # remove saved route from file
    def removeRoute(self, name):

        # if route is in dict, remove and save
        if self.savedRoutes.pop(name, None) != None:
            self.writeRoutes()

    # try to restore route with passed name, return True if successful
    def restoreRoute(self, name):

        retVal = False

        # look up route associated with this name
        restoredRoute = self.savedRoutes.get(name)

        # if we got one
        if restoredRoute != None:

            # set up API class with copy of route object
            self.routeName = self.t.restoreRoute(copy.deepcopy(restoredRoute))

            # set class variables
            self.stopName = self.t.getStopName()
            self.dirName, self.destName = self.t.getDirDest()

            retVal = True

        return retVal

    # set proper route name based on utterance
    # and get directions for route
    def setRouteAndDirection(self, routeName):

        # Silver Line and Crosstown must be abreviated for API calls
        routeName = routeName.replace('crosstown ', 'CT')
        routeName = routeName.replace('silverline ', 'SL')

        # quirks fround in testing
        routeName = routeName.replace('to', '2')
        routeName = routeName.replace('for', '4')

        # convert any alpha characters to uppercase
        routeName = routeName.upper()

        # tell API which route we are riding
        self.routeName = self.t.setRoute(routeName)

        # read directions for this route
        if self.routeName:
            self.directions = self.t.getDirections()

        return self.routeName

    # prompt for direction and set context
    def setDirectionContext(self):

        # possible directions have already been set
        dirChoices = {
            'dir1': self.directions[0][0],
            'dest1': self.directions[0][1],
            'dir2': self.directions[1][0],
            'dest2': self.directions[1][1]
        }

        # prompt and set context
        self.speak_dialog('Which.Direction', dirChoices, expect_response=True)
        self.set_context('DirectionNeededContex')

    # prompt for direction and set context
    def setStopContext(self):

        self.speak_dialog('Which.Stop', expect_response=True)
        self.set_context('StopNeededContext')

    # remove all contexts we may have set
    def removeContexts(self):

        self.remove_context('RouteNeededContex')
        self.remove_context('DirectionNeededContex')
        self.remove_context('StopNeededContex')

    # process request for arrivals or tracking
    def processRequest(self, message, tracking):

        # may have set context in previous call
        self.removeContexts()

        # may already be tracking
        self.endTracking()

        # init class variables
        self.routeName = None
        self.dirName = None
        self.destName = None
        self.stopName = None
        self.requestTracking = tracking

        # get fields from utterance
        routeName = message.data.get("Route.Name", None)
        direction = message.data.get("Direction", None)
        stop = message.data.get("Stop", None)

        # set up for API call
        if routeName:

            # set route name
            routeName = self.setRouteAndDirection(routeName)

            # check for error
            if self.t.callError() is True:

                # server error
                self.speak_dialog("Error.Calling.Server")

                # clear route name
                routeName = None

        # only accept direction if we have a route
        if routeName and direction:

            # set direction from utterance
            self.dirName, self.destName = self.t.setDirection(direction)
            #print('Direction set to {}'.format(self.dirName))

        # only accept stop name if we have route and direction
        if routeName and direction and stop:

            self.stopName = self.t.setStop(stop)
            #print('Direction set to {} toward {} at {}'.format(self.dirName,self.destName,self.stopName))

        # if we got all the info needed,get arrivals
        if (self.routeName and self.dirName and self.stopName):

            # good to go - list arrivals or start tracking
            if self.requestTracking:
                self.startTracking()
            else:
                self.getArrivals()

        elif (self.routeName and self.dirName):

            # got route and direction, need stop
            self.setStopContext()
            #print('got route {} and direction {}'.format(self.routeName, self.dirName))

        elif self.routeName:

            # got route,need direction
            #print('got route {} '.format(self.routeName))

            # now we nedd a direction
            self.setDirectionContext()

        else:

            # if we are here it is because no route was in the
            # utterance, the route in the utterance was not valid
            # or there was an error calling the API
            #
            # set context to get a valid route as long as there was
            # not an error calling the API
            #
            if self.t.callError() is False:

                # need route name
                self.speak_dialog('Which.Route', expect_response=True)
                self.set_context('RouteNeededContex')

    # set route based on context
    @intent_handler(
        IntentBuilder('').require('RouteNeededContex').optionally(
            'Route').require('Route.Name').build())
    def handle_route_context_intent(self, message):

        # done with this context
        self.remove_context('RouteNeededContex')

        # pull route from message
        routeName = message.data.get("Route.Name", None)

        # set route name and direction
        routeName = self.setRouteAndDirection(routeName)

        # check for server error
        if self.t.callError() is True:

            # server error
            self.speak_dialog("Error.Calling.Server")

        else:

            # now we nedd a direction
            self.setDirectionContext()

    # set direction based on context
    @intent_handler(IntentBuilder('').require('DirectionNeededContex').build())
    def handle_direction_context_intent(self, message):

        # done with this context
        self.remove_context('DirectionNeededContex')

        # set direction from utterance
        self.dirName, self.destName = self.t.setDirection(
            message.data.get('utterance'))
        #print('Direction set to {}'.format(self.dirName))

        # now we need a stop
        self.setStopContext()

    # set stop based on context
    @intent_handler(IntentBuilder('').require('StopNeededContext').build())
    def handle_stop_context_intent(self, message):

        # done with this context
        self.remove_context('StopNeededContext')

        # set stop from utterance
        self.stopName = self.t.setStop(message.data.get('utterance'))

        # good to go - list arrivals or start tracking
        if self.requestTracking:
            self.startTracking()
        else:
            self.getArrivals()

    # arrivals for bus route
    @intent_handler(
        IntentBuilder('').require('T.Bus').require('Arrivals').optionally(
            'Route').optionally('Route.Name').optionally(
                'Direction').optionally('Stop').build())
    def handle_arrivals_intent(self, message):
        # process arrivals request
        self.processRequest(message, False)

    # tracking bus route
    @intent_handler(
        IntentBuilder('').require('T.Bus').require('Tracking').optionally(
            'Route').optionally('Route.Name').optionally(
                'Direction').optionally('Stop').build())
    def handle_tracking_intent(self, message):

        # process tracking request
        self.processRequest(message, True)

    # save shortcut
    @intent_handler(
        IntentBuilder('').require('Save').require('T.Bus').require(
            'Shortcut').build())
    def handle_save_route_intent(self, message):

        # need full route information to save
        if (self.routeName and self.dirName and self.stopName):

            # get name for shortcut
            shortcutName = self.get_response("Shortcut.Prompt")

            if shortcutName:

                # save route under passed name
                self.saveRoute(shortcutName)

                shortcutInfo = {'shortcut': shortcutName}
                self.speak_dialog("Save.Complete", shortcutInfo)

        else:

            # don't have complete info
            self.speak_dialog("Not.Enough.Info")

    # remove shortcut
    @intent_handler(
        IntentBuilder('').require('Remove').require('T.Bus').optionally(
            'Shortcut').require('SavedRouteNames').build())
    def handle_remove_route_intent(self, message):

        # extract short cut
        shortcutNmae = message.data.get("SavedRouteNames", None)

        # remove it
        self.removeRoute(shortcutNmae)

        shortcutInfo = {'shortcut': shortcutNmae}
        self.speak_dialog("Delete.Complete", shortcutInfo)

    # list shortcuts
    @intent_handler(
        IntentBuilder('').require('List').require('T.Bus').require(
            'Shortcuts').build())
    def handle_list_saved_route_intent(self, message):
        # build list of rotue names
        routeList = [s for s in self.savedRoutes]

        if len(routeList) > 0:

            # speak the names
            routes = {'routes': ' '.join(routeList)}
            self.speak_dialog('List.Saved', routes)

        else:

            self.speak_dialog('No.Saved.Routes')

    # tracking for a saved route
    @intent_handler(
        IntentBuilder('').require('T.Bus').require('Tracking').optionally(
            'Route').require('SavedRouteNames').build())
    def handle_saved_tracking_intent(self, message):

        # may already be tracking
        self.endTracking()

        # restore named route and start tracking
        routeName = message.data.get("SavedRouteNames", None)
        self.restoreRoute(routeName)
        self.startTracking()

    # arrivals for a saved route
    @intent_handler(
        IntentBuilder('').require('T.Bus').require('Arrivals').optionally(
            'Route').require('SavedRouteNames').build())
    def handle_saved_arrivals_intent(self, message):

        # pull shortcut name from intent
        shortCut = message.data.get("SavedRouteNames", None)
        # if shortcut has been deleted it will still be
        # in vocablulary until restart

        if self.savedRoutes.get(shortCut, None):

            # may already be tracking
            self.endTracking()

            # restore route and list arrivals
            self.restoreRoute(shortCut)
            self.getArrivals()

    # stop tracking
    @intent_handler(
        IntentBuilder('').require('T.Bus').require('Shutdown').build())
    def handle_shutdown_intent(self, message):

        # stop tracking
        self.endTracking()

        # reset contexts
        self.removeContexts()

        self.speak_dialog('Shutdown.Message')