def run_ansible_playbook_module(self, install_file_path): """ Run the install.yml file through an Ansible playbook using the dedicated neuron ! :param sudo_password: local machine sudo password required to install libraries :param install_file_path: the path of the Ansible playbook to run. :return: """ logger.debug("[ResourcesManager] Run ansible playbook") Utils.print_info("Starting neuron installation") # ask the sudo password if self.sudo_password is not None: pswd = self.sudo_password else: pswd = getpass.getpass('Sudo password:') if not pswd or pswd == "": Utils.print_warning("You must enter a sudo password") return False else: ansible_neuron_parameters = { "task_file": install_file_path, "sudo": True, "sudo_user": "******", "sudo_password": pswd } neuron = Neuron(name="ansible_playbook", parameters=ansible_neuron_parameters) NeuronLauncher.start_neuron(neuron) return True
def _get_target_folder(resources, module_type): """ Return the folder from the resources and given a module type :param resources: Resource object :type resources: Resources :param module_type: type of the module (TYPE_NEURON, TYPE_STT, TYPE_TTS, TYPE_TRIGGER, TYPE_SIGNAL) :return: path of the folder """ module_type_converter = dict() # dict to get the path behind a type of resource try: module_type_converter = { TYPE_NEURON: resources.neuron_folder, TYPE_STT: resources.stt_folder, TYPE_TTS: resources.tts_folder, TYPE_TRIGGER: resources.trigger_folder, TYPE_SIGNAL: resources.signal_folder } except AttributeError: # will be raised if the resource folder is not set in settings pass # Let's find the right path depending of the type try: folder_path = module_type_converter[module_type] except KeyError: folder_path = None # No folder_path has been found message = "No %s folder set in settings." % module_type if folder_path is None: logger.debug(message) Utils.print_danger(message) return folder_path
def _get_target_folder(resources, module_type): """ Return the folder from the resources and given a module type :param resources: Resource object :type resources: Resources :param module_type: type of the module :return: path of the folder """ # dict to get the path behind a type of resource module_type_converter = { TYPE_NEURON: resources.neuron_folder, TYPE_STT: resources.stt_folder, TYPE_TTS: resources.tts_folder, TYPE_TRIGGER: resources.trigger_folder } # Let's find the right path depending of the type try: folder_path = module_type_converter[module_type] except KeyError: folder_path = None # No folder_path has been found message = "No %s folder set in settings, cannot install." % module_type if folder_path is None: logger.debug(message) Utils.print_danger(message) return folder_path
def wait_before_stop(self): logger.debug("[Ambient_sounds] Wait %s minutes before checking if the thread is alive" % self.auto_stop_minutes) Utils.print_info("[Ambient_sounds] Wait %s minutes before stopping the ambient sound" % self.auto_stop_minutes) sleep(self.auto_stop_minutes*60) # *60 to convert received minutes into seconds logger.debug("[Ambient_sounds] Time is over, Stop player") Utils.print_info("[Ambient_sounds] Time is over, stopping the ambient sound") self.stop_last_process()
def signal_handler(signal, frame): """ Used to catch a keyboard signal like Ctrl+C in order to kill the kalliope program :param signal: signal handler :param frame: execution frame """ print "\n" Utils.print_info("Ctrl+C pressed. Killing Kalliope") sys.exit(0)
def load_stt_plugin(self): if self.stt is None: self.stt_module_name = self.settings.default_stt_name for stt_object in self.settings.stts: if stt_object.name == self.stt_module_name: stt_object.parameters["callback"] = self.callback Utils.get_dynamic_class_instantiation( 'stt', stt_object.name.capitalize(), parameters=stt_object.parameters)
def __init__(self, file_path=None): self.file_path = file_path if self.file_path is None: # we don't provide a file path, so search for the default one self.file_path = Utils.get_real_file_path(FILE_NAME) else: self.file_path = Utils.get_real_file_path(file_path) # if the returned file path is none, the file doesn't exist if self.file_path is None: raise BrainNotFound("brain file not found") self.yaml_config = self.get_yaml_config() self.brain = self.get_brain()
def set_mute_status(mute=False): """ Define is the mute status :param mute: Boolean. If false, Kalliope is voice is stopped """ logger.debug("[SettingEditor] mute. Switch trigger process to mute : %s" % mute) settings = SettingLoader().settings if mute: Utils.print_info("Kalliope now muted, voice has been stopped.") HookManager.on_mute() else: Utils.print_info("Kalliope now speaking.") HookManager.on_unmute() settings.options.mute = mute
def _clone_repo(path, git_url): """ Use git to clone locally the neuron in a temp folder :return: """ # clone the repo logger.debug("[ResourcesManager] GIT clone into folder: %s" % path) Utils.print_info("Cloning repository...") # if the folder already exist we remove it if os.path.exists(path): shutil.rmtree(path) else: os.makedirs(path) Repo.clone_from(git_url, path)
def __init__(self, file_path=None): sl = SettingLoader() self.settings = sl.settings self.file_path = file_path if self.file_path is None: # we don't provide a file path, so search for the default one self.file_path = Utils.get_real_file_path(FILE_NAME) else: self.file_path = Utils.get_real_file_path(file_path) # if the returned file path is none, the file doesn't exist if self.file_path is None: raise BrainNotFound("brain file not found") self.yaml_config = self.get_yaml_config() self.brain = self.load_brain()
def install(self): """ Module installation method. """ # first, we clone the repo self._clone_repo(path=self.tmp_path, git_url=self.git_url) # check the content of the cloned repo if self.is_repo_ok(dna_file_path=self.dna_file_path, install_file_path=self.install_file_path): # Load the dna.yml file self.dna = DnaLoader(self.dna_file_path).get_dna() if self.dna is not None: logger.debug("[ResourcesManager] DNA file content: " + str(self.dna)) if self.is_settings_ok(resources=self.settings.resources, dna=self.dna): # the dna file is ok, check the supported version if self._check_supported_version( current_version=self.settings.kalliope_version, supported_versions=self.dna. kalliope_supported_version): # Let's find the target folder depending the type module_type = self.dna.module_type.lower() target_folder = self._get_target_folder( resources=self.settings.resources, module_type=module_type) if target_folder is not None: # let's move the tmp folder in the right folder and get a new path for the module module_name = self.dna.name.lower() target_path = self._rename_temp_folder( name=self.dna.name.lower(), target_folder=target_folder, tmp_path=self.tmp_path) # if the target_path exists, then run the install file within the new repository if target_path is not None: self.install_file_path = target_path + os.sep + INSTALL_FILE_NAME self.run_ansible_playbook_module( install_file_path=self.install_file_path) Utils.print_success("Module: %s installed" % module_name) else: logger.debug( "[ResourcesManager] installation cancelled, deleting temp repo %s" % str(self.tmp_path)) shutil.rmtree(self.tmp_path)
def set_mute_status(mute=False): """ Define is the mute status :param mute: Boolean. If false, Kalliope is voice is stopped """ logger.debug( "[SettingEditor] mute. Switch trigger process to mute : %s" % mute) settings = SettingLoader().settings if mute: Utils.print_info("Kalliope now muted, voice has been stopped.") HookManager.on_mute() else: Utils.print_info("Kalliope now speaking.") HookManager.on_unmute() settings.options.mute = mute
def _replace_global_variables(cls, parameter, settings): """ replace a parameter that contains bracket by the instantiated parameter from the var file This function will call itself multiple time to handle different level of parameter in a neuron :param parameter: the parameter to update. can be a dict, a list or a string :param settings: the settings :return: the parameter dict """ if isinstance(parameter, dict): # print "parameter is dict %s" % str(parameter) for key, value in parameter.iteritems(): parameter[key] = cls._replace_global_variables(value, settings=settings) return parameter if isinstance(parameter, list): # print "parameter is list %s" % str(parameter) new_parameter_list = list() for el in parameter: new_parameter_list.append(cls._replace_global_variables(el, settings=settings)) return new_parameter_list if isinstance(parameter, str) or isinstance(parameter, unicode): # print "parameter is string %s" % parameter if Utils.is_containing_bracket(parameter): return cls._get_global_variable(sentence=parameter, settings=settings) return parameter
def _get_global_variable(sentence, settings): """ Get the global variable from the sentence with brackets :param sentence: the sentence to check :return: the global variable """ sentence_no_spaces = Utils.remove_spaces_in_brackets(sentence=sentence) list_of_bracket_params = Utils.find_all_matching_brackets(sentence=sentence_no_spaces) for param_with_bracket in list_of_bracket_params: param_no_brackets = param_with_bracket.replace("{{", "").replace("}}", "") if param_no_brackets in settings.variables: logger.debug("Replacing variable %s with %s" % (param_with_bracket, settings.variables[param_no_brackets])) sentence_no_spaces = sentence_no_spaces.replace(param_with_bracket, str(settings.variables[param_no_brackets])) return sentence_no_spaces
def _associate_order_params_to_values(cls, order, order_to_check): """ Associate the variables from the order to the incoming user order :param order_to_check: the order to check incoming from the brain :type order_to_check: str :param order: the order from user :type order: str :return: the dict corresponding to the key / value of the params """ logger.debug( "[NeuronParameterLoader._associate_order_params_to_values] user order: %s, " "order from synapse: %s" % (order, order_to_check)) list_word_in_order = Utils.remove_spaces_in_brackets( order_to_check).split() # get the order, defined by the first words before {{ # /!\ Could be empty if order starts with double brace the_order = order_to_check[:order_to_check.find('{{')] # remove sentence before order which are sentences not matching anyway # Manage Upper/Lower case truncate_user_sentence = order[order.lower().find(the_order.lower()):] truncate_list_word_said = truncate_user_sentence.split() # make dict var:value dict_var = dict() for idx, ow in enumerate(list_word_in_order): if Utils.is_containing_bracket(ow): # remove bracket and grab the next value / stop value var_name = ow.replace("{{", "").replace("}}", "") stop_value = Utils.get_next_value_list( list_word_in_order[idx:]) if stop_value is None: dict_var[var_name] = " ".join(truncate_list_word_said) break for word_said in truncate_list_word_said: if word_said.lower() == stop_value.lower( ): # Do not consider the case break if var_name in dict_var: dict_var[var_name] += " " + word_said truncate_list_word_said = truncate_list_word_said[1:] else: dict_var[var_name] = word_said truncate_list_word_said = truncate_list_word_said[1:] return dict_var
def _associate_order_params_to_values(cls, order, order_to_check): """ Associate the variables from the order to the incoming user order :param order_to_check: the order to check incoming from the brain :type order_to_check: str :param order: the order from user :type order: str :return: the dict corresponding to the key / value of the params """ logger.debug( "[NeuronParameterLoader._associate_order_params_to_values] user order: %s, " "order from synapse: %s" % (order, order_to_check)) list_word_in_order = Utils.remove_spaces_in_brackets( order_to_check).split() # remove sentence before order which are sentences not matching anyway truncate_list_word_said = order.split() # make dict var:value dict_var = dict() for idx, ow in enumerate(list_word_in_order): if not Utils.is_containing_bracket(ow): while truncate_list_word_said and ow.lower( ) != truncate_list_word_said[0].lower(): truncate_list_word_said = truncate_list_word_said[1:] else: # remove bracket and grab the next value / stop value var_name = ow.replace("{{", "").replace("}}", "") stop_value = Utils.get_next_value_list( list_word_in_order[idx:]) if stop_value is None: dict_var[var_name] = " ".join(truncate_list_word_said) break for word_said in truncate_list_word_said: if word_said.lower() == stop_value.lower( ): # Do not consider the case break if var_name in dict_var: dict_var[var_name] += " " + word_said truncate_list_word_said = truncate_list_word_said[1:] else: dict_var[var_name] = word_said truncate_list_word_said = truncate_list_word_said[1:] return dict_var
def _rename_temp_folder(name, target_folder, tmp_path): """ Rename the temp folder of the cloned repo Return the name of the path to install :return: path to install, None if already exists """ logger.debug("[ResourcesManager] Rename temp folder") new_absolute_neuron_path = target_folder + os.sep + name try: os.rename(tmp_path, new_absolute_neuron_path) return new_absolute_neuron_path except OSError: # the folder already exist Utils.print_warning("The module %s already exist in the path %s" % (name, target_folder)) # remove the cloned repo logger.debug("[ResourcesManager] Deleting temp folder %s" % str(tmp_path)) shutil.rmtree(tmp_path)
def set_deaf_status(trigger_instance, deaf=False): """ Define is the trigger is listening or not. :param trigger_instance: the trigger instance coming from the order. It will be paused or unpaused. :param deaf: Boolean. If true, kalliope is trigger is paused """ logger.debug("[MainController] deaf . Switch trigger process to deaf : %s" % deaf) settings = SettingLoader().settings if deaf: trigger_instance.pause() Utils.print_info("Kalliope now deaf, trigger has been paused") HookManager.on_deaf() else: trigger_instance.unpause() Utils.print_info("Kalliope now listening for trigger detection") HookManager.on_undeaf() settings.options.deaf = deaf
def _check_supported_version(current_version, supported_versions): """ The dna file contains supported Kalliope version for the module to install. Check if supported versions are match the current installed version. If not, ask the user to confirm the installation anyway :param current_version: current version installed of Kalliope. E.g 0.4.0 :param supported_versions: list of supported version :return: True if the version is supported or user has confirmed the installation """ logger.debug("[ResourcesManager] Current installed version of Kalliope: %s" % str(current_version)) logger.debug("[ResourcesManager] Module supported version: %s" % str(supported_versions)) supported_version_found = False for supported_version in supported_versions: if version.parse(current_version) == version.parse(supported_version): # we found the exact version supported_version_found = True break if not supported_version_found: # we ask the user if we want to install the module even if the version doesn't match Utils.print_info("Current installed version of Kalliope: %s" % current_version) Utils.print_info("Module supported versions: %s" % str(supported_versions)) Utils.print_warning("The neuron seems to be not supported by your current version of Kalliope") supported_version_found = Utils.query_yes_no("install it anyway?") logger.debug("[ResourcesManager] install it anyway user answer: %s" % supported_version_found) logger.debug("[ResourcesManager] check_supported_version: %s" % str(supported_version_found)) return supported_version_found
def start_neurone(cls, neuron): """ Start a neuron plugin :param neuron: neuron object :type neuron: Neuron :return: """ logger.debug("Run plugin \"%s\" with parameters %s" % (neuron.name, neuron.parameters)) return Utils.get_dynamic_class_instantiation("neurons", neuron.name.capitalize(), neuron.parameters)
def set_deaf_status(trigger_instance, deaf=False): """ Define is the trigger is listening or not. :param trigger_instance: the trigger instance coming from the order. It will be paused or unpaused. :param deaf: Boolean. If true, kalliope is trigger is paused """ logger.debug( "[MainController] deaf . Switch trigger process to deaf : %s" % deaf) settings = SettingLoader().settings if deaf: trigger_instance.pause() Utils.print_info("Kalliope now deaf, trigger has been paused") HookManager.on_deaf() else: trigger_instance.unpause() Utils.print_info("Kalliope now listening for trigger detection") HookManager.on_undeaf() settings.options.deaf = deaf
def run_ansible_playbook_module(install_file_path): """ Run the install.yml file through an Ansible playbook using the dedicated neuron ! :param install_file_path: the path of the Ansible playbook to run. :return: """ logger.debug("[ResourcesManager] Run ansible playbook") Utils.print_info("Starting neuron installation") # ask the sudo password pswd = getpass.getpass('Sudo password:') ansible_neuron_parameters = { "task_file": install_file_path, "sudo": True, "sudo_user": "******", "sudo_password": pswd } neuron = Neuron(name="ansible_playbook", parameters=ansible_neuron_parameters) NeuronLauncher.start_neuron(neuron)
def get_parameters(cls, synapse_order, user_order): """ Class method to get all params coming from a string order. Returns a dict of key/value. """ params = dict() if Utils.is_containing_bracket(synapse_order): params = cls._associate_order_params_to_values( user_order, synapse_order) logger.debug("Parameters for order: %s" % params) return params
def start(self): """ This method matches the incoming messages to the signals/order sentences provided in the Brain """ # create a dict of synapses that have been launched launched_synapses = self._get_matching_synapse_list( self.brain.synapses, self.order) if not launched_synapses: Utils.print_info("No synapse match the captured order: %s" % self.order) else: for synapse in launched_synapses: params = self._get_synapse_params(synapse, self.order) for neuron in synapse.neurons: self._start_neuron(neuron, params) # return the list of launched synapse return launched_synapses
def uninstall(self, neuron_name=None, tts_name=None, stt_name=None, trigger_name=None): """ Uninstall a community resource """ target_path_to_delete = None module_name = "" if neuron_name is not None: target_path_to_delete = self._get_target_folder(resources=self.settings.resources, module_type="neuron") module_name = neuron_name if tts_name is not None: target_path_to_delete = self._get_target_folder(resources=self.settings.resources, module_type="neuron") module_name = tts_name if stt_name is not None: target_path_to_delete = self._get_target_folder(resources=self.settings.resources, module_type="neuron") module_name = stt_name if trigger_name is not None: target_path_to_delete = self._get_target_folder(resources=self.settings.resources, module_type="neuron") module_name = trigger_name if target_path_to_delete is not None: try: shutil.rmtree(target_path_to_delete + os.sep + module_name.lower()) Utils.print_success("Module %s deleted" % module_name.lower()) except shutil.Error: Utils.print_warning("The module %s doest not exist in the path %s" % (module_name.lower(), target_path_to_delete)) except OSError: Utils.print_warning( "The module %s doest not exist in the path %s" % (module_name.lower(), target_path_to_delete))
def _get_matching_synapse_list(cls, all_synapses_list, order_to_match): """ Class method to return all the matching synapses with the order from the complete of synapses. :param all_synapses_list: the complete list of all synapses :param order_to_match: the order to match :type order_to_match: str :return: the list of matching synapses """ matching_synapses_list = list() for synapse in all_synapses_list: for signal in synapse.signals: if type(signal) == Order: if cls._spelt_order_match_brain_order_via_table( signal.sentence, order_to_match): matching_synapses_list.append(synapse) logger.debug("Order found! Run neurons: %s" % synapse.neurons) Utils.print_success( "Order matched in the brain. Running synapse \"%s\"" % synapse.name) return matching_synapses_list
def get_file_from_path(file_path): """ Trigger can be based on a model file, or other file. If a file is precised in settings, the path can be relative or absolute. If the path is absolute, there is no problem when can try to load it directly If the path is relative, we need to test the get the full path of the file in the following order: - from the current directory where kalliope has been called. Eg: /home/me/Documents/kalliope_config - from /etc/kalliope - from the root of the project. Eg: /usr/local/lib/python2.7/dist-packages/kalliope-version/kalliope/<file_path> :return: absolute path """ return Utils.get_real_file_path(file_path)
def serialize(self): """ This method allows to serialize in a proper way this object :return: A dict of name and parameters :rtype: Dict """ self.user_order = Utils.encode_text_utf8(self.user_order) return { 'user_order': self.user_order, 'matched_synapses': [e.serialize() for e in self.list_processed_matched_synapse], 'status': self.status }
def is_settings_ok(resources, dna): """ Test if required settings files in config of Kalliope are ok. The resource object must not be empty Check id the use have set the an installation path in his settings for the target module type :param resources: the Resources model :param dna: DNA info about the module to install :return: """ settings_ok = True if resources is None: message = "Resources folder not set in settings, cannot install." logger.debug(message) Utils.print_danger(message) settings_ok = False else: if dna.module_type == "neuron" and resources.neuron_folder is None: message = "Resources folder for neuron installation not set in settings, cannot install." logger.debug(message) Utils.print_danger(message) settings_ok = False if dna.module_type == "stt" and resources.stt_folder is None: message = "Resources folder for stt installation not set in settings, cannot install." logger.debug(message) Utils.print_danger(message) settings_ok = False if dna.module_type == "tts" and resources.tts_folder is None: message = "Resources folder for tts installation not set in settings, cannot install." logger.debug(message) Utils.print_danger(message) settings_ok = False if dna.module_type == "trigger" and resources.trigger_folder is None: message = "Resources folder for trigger installation not set in settings, cannot install." logger.debug(message) Utils.print_danger(message) settings_ok = False return settings_ok
def get_parameters(cls, synapse_order, user_order): """ Class method to get all params coming from a string order. Returns a dict of key/value. """ params = dict() if Utils.is_containing_bracket(synapse_order): params = cls._associate_order_params_to_values( user_order, synapse_order) logger.debug( "[NeuronParameterLoader.get_parameters]Parameters for order: %s" % params) # we place the dict of parameters load from order into a cache in Cortex so the user can save it later Cortex.add_parameters_from_order(params) return params
def say(self, message): """ USe TTS to speak out loud the Message. A message can be a string, a list or a dict If it's a string, simply use the TTS with the message If it's a list, we select randomly a string in the list and give it to the TTS If it's a dict, we use the template given in parameter to create a string that we give to the TTS :param message: Can be a String or a dict or a list .. raises:: TTSModuleNotFound """ logger.debug("NeuronModule Say() called with message: %s" % message) tts_message = None if isinstance(message, str) or isinstance(message, unicode): logger.debug("message is string") tts_message = message if isinstance(message, list): logger.debug("message is list") tts_message = random.choice(message) if isinstance(message, dict): logger.debug("message is dict") tts_message = self._get_message_from_dict(message) if tts_message is not None: logger.debug("tts_message to say: %s" % tts_message) # create a tts object from the tts the user want to user tts_object = next((x for x in self.settings.ttss if x.name == self.tts), None) if tts_object is None: raise TTSModuleNotFound("The tts module name %s does not exist in settings file" % self.tts) # change the cache settings with the one precised for the current neuron if self.override_cache is not None: tts_object.parameters = self._update_cache_var(self.override_cache, tts_object.parameters) logger.debug("NeuroneModule: TTS args: %s" % tts_object) # get the instance of the TTS module tts_module_instance = Utils.get_dynamic_class_instantiation("tts", tts_object.name.capitalize(), tts_object.parameters) # generate the audio file and play it tts_module_instance.say(tts_message)
def is_repo_ok(dna_file_path, install_file_path): """ Check if the git cloned repo is fine to be installed :return: True if repo is ok to be installed, False otherwise """ Utils.print_info("Checking repository...") repo_ok = True # check that a install.yml file is present if not os.path.exists(install_file_path): Utils.print_danger("Missing %s file" % INSTALL_FILE_NAME) repo_ok = False if not os.path.exists(dna_file_path): Utils.print_danger("Missing %s file" % DNA_FILE_NAME) repo_ok = False return repo_ok
def _start_neuron(cls, neuron, params): """ Associate params and Starts a neuron. :param neuron: the neuron to start :param params: the params to check and associate to the neuron args. """ problem_in_neuron_found = False if isinstance(neuron.parameters, dict): # print neuron.parameters if "args" in neuron.parameters: logger.debug("The neuron waits for parameter") # check that the user added parameters to his order if params is None: # we don't raise an error and break the program but we don't run the neuron problem_in_neuron_found = True Utils.print_danger( "Error: The neuron %s is waiting for argument. " "Argument found in bracket in the given order" % neuron.name) else: # we add wanted arguments the existing neuron parameter dict for arg in neuron.parameters["args"]: if arg in params: logger.debug( "Parameter %s added to the current parameter " "of the neuron: %s" % (arg, neuron.name)) neuron.parameters[arg] = params[arg] else: # we don't raise an error and break the program but # we don't run the neuron problem_in_neuron_found = True Utils.print_danger( "Error: Argument \"%s\" not found in the" " order" % arg) # if no error detected, we run the neuron if not problem_in_neuron_found: NeuroneLauncher.start_neurone(neuron) else: Utils.print_danger("A problem has been found in the Synapse.")