Exemple #1
0
    def execute(self, sentence, ident=None):
        """
            Convert sentence to speech, preprocessing out unsupported ssml

            The method caches results if possible using the hash of the
            sentence.

            Args:
                sentence:   Sentence to be spoken
                ident:      Id reference to current interaction
        """
        sentence = self.validate_ssml(sentence)

        create_signal("isSpeaking")
        if self.phonetic_spelling:
            for word in re.findall(r"[\w']+", sentence):
                if word.lower() in self.spellings:
                    sentence = sentence.replace(word,
                                                self.spellings[word.lower()])

        key = str(hashlib.md5(sentence.encode('utf-8', 'ignore')).hexdigest())
        wav_file = os.path.join(owo.util.get_cache_directory("tts"),
                                key + '.' + self.audio_ext)

        if os.path.exists(wav_file):
            LOG.debug("TTS cache hit")
            phonemes = self.load_phonemes(key)
        else:
            wav_file, phonemes = self.get_tts(sentence, wav_file)
            if phonemes:
                self.save_phonemes(key, phonemes)

        vis = self.visime(phonemes)
        self.queue.put((self.audio_ext, wav_file, vis, ident))
Exemple #2
0
    def schedule_repeating_event(self,
                                 handler,
                                 when,
                                 frequency,
                                 data=None,
                                 name=None):
        """
            Schedule a repeating event.

            Args:
                handler:                method to be called
                when (datetime):        time for calling the handler or None
                                        to initially trigger <frequency>
                                        seconds from now
                frequency (float/int):  time in seconds between calls
                data (dict, optional):  data to send along to the handler
                name (str, optional):   friendly name parameter
        """
        # Do not schedule if this event is already scheduled by the skill
        if name not in self.scheduled_repeats:
            data = data or {}
            if not when:
                when = datetime.now() + timedelta(seconds=frequency)
            self._schedule_event(handler, when, data, name, frequency)
        else:
            LOG.debug('The event is already scheduled, cancel previous '
                      'event if this scheduling should replace the last.')
Exemple #3
0
    def transcribe(self, audio):
        try:
            # Invoke the STT engine on the audio clip
            text = self.stt.execute(audio).lower().strip()
            LOG.debug("STT: " + text)
            return text
        except sr.RequestError as e:
            LOG.error("Could not request Speech Recognition {0}".format(e))
        except ConnectionError as e:
            LOG.error("Connection Error: {0}".format(e))

            self.emitter.emit("recognizer_loop:no_internet")
        except HTTPError as e:
            if e.response.status_code == 401:
                LOG.warning("Access Denied at owo.ai")
                return "pair my device"  # phrase to start the pairing process
            else:
                LOG.error(e.__class__.__name__ + ': ' + str(e))
        except RequestException as e:
            LOG.error(e.__class__.__name__ + ': ' + str(e))
        except Exception as e:
            self.emitter.emit('recognizer_loop:speech.recognition.unknown')
            if isinstance(e, IndexError):
                LOG.info('no words were transcribed')
            else:
                LOG.error(e)
            LOG.error("Speech Recognition could not understand audio")
            return None
        if connected():
            dialog_name = 'backend.down'
        else:
            dialog_name = 'not connected to the internet'
        self.emitter.emit('speak', {'utterance': dialog.get(dialog_name)})
Exemple #4
0
    def enable_intent(self, intent_name):
        """
        (Re)Enable a registered intent if it belongs to this skill

        Args:
                intent_name: name of the intent to be enabled

        Returns:
                bool: True if enabled, False if it wasn't registered
        """
        names = [intent[0] for intent in self.registered_intents]
        intents = [intent[1] for intent in self.registered_intents]
        if intent_name in names:
            intent = intents[names.index(intent_name)]
            self.registered_intents.remove((intent_name, intent))
            if ".intent" in intent_name:
                self.register_intent_file(intent_name, None)
            else:
                intent.name = intent_name
                self.register_intent(intent, None)
            LOG.debug('Enabling intent ' + intent_name)
            return True

        LOG.error('Could not enable ' + intent_name + ', it hasn\'t been '
                  'registered.')
        return False
