def __init__(self, **kwargs): """ Class used by neuron for talking :param kwargs: Same parameter as the Child. Can contain info about the tts to use instead of the default one """ # get the child who called the class child_name = self.__class__.__name__ self.neuron_name = child_name sl = SettingLoader() self.settings = sl.settings brain_loader = BrainLoader() self.brain = brain_loader.brain self.tts = self._get_tts_object(settings=self.settings) # get templates if provided # Check if there is a template associate to the output message self.say_template = kwargs.get('say_template', None) # check if there is a template file associate to the output message self.file_template = kwargs.get('file_template', None) # keep the generated message self.tts_message = None # if the current call is api one self.is_api_call = kwargs.get('is_api_call', False) # boolean to know id the synapse is waiting for an answer self.is_waiting_for_answer = False # the synapse name to add the the buffer self.pending_synapse = None # a dict of parameters the user ask to save in short term memory self.kalliope_memory = kwargs.get('kalliope_memory', None) # parameters loaded from the order can be save now Cortex.save_parameter_from_order_in_memory(self.kalliope_memory)
def start_new_process(self, sound_arg): """ Start mplayer process with the given sounds to play :param sound_arg: :type sound_arg: list of dicts [{name: link}, {name: link}, {name: link}] :return: """ mplayer_exec_path = [self.mplayer_path] mplayer_options = ['-slave', '-quiet', '-af', 'volume=-15', '-loop'] mplayer_options.append("0" if self.loop_option == "loop" else "1") mplayer_command = list() mplayer_command.extend(mplayer_exec_path) mplayer_command.extend(mplayer_options) for sound in sound_arg: for sound_name, sound_link in sound.items(): mplayer_command.append(sound_link) logger.debug("[Background_sound_player] Mplayer cmd: %s" % str(mplayer_command)) Cortex.save("current_playing_background_sound", sound_name) # give the current file name played to the neuron template self.message['sound_name'] = sound_name self.message["sound_link"] = sound_link # run mplayer in background inside a new process fnull = open(os.devnull, 'w') pid = subprocess.Popen(mplayer_command, stdout=fnull, stderr=fnull).pid # store the pid in a file to be killed later self.store_pid(pid) logger.debug("[Background_sound_player] Mplayer started, pid: %s" % pid)
def test_save_parameter_from_order_in_memory(self): # Test with a value that exist in the temp memory order_parameters = { "key1": "value1", "key2": "value2" } Cortex.temp = order_parameters dict_val_to_save = {"my_key_in_memory": "{{key1}}"} expected_dict = {"my_key_in_memory": "value1"} Cortex.save_parameter_from_order_in_memory(dict_val_to_save) self.assertDictEqual(expected_dict, Cortex.memory) # test with a value that does not exsit order_parameters = { "key1": "value1", "key2": "value2" } Cortex.temp = order_parameters dict_val_to_save = {"my_key_in_memory": "{{key3}}"} self.assertFalse(Cortex.save_parameter_from_order_in_memory(dict_val_to_save))
def test_save_parameter_from_order_in_memory(self): # Test with a value that exist in the temp memory order_parameters = {"key1": "value1", "key2": "value2"} Cortex.temp = order_parameters dict_val_to_save = {"my_key_in_memory": "{{key1}}"} expected_dict = {"my_key_in_memory": "value1"} Cortex.save_parameter_from_order_in_memory(dict_val_to_save) self.assertDictEqual(expected_dict, Cortex.memory) # test with a value that does not exist order_parameters = {"key1": "value1", "key2": "value2"} Cortex.temp = order_parameters dict_val_to_save = {"my_key_in_memory": "{{key3}}"} self.assertFalse( Cortex.save_parameter_from_order_in_memory(dict_val_to_save)) # save a value with no brackets dict_val_to_save = {"my_key_in_memory": "my value"} expected_dict = {"my_key_in_memory": "my value"} self.assertTrue( Cortex.save_parameter_from_order_in_memory(dict_val_to_save)) self.assertDictEqual(expected_dict, Cortex.memory)
def __init__(self, **kwargs): super(Editor, self).__init__(**kwargs) # the args from the neuron configuration listen_ip = kwargs.get('listen_ip', '0.0.0.0') port = kwargs.get('port', 8000) ignore_pattern = kwargs.get('ignore_pattern', None) dir_first = kwargs.get('dir_first', False) hide_hidden = kwargs.get('hide_hidden', False) page_title = kwargs.get('page_title', "Kalliope Editor") stop_server = kwargs.get('stop_server', False) if stop_server: self.stop_http_server() Utils.print_info("[ Editor ] Editor stopped") else: global IGNORE_PATTERN, DIRSFIRST, HIDEHIDDEN, PAGE_TITLE IGNORE_PATTERN = ignore_pattern DIRSFIRST = dir_first HIDEHIDDEN = hide_hidden PAGE_TITLE = page_title if self.stop_http_server(): server = EditorThread(listen_ip, int(port)) server.daemon = True server.start() Cortex.save('EditorServerThread', server)
def test_add_parameters_from_order(self): order_parameters = {"key1": "value1", "key2": "value2"} expected_temp_dict = {"key1": "value1", "key2": "value2"} Cortex.add_parameters_from_order(order_parameters) self.assertDictEqual(Cortex.temp, expected_temp_dict)
def test_save(self): key_to_save = "key1" value_to_save = "value1" expected_memory = {"key1": "value1"} Cortex.save(key=key_to_save, value=value_to_save) self.assertDictEqual(expected_memory, Cortex.memory)
def start_synapse_by_list_name(cls, list_name, brain=None, overriding_parameter_dict=None, new_lifo=False): """ Start synapses by their name :param list_name: List of name of the synapse to launch :param brain: Brain instance :param overriding_parameter_dict: parameter to pass to neurons :param new_lifo: If True, ask the LifoManager to return a new lifo and not the singleton """ logger.debug( "[SynapseLauncher] start_synapse_by_list_name called with synapse list: %s " % list_name) if list_name: if brain is None: brain = BrainLoader().brain if overriding_parameter_dict: # this dict is used by signals to pass parameter to neuron, # save in temp memory in case the user want to save in kalliope memory Cortex.add_parameters_from_order(overriding_parameter_dict) # get all synapse object list_synapse_object_to_start = list() for name in list_name: synapse_to_start = brain.get_synapse_by_name(synapse_name=name) if not synapse_to_start: raise SynapseNameNotFound( "[SynapseLauncher] The synapse name \"%s\" does not exist " "in the brain file" % name) if synapse_to_start.enabled: list_synapse_object_to_start.append(synapse_to_start) else: logger.debug( "[SynapseLauncher] Synapse not activated: %s " % synapse_to_start) # run the LIFO with all synapse if new_lifo: lifo_buffer = LifoManager.get_new_lifo() else: lifo_buffer = LifoManager.get_singleton_lifo() list_synapse_to_process = list() for synapse in list_synapse_object_to_start: if synapse is not None: new_matching_synapse = MatchedSynapse( matched_synapse=synapse, matched_order=None, user_order=None, overriding_parameter=overriding_parameter_dict) list_synapse_to_process.append(new_matching_synapse) lifo_buffer.add_synapse_list_to_lifo(list_synapse_to_process) return lifo_buffer.execute(is_api_call=True) return None
def test_clean_parameter_from_order(self): Cortex.temp = { "key1": "value1", "key2": "value2" } Cortex.clean_parameter_from_order() expected_temp_dict = dict() self.assertDictEqual(expected_temp_dict, Cortex.memory)
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 # we can save parameters from the neuron in memory Cortex.save_neuron_parameter_in_memory(self.kalliope_memory, message) if isinstance(message, str) or isinstance(message, six.text_type): logger.debug("[NeuronModule] message is string") tts_message = message if isinstance(message, list): logger.debug("[NeuronModule] message is list") tts_message = random.choice(message) if isinstance(message, dict): logger.debug("[NeuronModule] message is dict") tts_message = self._get_message_from_dict(message) if tts_message is not None: logger.debug("[NeuronModule] tts_message to say: %s" % tts_message) self.tts_message = tts_message Utils.print_success(tts_message) # save in kalliope memory the last tts message Cortex.save("kalliope_last_tts_message", tts_message) # process the audio only if the mute flag is false if self.settings.options.mute: logger.debug("[NeuronModule] mute is True, Kalliope is muted") else: logger.debug( "[NeuronModule] mute is False, make Kalliope speaking") HookManager.on_start_speaking() # get the instance of the TTS module tts_folder = None if self.settings.resources: tts_folder = self.settings.resources.tts_folder tts_module_instance = Utils.get_dynamic_class_instantiation( package_name="tts", module_name=self.tts.name, parameters=self.tts.parameters, resources_dir=tts_folder) # generate the audio file and play it tts_module_instance.say(tts_message) HookManager.on_stop_speaking()
def run_synapse_by_order(self): """ Give an order to Kalliope via API like it was from a spoken one Test with curl curl -i --user admin:secret -H "Content-Type: application/json" -X POST \ -d '{"order":"my order"}' http://localhost:5000/synapses/start/order In case of quotes in the order or accents, use a file cat post.json: {"order":"j'aime"} curl -i --user admin:secret -H "Content-Type: application/json" -X POST \ --data @post.json http://localhost:5000/order/ Can be used with mute flag curl -i --user admin:secret -H "Content-Type: application/json" -X POST \ -d '{"order":"my order", "mute":"true"}' http://localhost:5000/synapses/start/order :return: """ if not request.get_json() or 'order' not in request.get_json(): data = { "Error": "Wrong parameters, 'order' not set" } return jsonify(error=data), 400 order = request.get_json('order') # Store the mute value, then apply depending of the request parameters old_mute_value = self.settings.options.mute mute = utils.get_value_flag_from_request(http_request=request, flag_to_find="mute", is_boolean=True) if mute is not None: SettingEditor.set_mute_status(mute=mute) if order is not None: # get the order order_to_run = order["order"] logger.debug("[FlaskAPI] run_synapse_by_order: order to run -> %s" % order_to_run) api_response = SynapseLauncher.run_matching_synapse_from_order(order_to_run, self.brain, self.settings, is_api_call=True) Cortex.save('kalliope_last_order', order_to_run) data = jsonify(api_response) if mute is not None: SettingEditor.set_mute_status(mute=old_mute_value) return data, 201 else: data = { "error": "order cannot be null" } if mute is not None: SettingEditor.set_mute_status(mute=old_mute_value) return jsonify(error=data), 400
def test_save(self): key_to_save = "key1" value_to_save = "value1" expected_memory = { "key1": "value1" } Cortex.save(key=key_to_save, value=value_to_save) self.assertDictEqual(expected_memory, Cortex.memory)
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 # we can save parameters from the neuron in memory Cortex.save_neuron_parameter_in_memory(self.kalliope_memory, message) if isinstance(message, str) or isinstance(message, six.text_type): logger.debug("[NeuronModule] message is string") tts_message = message if isinstance(message, list): logger.debug("[NeuronModule] message is list") tts_message = random.choice(message) if isinstance(message, dict): logger.debug("[NeuronModule] message is dict") tts_message = self._get_message_from_dict(message) if tts_message is not None: logger.debug("[NeuronModule] tts_message to say: %s" % tts_message) self.tts_message = tts_message Utils.print_success(tts_message) # save in kalliope memory the last tts message Cortex.save("kalliope_last_tts_message", tts_message) # process the audio only if the mute flag is false if self.settings.options.mute: logger.debug("[NeuronModule] mute is True, Kalliope is muted") else: logger.debug("[NeuronModule] mute is False, make Kalliope speaking") HookManager.on_start_speaking() # get the instance of the TTS module tts_folder = None if self.settings.resources: tts_folder = self.settings.resources.tts_folder tts_module_instance = Utils.get_dynamic_class_instantiation(package_name="tts", module_name=self.tts.name, parameters=self.tts.parameters, resources_dir=tts_folder) # generate the audio file and play it tts_module_instance.say(tts_message) HookManager.on_stop_speaking()
def test_save_neuron_parameter_in_memory(self): # test with a list of parameter with bracket neuron1_parameters = {"key1": "value1", "key2": "value2"} dict_val_to_save = {"my_key_in_memory": "{{key1}}"} expected_dict = {"my_key_in_memory": "value1"} Cortex.save_neuron_parameter_in_memory( kalliope_memory_dict=dict_val_to_save, neuron_parameters=neuron1_parameters) self.assertDictEqual(expected_dict, Cortex.memory) # test with a list of parameter with brackets and string self.setUp() # clean neuron1_parameters = {"key1": "value1", "key2": "value2"} dict_val_to_save = {"my_key_in_memory": "string {{key1}}"} expected_dict = {"my_key_in_memory": "string value1"} Cortex.save_neuron_parameter_in_memory( kalliope_memory_dict=dict_val_to_save, neuron_parameters=neuron1_parameters) self.assertDictEqual(expected_dict, Cortex.memory) # test with a list of parameter with only a string. Neuron parameters are not used self.setUp() # clean neuron1_parameters = {"key1": "value1", "key2": "value2"} dict_val_to_save = {"my_key_in_memory": "string"} expected_dict = {"my_key_in_memory": "string"} Cortex.save_neuron_parameter_in_memory( kalliope_memory_dict=dict_val_to_save, neuron_parameters=neuron1_parameters) self.assertDictEqual(expected_dict, Cortex.memory) # test with an empty list of parameter to save (no kalliope_memory set) self.setUp() # clean neuron1_parameters = {"key1": "value1", "key2": "value2"} dict_val_to_save = None Cortex.save_neuron_parameter_in_memory( kalliope_memory_dict=dict_val_to_save, neuron_parameters=neuron1_parameters) self.assertDictEqual(dict(), Cortex.memory) # test with a neuron that return a dict in each parameter self.setUp() # clean neuron1_parameters = {"key1": {"key2": "value2", "key3": "value3"}} dict_val_to_save = {"my_key_in_memory": "{{key1}}"} expected_dict = { "my_key_in_memory": { "key2": "value2", "key3": "value3" } } Cortex.save_neuron_parameter_in_memory( kalliope_memory_dict=dict_val_to_save, neuron_parameters=neuron1_parameters) self.assertDictEqual(expected_dict, Cortex.memory)
def order_listener_callback(self, order): """ Receive an order, try to retrieve it in the brain.yml to launch to attached plugins :param order: the sentence received :type order: str """ logger.debug("[Order] Order listener callback called. Order to process: %s" % order) HookManager.on_stop_listening() self.order_to_process = order self.order_listener_callback_called = True # save in kalliope memory the last order Cortex.save('kalliope_last_order', order)
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 test_add_parameters_from_order(self): order_parameters = { "key1": "value1", "key2": "value2" } expected_temp_dict = { "key1": "value1", "key2": "value2" } Cortex.add_parameters_from_order(order_parameters) self.assertDictEqual(Cortex.temp, expected_temp_dict)
def _replace_brackets_by_loaded_parameter(cls, neuron_parameters, loaded_parameters): """ Receive a value (which can be a str or dict or list) and instantiate value in double brace bracket by the value specified in the loaded_parameters dict. This method will call itself until all values has been instantiated :param neuron_parameters: value to instantiate. Str or dict or list :param loaded_parameters: dict of parameters """ logger.debug("[NeuronLauncher] replacing brackets from %s, using %s" % (neuron_parameters, loaded_parameters)) # add variables from the short term memory to the list of loaded parameters that can be used in a template # the final dict is added into a key "kalliope_memory" to not override existing keys loaded form the order memory_dict = dict() memory_dict["kalliope_memory"] = Cortex.get_memory() if loaded_parameters is None: loaded_parameters = dict( ) # instantiate an empty dict in order to be able to add memory in it loaded_parameters.update(memory_dict) if isinstance(neuron_parameters, str) or isinstance( neuron_parameters, six.text_type): # replace bracket parameter only if the str contains brackets if Utils.is_containing_bracket(neuron_parameters): # check that the parameter to replace is available in the loaded_parameters dict if cls._neuron_parameters_are_available_in_loaded_parameters( neuron_parameters, loaded_parameters): # add parameters from global variable into the final loaded parameter dict settings = cls.load_settings() loaded_parameters.update(settings.variables) neuron_parameters = jinja2.Template( neuron_parameters).render(loaded_parameters) neuron_parameters = Utils.encode_text_utf8( neuron_parameters) return str(neuron_parameters) else: raise NeuronParameterNotAvailable return neuron_parameters if isinstance(neuron_parameters, dict): returned_dict = dict() for key, value in neuron_parameters.items(): # following keys are reserved by kalliope core if key in "say_template" or key in "file_template" or key in "kalliope_memory" \ or key in "from_answer_link": returned_dict[key] = value else: returned_dict[ key] = cls._replace_brackets_by_loaded_parameter( value, loaded_parameters) return returned_dict if isinstance(neuron_parameters, list): returned_list = list() for el in neuron_parameters: templated_value = cls._replace_brackets_by_loaded_parameter( el, loaded_parameters) returned_list.append(templated_value) return returned_list # in all other case (boolean or int for example) we return the value as it return neuron_parameters
def test_run_synapse_with_order(self): url = self.get_server_url() + "/synapses/start/order" headers = {"Content-Type": "application/json"} data = {"order": "test_order"} result = self.client.post(url, headers=headers, data=json.dumps(data)) expected_content = {'status': 'complete', 'matched_synapses': [ { 'matched_order': "test_order", 'neuron_module_list': [ { 'generated_message': 'test message', 'neuron_name': 'Say' } ], 'synapse_name': 'test' } ], 'user_order': "test_order" } self.assertEqual(json.dumps(expected_content, sort_keys=True), json.dumps(json.loads(result.get_data().decode('utf-8')), sort_keys=True)) self.assertEqual(result.status_code, 201) # check that the cortex contains the last order self.assertEqual("test_order", Cortex.get_from_key("kalliope_last_order")) # test with a wrong parameter in a neuron data = {"order": "test_order_miss_configured_neuron"} result = self.client.post(url, headers=headers, data=json.dumps(data)) self.assertEqual(result.status_code, 201)
def stop_http_server(self): running_server = Cortex.get_from_key("EditorServerThread") if running_server: Utils.print_info("[ Editor ] Editor is running, stopping now...") running_server.shutdown_server() while not running_server.is_down: time.sleep(0.1) return True
def test_get_memory(self): test_memory = { "key1": "value1", "key2": "value2" } Cortex.memory = test_memory self.assertDictEqual(test_memory, Cortex.get_memory())
def test_get_from_key(self): test_memory = { "key1": "value1", "key2": "value2" } Cortex.memory = test_memory expected_value = "value2" self.assertEqual(expected_value, Cortex.get_from_key("key2"))
def execute(self, answer=None, is_api_call=False, no_voice=False): """ Process the LIFO list. The LIFO list contains multiple list of matched synapses. For each list of matched synapse we process synapses inside For each synapses we process neurons. If a neuron add a Synapse list to the lifo, this synapse list is processed before executing the first list in which we were in. :param answer: String answer to give the the last neuron which was waiting for an answer :param is_api_call: Boolean passed to all neuron in order to let them know if the current call comes from API :param no_voice: If true, the generated text will not be processed by the TTS engine :return: serialized APIResponse object """ # store the answer if present self.answer = answer self.is_api_call = is_api_call self.no_voice = no_voice if not self.is_running: self.is_running = True try: # we keep looping over the LIFO til we have synapse list to process in it while self.lifo_list: logger.debug( "[LIFOBuffer] number of synapse list to process: %s" % len(self.lifo_list)) try: # get the last list of matched synapse in the LIFO last_synapse_fifo_list = self.lifo_list[-1] self._process_synapse_list(last_synapse_fifo_list) except SynapseListAddedToLIFO: continue # remove the synapse list from the LIFO self.lifo_list.remove(last_synapse_fifo_list) # clean the cortex from value loaded from order as all synapses have been processed Cortex.clean_parameter_from_order() self.is_running = False raise Serialize except Serialize: return self._return_serialized_api_response()
def start_new_process(self, sound_arg): """ Start mplayer process with the given sounds to play :param sound_arg: :type sound_arg: list of dicts [{name: link}, {name: link}, {name: link}] :return: """ mplayer_exec_path = [self.mplayer_path] mplayer_options = [ '-slave', '-quiet', '-af', 'volume=-10', '-volume', str(self.launch_volume), '-loop' ] mplayer_options.append("0" if self.loop_option == "loop" else "1") mplayer_command = list() mplayer_command.extend(mplayer_exec_path) mplayer_command.extend(mplayer_options) for sound in sound_arg: for sound_name, sound_link in sound.items(): mplayer_command.append(sound_link) logger.debug("[Background_sound_player] Mplayer cmd: %s" % str(mplayer_command)) # give the current file name played to the neuron template self.message['sound_name'] = sound_name self.message["sound_link"] = sound_link # run mplayer in background inside a new process fnull = open(os.devnull, 'w') process = subprocess.Popen(mplayer_command, stdout=fnull, stderr=fnull, stdin=subprocess.PIPE, universal_newlines=True) self.lower_sound_for_speaking() Cortex.save("current_playing_background_sound", sound_name) Cortex.save("background_mplayer_popen", process) logger.debug("[Background_sound_player] Mplayer started, pid: %s" % process.pid)
def stop_last_process(self): """ stop the last mplayer process launched by this neuron :return: """ if self.mplayer_popen_obj is not None: logger.debug("[Background_sound_player] loaded pid: %s" % self.mplayer_popen_obj.pid) self.mplayer_popen_obj.kill() logger.debug( "[Background_sound_player] mplayer process with pid %s killed" % self.mplayer_popen_obj.pid) Cortex.save("current_playing_background_sound", "Aucun fond sonore lancé actuellement") Cortex.save("background_mplayer_popen", 'NIL') else: logger.debug( "[Background_sound_player] Popen object is None. Process already stopped" )
def audio_analyser_callback(self, order): """ Callback of the OrderListener. Called after the processing of the audio file This method will - call the Order Analyser to analyse the order and launch corresponding synapse as usual. - get a list of launched synapse. - give the list to the main process via self.launched_synapses - notify that the processing is over via order_analyser_return :param order: string order to analyse :return: """ logger.debug("[FlaskAPI] audio_analyser_callback: order to process -> %s" % order) api_response = SynapseLauncher.run_matching_synapse_from_order(order, self.brain, self.settings, is_api_call=True) self.api_response = api_response Cortex.save('kalliope_last_order', order) # this boolean will notify the main process that the order have been processed self.order_analyser_return = True
def execute(self, answer=None, is_api_call=False): """ Process the LIFO list. The LIFO list contains multiple list of matched synapses. For each list of matched synapse we process synapses inside For each synapses we process neurons. If a neuron add a Synapse list to the lifo, this synapse list is processed before executing the first list in which we were in. :param answer: String answer to give the the last neuron which was waiting for an answer :param is_api_call: Boolean passed to all neuron in order to let them know if the current call comes from API :return: serialized APIResponse object """ # store the answer if present self.answer = answer self.is_api_call = is_api_call try: if not self.is_running: self.is_running = True # we keep looping over the LIFO til we have synapse list to process in it while self.lifo_list: logger.debug("[LIFOBuffer] number of synapse list to process: %s" % len(self.lifo_list)) try: # get the last list of matched synapse in the LIFO last_synapse_fifo_list = self.lifo_list[-1] self._process_synapse_list(last_synapse_fifo_list) except SynapseListAddedToLIFO: continue # remove the synapse list from the LIFO self.lifo_list.remove(last_synapse_fifo_list) # clean the cortex from value loaded from order as all synapses have been processed Cortex.clean_parameter_from_order() self.is_running = False raise Serialize except Serialize: return self._return_serialized_api_response()
def __init__(self, **kwargs): super(Background_sound_player, self).__init__(**kwargs) self.state = kwargs.get('state', None) # "on" / "off" self.sounds = kwargs.get( 'sounds', None) # "[{'title1': 'link1'}, {'title2': 'link2'}, ...]" self.random_option = kwargs.get( 'random_option', "random-select-one" ) # "random-order-play" / "random-select-one" / "no-random" self.loop_option = kwargs.get('loop_option', 'no-loop') # "loop" / "no-loop" self.mplayer_path = kwargs.get('mplayer_path', "/usr/bin/mplayer") self.auto_stop_minutes = kwargs.get('auto_stop_minutes', None) self.currently_playing_sound = None # a dict of parameters the user ask to save in short term memory self.kalliope_memory = kwargs.get('kalliope_memory', None) # parameters loaded from the order can be save now Cortex.save_parameter_from_order_in_memory(self.kalliope_memory) Cortex.save("current_playing_background_sound", "Aucun fond sonore lancé actuellement") # message dict that will be passed to the neuron template self.message = dict() # check if sent parameters are in good state if self._is_parameters_ok(): if self.state == "off": self.stop_last_process() self.clean_pid_file() Cortex.save("current_playing_background_sound", "Aucun fond sonore lancé actuellement") else: # we stop the last process if exist self.stop_last_process() # pick one sound randomly in all sounds entered if self.random_option == "random-select-one": self.currently_playing_sound = [random.choice(self.sounds)] # play all sounds in random order elif self.random_option == "random-order-play": random.shuffle(self.sounds) self.currently_playing_sound = self.sounds # play all sounds the specified order else: self.currently_playing_sound = self.sounds # then we can start a new process self.start_new_process(self.currently_playing_sound) # run auto stop thread if self.auto_stop_minutes: thread_auto_stop = threading.Thread( target=self.wait_before_stop) thread_auto_stop.start() # give the message dict to the neuron template self.say(self.message)
def _replace_brackets_by_loaded_parameter(cls, neuron_parameters, loaded_parameters=dict()): """ Receive a value (which can be a str or dict or list) and instantiate value in double brace bracket by the value specified in the loaded_parameters dict. This method will call itself until all values has been instantiated :param neuron_parameters: value to instantiate. Str or dict or list :param loaded_parameters: dict of parameters """ logger.debug("[NeuronLauncher] replacing brackets from %s, using %s" % (neuron_parameters, loaded_parameters)) if isinstance(neuron_parameters, str) or isinstance(neuron_parameters, six.text_type): # replace bracket parameter only if the str contains brackets if Utils.is_containing_bracket(neuron_parameters): settings = cls.load_settings() # Priority to memory over the variables loaded_parameters.update(settings.variables) memory_dict = dict() # add variables from the short term memory to the list of loaded parameters that can be used in a template # the final dict is added into a key "kalliope_memory" to not override existing keys loaded form the order memory_dict["kalliope_memory"] = Cortex.get_memory() loaded_parameters.update(memory_dict) # check that the parameter to replace is available in the loaded_parameters dict if cls._neuron_parameters_are_available_in_loaded_parameters(neuron_parameters, loaded_parameters): # add parameters from global variable into the final loaded parameter dict neuron_parameters = jinja2.Template(neuron_parameters).render(loaded_parameters) neuron_parameters = Utils.encode_text_utf8(neuron_parameters) return str(neuron_parameters) else: raise NeuronParameterNotAvailable return neuron_parameters if isinstance(neuron_parameters, dict): returned_dict = dict() for key, value in neuron_parameters.items(): # following keys are reserved by kalliope core if key in "say_template" or key in "file_template" or key in "kalliope_memory" \ or key in "from_answer_link": returned_dict[key] = value else: returned_dict[key] = cls._replace_brackets_by_loaded_parameter(value, loaded_parameters) return returned_dict if isinstance(neuron_parameters, list): returned_list = list() for el in neuron_parameters: templated_value = cls._replace_brackets_by_loaded_parameter(el, loaded_parameters) returned_list.append(templated_value) return returned_list # in all other case (boolean or int for example) we return the value as it return neuron_parameters
def _get_file_template(cls, file_template, message_dict): real_file_template_path = Utils.get_real_file_path(file_template) if real_file_template_path is None: raise TemplateFileNotFoundException( "Template file %s not found in templates folder" % real_file_template_path) # load the content of the file as template t = Template(cls._get_content_of_file(real_file_template_path)) # add kalliope memory final_message_dict = dict() final_message_dict["kalliope_memory"] = Cortex.get_memory() if message_dict: final_message_dict.update(**message_dict) returned_message = t.render(final_message_dict) return returned_message
def get_mplayer_popen_obj(): ret_process = Cortex.get_from_key('background_mplayer_popen') if (ret_process == 'NIL'): ret_process = None return ret_process
def test_save_neuron_parameter_in_memory(self): # test with a list of parameter with bracket neuron1_parameters = { "key1": "value1", "key2": "value2" } dict_val_to_save = {"my_key_in_memory": "{{key1}}"} expected_dict = {"my_key_in_memory": "value1"} Cortex.save_neuron_parameter_in_memory(kalliope_memory_dict=dict_val_to_save, neuron_parameters=neuron1_parameters) self.assertDictEqual(expected_dict, Cortex.memory) # test with a list of parameter with brackets and string self.setUp() # clean neuron1_parameters = { "key1": "value1", "key2": "value2" } dict_val_to_save = {"my_key_in_memory": "string {{key1}}"} expected_dict = {"my_key_in_memory": "string value1"} Cortex.save_neuron_parameter_in_memory(kalliope_memory_dict=dict_val_to_save, neuron_parameters=neuron1_parameters) self.assertDictEqual(expected_dict, Cortex.memory) # test with a list of parameter with only a string. Neuron parameters are not used self.setUp() # clean neuron1_parameters = { "key1": "value1", "key2": "value2" } dict_val_to_save = {"my_key_in_memory": "string"} expected_dict = {"my_key_in_memory": "string"} Cortex.save_neuron_parameter_in_memory(kalliope_memory_dict=dict_val_to_save, neuron_parameters=neuron1_parameters) self.assertDictEqual(expected_dict, Cortex.memory) # test with an empty list of parameter to save (no kalliope_memory set) self.setUp() # clean neuron1_parameters = { "key1": "value1", "key2": "value2" } dict_val_to_save = None Cortex.save_neuron_parameter_in_memory(kalliope_memory_dict=dict_val_to_save, neuron_parameters=neuron1_parameters) self.assertDictEqual(dict(), Cortex.memory)
def run(self): """ Start the voice detector. For every `sleep_time` second it checks the audio buffer for triggering keywords. If detected, then call corresponding function in `detected_callback`, which can be a single function (single model) or a list of callback functions (multiple models). Every loop it also calls `interrupt_check` -- if it returns True, then breaks from the loop and return. :param detected_callback: a function or list of functions. The number of items must match the number of models in `decoder_model`. :param interrupt_check: a function that returns True if the main loop needs to stop. :param float sleep_time: how much time in second every loop waits. :param audio_recorder_callback: if specified, this will be called after a keyword has been spoken and after the phrase immediately after the keyword has been recorded. The function will be passed the name of the file where the phrase was recorded. :param silent_count_threshold: indicates how long silence must be heard to mark the end of a phrase that is being recorded. :param recording_timeout: limits the maximum length of a recording. :return: None """ if self.interrupt_check(): logger.debug("detect voice return") return tc = type(self.detected_callback) if tc is not list: self.detected_callback = [self.detected_callback] if len(self.detected_callback) == 1 and self.num_hotwords > 1: self.detected_callback *= self.num_hotwords assert self.num_hotwords == len(self.detected_callback), \ "Error: hotwords in your models (%d) do not match the number of " \ "callbacks (%d)" % (self.num_hotwords, len(self.detected_callback)) logger.debug("detecting...") SR = SpeechRecorder() while not self.kill_received: #if not self.paused: if self.interrupt_check(): logger.debug("detect voice break") break data = self.ring_buffer.get() if len(data) == 0: time.sleep(self.sleep_time) continue self.saveMessage( data ) # Save trigger data so it can be append to the record for STT status = self.detector.RunDetection(data) if status > 0: #key word found SR.start() # Start the speech recorder Utils.print_info("Keyword " + self.keyword_names[status] + " detected") Cortex.save( 'kalliope_trigger_called', self.keyword_names[status] ) # I save it to the Cortex, to use it by another neuron # for changing the tts acording to the trigger name HookManager.on_triggered() callback = self.detected_callback[status - 1] if callback is not None: callback() if status == -1: logger.warning( "Error initializing streams or reading audio data") logger.debug("finished.")
def test_clean_parameter_from_order(self): Cortex.temp = {"key1": "value1", "key2": "value2"} Cortex.clean_parameter_from_order() expected_temp_dict = dict() self.assertDictEqual(expected_temp_dict, Cortex.memory)
def start_new_process(self, sound_arg): #Start mplayer process """ Start mplayer process with the given sounds to play :param sound_arg: :type sound_arg: list of dicts [{name: link}, {name: link}, {name: link}] :return: """ currently_playing_sound = None mplayer_exec_path = [self.mplayer_path] mplayer_options = ['-slave', '-quiet', '-af'] mplayer_volume = ['volume'] mplayer_volume[0] = "volume=" + self.volume mplayer_loop = ['-loop'] mplayer_loop.append("0" if self.loop_option == "loop" else "1") # mplayer { 1.avi - loop 2 2.avi } -loop 3 > La commande jouera les fichiers dans cet ordre: 1, 1, 2, 1, 1, 2, 1, 1, 2. "-loop 0" tournera a l'infini first_sound_name, first_sound_link = list(sound_arg[0].items())[0] first_sound_link = str(first_sound_link) # Pick one sound randomly in all sounds entered. currently_playing_sound will have only one entry # Need anyway to add "-playlist" if the link is a TXT playlist. But this playlist will be read with no shuffle option if self.random_option == "random-select-one": currently_playing_sound = [random.choice(sound_arg)] if (first_sound_link)[-4:] == ".txt": mplayer_loop.append("-playlist") # play all sounds in random order if there is no TXT playlist # at this stage, the list either only TXT or playable files (if not it should has raised an error in _check_sounds function # if the links are then TXT playlist, one will be selected. Otherwise, shuffle parameter is added to MPLAYER commands > need "-playlist" for TXT playlist elif self.random_option == "random-order-play": mplayer_loop.append("-shuffle") if str( first_sound_link )[-4:] == ".txt": # if it is a TXT (then like all others), need to randomly select one currently_playing_sound = [random.choice(sound_arg)] mplayer_loop.append("-playlist") else: currently_playing_sound = sound_arg # play all sounds the specified order else: if str(first_sound_link )[-4:] == ".txt": # if it is a TXT (then like all others) mplayer_loop.append( "-playlist") # then indicate that this is a playlist currently_playing_sound = sound_arg[ 0] # and select the first playlist else: currently_playing_sound = sound_arg # else the list is simply copied mplayer_command = list() mplayer_command.extend(mplayer_exec_path) mplayer_command.extend(mplayer_options) mplayer_command.extend(mplayer_volume) mplayer_command.extend(mplayer_loop) for sound in currently_playing_sound: for sound_name, sound_link in sound.items(): mplayer_command.append(sound_link) logger.debug("[Background_sound_player] Mplayer cmd: %s" % str(mplayer_command)) Cortex.save("current_playing_background_sound", sound_name) # give the current file name played to the neuron template self.message['sound_name'] = sound_name self.message["sound_link"] = sound_link # run mplayer in background inside a new process fnull = open(os.devnull, 'w') # mplayer_command = "mplayer -slave -quiet -af volume=-20 -loop 0 -shuffle -playlist /home/pi/kalliope_starter_fr/resources/sounds/MP3/Florian/*.*" pid = subprocess.Popen(mplayer_command, stdout=fnull, stderr=fnull).pid # store the pid in a file to be killed later self.store_pid(pid) logger.debug("[Background_sound_player] Mplayer started, pid: %s" % pid)
def test_get_memory(self): test_memory = {"key1": "value1", "key2": "value2"} Cortex.memory = test_memory self.assertDictEqual(test_memory, Cortex.get_memory())
def test_get_from_key(self): test_memory = {"key1": "value1", "key2": "value2"} Cortex.memory = test_memory expected_value = "value2" self.assertEqual(expected_value, Cortex.get_from_key("key2"))