Exemple #5
0
def get(phrase, lang=None, context=None):
    """
    Looks up a resource file for the given phrase.  If no file
    is found, the requested phrase is returned as the string.
    This will use the default language for translations.

    Args:
        phrase (str): resource phrase to retrieve/translate
        lang (str): the language to use
        context (dict): values to be inserted into the string

    Returns:
        str: a randomized and/or translated version of the phrase
    """

    if not lang:
        from owo.configuration import Configuration
        lang = Configuration.get().get("lang")

    filename = "text/" + lang.lower() + "/" + phrase + ".dialog"
    template = resolve_resource_file(filename)
    if not template:
        LOG.debug("Resource file not found: " + filename)
        return phrase

    stache = MustacheDialogRenderer()
    stache.load_template_file("template", template)
    if not context:
        context = {}
    return stache.render("template", context)
Exemple #6
0
 def load_vocab_files(self, root_directory):
     vocab_dir = join(root_directory, 'vocab', self.lang)
     if exists(vocab_dir):
         load_vocabulary(vocab_dir, self.bus, self.skill_id)
     elif exists(join(root_directory, 'locale', self.lang)):
         load_vocabulary(join(root_directory, 'locale', self.lang),
                         self.bus, self.skill_id)
     else:
         LOG.debug('No vocab loaded')
Exemple #7
0
 def init_dialog(self, root_directory):
     # If "<skill>/dialog/<lang>" exists, load from there.  Otherwise
     # load dialog from "<skill>/locale/<lang>"
     dialog_dir = join(root_directory, 'dialog', self.lang)
     if exists(dialog_dir):
         self.dialog_renderer = DialogLoader().load(dialog_dir)
     elif exists(join(root_directory, 'locale', self.lang)):
         locale_path = join(root_directory, 'locale', self.lang)
         self.dialog_renderer = DialogLoader().load(locale_path)
     else:
         LOG.debug('No dialog loaded')
Exemple #8
0
    def play(self):
        """ Start playback. """
        self.cast.quit_app()

        track = self.tracklist[0]
        # Report start of playback to audioservice
        if self._track_start_callback:
            self._track_start_callback(track)
        LOG.debug('track: {}, type: {}'.format(track, guess_type(track)))
        mime = guess_type(track)[0] or 'audio/mp3'
        self.cast.play_media(track, mime)
Exemple #9
0
 def run(self):
     self.start_async()
     while self.state.running:
         try:
             time.sleep(1)
             if self._config_hash != hash(str(Configuration().get())):
                 LOG.debug('Config has changed, reloading...')
                 self.reload()
         except KeyboardInterrupt as e:
             LOG.error(e)
             self.stop()
             raise  # Re-raise KeyboardInterrupt
Exemple #10
0
    def _save_uuid(self, uuid):
        """ Saves uuid.

            Args:
                uuid (str): uuid, unique id of new settingsmeta
        """
        LOG.debug("saving uuid {}".format(str(uuid)))
        directory = self.config.get("skills")["directory"]
        directory = join(directory, self.name)
        directory = expanduser(directory)
        uuid_file = join(directory, 'uuid')
        with open(uuid_file, 'w') as f:
            f.write(str(uuid))
Exemple #11
0
    def _save_hash(self, hashed_meta):
        """ Saves hashed_meta to settings directory.

            Args:
                hashed_meta (str): hash of new settingsmeta
        """
        LOG.debug("saving hash {}".format(hashed_meta))
        directory = self.config.get("skills")["directory"]
        directory = join(directory, self.name)
        directory = expanduser(directory)
        hash_file = join(directory, 'hash')
        with open(hash_file, 'w') as f:
            f.write(hashed_meta)
    def _register_object(self, message, object_name, register_func):
        file_name = message.data['file_name']
        name = message.data['name']

        LOG.debug('Registering Padatious ' + object_name + ': ' + name)

        if not isfile(file_name):
            LOG.warning('Could not find file ' + file_name)
            return

        register_func(name, file_name)
        self.train_time = get_time() + self.train_delay
        self.wait_and_train()
Exemple #13
0
 def _load_settings_meta(self):
     """ Loads settings metadata from skills path. """
     if isfile(self._meta_path):
         try:
             with open(self._meta_path) as f:
                 data = json.load(f)
             return data
         except Exception as e:
             LOG.error("Failed to load setting file: " + self._meta_path)
             LOG.error(repr(e))
             return None
     else:
         LOG.debug("settingemeta.json does not exist")
         return None
Exemple #14
0
    def _restore_volume(self, message):
        """
            Is triggered when OwO is done speaking and restores the volume

            Args:
                message: message bus message, not used but required
        """
        if self.current:
            LOG.debug('restoring volume')
            self.volume_is_low = False
            time.sleep(2)
            if not self.volume_is_low:
                self.current.restore_volume()
        if self.pulse_restore:
            self.pulse_restore()
Exemple #15
0
    def initialize_remote_settings(self):
        """ initializes the remote settings to the server """
        # if settingsmeta.json exists (and is valid)
        # this block of code is a control flow for
        # different scenarios that may arises with settingsmeta
        self.load_skill_settings_from_file()  # loads existing settings.json
        settings_meta = self._load_settings_meta()
        if not settings_meta:
            return

        if not is_paired():
            return

        self._device_identity = self.api.identity.uuid
        self._api_path = "/" + self._device_identity + "/skill"
        self._user_identity = self.api.get()['user']['uuid']
        LOG.debug("settingsmeta.json exist for {}".format(self.name))
        hashed_meta = self._get_meta_hash(settings_meta)
        skill_settings = self._request_other_settings(hashed_meta)
        # if hash is new then there is a diff version of settingsmeta
        if self._is_new_hash(hashed_meta):
            # first look at all other devices on user account to see
            # if the settings exist. if it does then sync with device
            if skill_settings:
                # not_owner flags that this settings is loaded from
                # another device. If a skill settings doesn't have
                # not_owner, then the skill is created from that device
                self['not_owner'] = True
                self.save_skill_settings(skill_settings)
            else:  # upload skill settings if
                uuid = self._load_uuid()
                if uuid is not None:
                    self._delete_metadata(uuid)
                self._upload_meta(settings_meta, hashed_meta)
        else:  # hash is not new
            if skill_settings is not None:
                self['not_owner'] = True
                self.save_skill_settings(skill_settings)
            else:
                settings = self._request_my_settings(hashed_meta)
                if settings is None:
                    LOG.debug("seems like it got deleted from home... "
                              "sending settingsmeta.json for "
                              "{}".format(self.name))
                    self._upload_meta(settings_meta, hashed_meta)
                else:
                    self.save_skill_settings(settings)
        self._complete_intialization = True
Exemple #16
0
    def _lower_volume(self, message=None):
        """
            Is triggered when OwO starts to speak and reduces the volume.

            Args:
                message: message bus message, not used but required
        """
        if self.current:
            LOG.debug('lowering volume')
            self.current.lower_volume()
            self.volume_is_low = True
        try:
            if self.pulse_quiet:
                self.pulse_quiet()
        except Exception as exc:
            LOG.error(exc)
Exemple #17
0
    def _stop(self, message=None):
        """
            Handler for owo.stop. Stops any playing service.

            Args:
                message: message bus message, not used but required
        """
        LOG.debug('stopping all playing services')
        with self.service_lock:
            if self.current:
                name = self.current.name
                if self.current.stop():
                    self.bus.emit(
                        Message("owo.stop.handled", {"by": "audio:" + name}))

                self.current = None
Exemple #18
0
    def _delete_metadata(self, uuid):
        """ Deletes the current skill metadata

            Args:
                uuid (str): unique id of the skill
        """
        try:
            LOG.debug("deleting metadata")
            self.api.request({
                "method": "DELETE",
                "path": self._api_path + "/{}".format(uuid)
            })
        except Exception as e:
            LOG.error(e)
            LOG.error("cannot delete metadata because this"
                      "device is not original uploader of skill")
Exemple #19
0
    def on_message(self, message):
        LOG.debug(message)
        try:
            deserialized_message = Message.deserialize(message)
        except:
            return

        try:
            self.emitter.emit(deserialized_message.type, deserialized_message)
        except Exception as e:
            LOG.exception(e)
            traceback.print_exc(file=sys.stdout)
            pass

        for client in client_connections:
            client.write_message(message)
Exemple #20
0
    def load_phonemes(self, key):
        """
            Load phonemes from cache file.

            Args:
                Key:    Key identifying phoneme cache
        """
        pho_file = os.path.join(owo.util.get_cache_directory("tts"),
                                key + ".pho")
        if os.path.exists(pho_file):
            try:
                with open(pho_file, "r") as cachefile:
                    phonemes = cachefile.read().strip()
                return phonemes
            except:
                LOG.debug("Failed to read .PHO from cache")
        return None
    def handle_fallback(self, message):
        if not self.finished_training_event.is_set():
            LOG.debug('Waiting for Padatious training to finish...')
            return False

        utt = message.data.get('utterance')
        LOG.debug("Padatious fallback attempt: " + utt)
        data = self.calc_intent(utt)
        if data.conf < 0.5:
            return False

        data.matches['utterance'] = utt

        self.service.add_active_skill(data.name.split(':')[0])

        self.bus.emit(message.reply(data.name, data=data.matches))
        return True
Exemple #22
0
    def _upload_meta(self, settings_meta, hashed_meta):
        """ uploads the new meta data to settings with settings migration

            Args:
                settings_meta (dict): settingsmeta.json
                hashed_meta (str): {skill-folder}-settinsmeta.json
        """
        LOG.debug("sending settingsmeta.json for {}".format(self.name) +
                  " to servers")
        meta = self._migrate_settings(settings_meta)
        meta['identifier'] = str(hashed_meta)
        response = self._send_settings_meta(meta)
        if response and 'uuid' in response:
            self._save_uuid(response['uuid'])
            if 'not_owner' in self:
                del self['not_owner']
        self._save_hash(hashed_meta)
Exemple #23
0
    def flush(self):
        publisher = MetricsPublisher()
        payload = {
            'counters': self._counters,
            'timers': self._timers,
            'levels': self._levels,
            'attributes': self._attributes
        }
        self.clear()
        count = (len(payload['counters']) + len(payload['timers']) +
                 len(payload['levels']))
        if count > 0:
            LOG.debug(json.dumps(payload))

            def publish():
                publisher.publish(payload)

            threading.Thread(target=publish).start()
Exemple #24
0
    def _unload_removed(self, paths):
        """ Shutdown removed skills.

            Arguments:
                paths: list of current directories in the skills folder
        """
        paths = [p.rstrip('/') for p in paths]
        skills = self.loaded_skills
        # Find loaded skills that doesn't exist on disk
        removed_skills = [str(s) for s in skills.keys() if str(s) not in paths]
        for s in removed_skills:
            LOG.info('removing {}'.format(s))
            try:
                LOG.debug('Removing: {}'.format(skills[s]))
                skills[s]['instance'].default_shutdown()
            except Exception as e:
                LOG.exception(e)
            self.loaded_skills.pop(s)
Exemple #25
0
    def _skip_wake_word(self):
        # Check if told programatically to skip the wake word, like
        # when we are in a dialog with the user.
        if check_for_signal('startListening'):
            return True

        # Pressing the Mark 1 button can start recording (unless
        # it is being used to mean 'stop' instead)
        if check_for_signal('buttonPress', 1):
            # give other processes time to consume this signal if
            # it was meant to be a 'stop'
            sleep(0.25)
            if check_for_signal('buttonPress'):
                # Signal is still here, assume it was intended to
                # begin recording
                LOG.debug("Button Pressed, wakeword not needed")
                return True

        return False
Exemple #26
0
    def _connect(self, message):
        """
            Callback method to connect to mopidy if server is not available
            at startup.
        """
        url = 'http://localhost:6680'
        if self.config is not None:
            url = self.config.get('url', url)
        try:
            self.mopidy = Mopidy(url)
        except:
            if self.connection_attempts < 1:
                LOG.debug('Could not connect to server, will retry quietly')
            self.connection_attempts += 1
            time.sleep(10)
            self.bus.emit(Message('MopidyServiceConnect'))
            return

        LOG.info('Connected to mopidy server')
Exemple #27
0
def download_subscriber_voices(selected_voice):
    """
        Function to download all premium voices, starting with
        the currently selected if applicable
    """

    def make_executable(dest):
        """ Call back function to make the downloaded file executable. """
        LOG.info('Make executable')
        # make executable
        st = os.stat(dest)
        os.chmod(dest, st.st_mode | stat.S_IEXEC)

    # First download the selected voice if needed
    voice_file = SUBSCRIBER_VOICES.get(selected_voice)
    if voice_file is not None and not exists(voice_file):
        LOG.info('voice doesn\'t exist, downloading')
        url = DeviceApi().get_subscriber_voice_url(selected_voice)
        # Check we got an url
        if url:
            dl = download(url, voice_file, make_executable)
            # Wait for completion
            while not dl.done:
                sleep(1)
        else:
            LOG.debug('{} is not available for this architecture'
                      .format(selected_voice))

    # Download the rest of the subsciber voices as needed
    for voice in SUBSCRIBER_VOICES:
        voice_file = SUBSCRIBER_VOICES[voice]
        if not exists(voice_file):
            url = DeviceApi().get_subscriber_voice_url(voice)
            # Check we got an url
            if url:
                dl = download(url, voice_file, make_executable)
                # Wait for completion
                while not dl.done:
                    sleep(1)
            else:
                LOG.debug('{} is not available for this architecture'
                          .format(voice))
Exemple #28
0
    def store(self, force=False):
        """ Store dictionary to file if a change has occured.

            Args:
                force:  Force write despite no change
        """

        if force or not self._is_stored:
            with open(self._settings_path, 'w') as f:
                json.dump(self, f)
            self.loaded_hash = hash(json.dumps(self, sort_keys=True))

        if self._should_upload_from_change:
            settings_meta = self._load_settings_meta()
            hashed_meta = self._get_meta_hash(settings_meta)
            uuid = self._load_uuid()
            if uuid is not None:
                LOG.debug("deleting meta data for {}".format(self.name))
                self._delete_metadata(uuid)
            self._upload_meta(settings_meta, hashed_meta)
Exemple #29
0
    def disable_intent(self, intent_name):
        """
        Disable a registered intent if it belongs to this skill

        Args:
            intent_name (string): name of the intent to be disabled

        Returns:
                bool: True if disabled, False if it wasn't registered
        """
        names = [intent_tuple[0] for intent_tuple in self.registered_intents]
        if intent_name in names:
            LOG.debug('Disabling intent ' + intent_name)
            name = str(self.skill_id) + ':' + intent_name
            self.bus.emit(Message("detach_intent", {"intent_name": name}))
            return True

        LOG.error('Could not disable ' + intent_name +
                  ', it hasn\'t been registered.')
        return False
Exemple #30
0
    def _request_other_settings(self, identifier):
        """ Retrieves user skill from other devices by identifier (hashed_meta)

        Args:
            identifier (str): identifier for this skill

        Returns:
            settings (dict or None): returns the settings if true else None
        """
        LOG.debug("syncing settings with other devices "
                  "from server for {}".format(self.name))

        path = \
            "/" + self._device_identity + "/userSkill?identifier=" + identifier
        user_skill = self.api.request({"method": "GET", "path": path})
        if len(user_skill) == 0:
            return None
        else:
            settings = self._type_cast(user_skill[0], to_platform='core')
            return settings