def waiting_for_order_listener_callback_thread(self): """ Method to print in debug that the main process is waiting for an order to analyse """ logger.debug("[MainController] Entering state: %s" % self.state) # this loop is used to keep the main thread alive while not self.order_listener_callback_called: sleep(0.1) if self.settings.rpi_settings: if self.settings.rpi_settings.pin_led_listening: RpiUtils.switch_pin_to_off(self.settings.rpi_settings.pin_led_listening) self.next_state()
def switch_on_led_talking(rpi_settings, on): """ Call the Rpi utils class to switch the led talking if the setting has been specified by the user :param rpi_settings: Rpi :param on: True if the led need to be switched to on """ if rpi_settings: if rpi_settings.pin_led_talking: if on: RpiUtils.switch_pin_to_on(rpi_settings.pin_led_talking) else: RpiUtils.switch_pin_to_off(rpi_settings.pin_led_talking)
def run(self): """ Start the thread that listen the microphone and then give the audio to the callback method """ if self.audio_stream is None: Utils.print_info("Say something!") # Turn on the listening led if we are on a Raspberry if self.settings.rpi_settings: if self.settings.rpi_settings.pin_led_listening: RpiUtils.switch_pin_to_on( self.settings.rpi_settings.pin_led_listening) self.stop_thread = self.recognizer.listen_in_background( self.microphone, self.callback) while not self.kill_yourself: sleep(0.1) logger.debug("kill the speech recognition process") self.stop_thread() else: self.callback(self.recognizer, self.audio_stream)
def init_rpi_utils(self): """ Start listening on GPIO if defined in settings """ if self.settings.rpi_settings: # the user set GPIO pin, we need to instantiate the RpiUtils class in order to setup GPIO rpi_utils = RpiUtils(self.settings.rpi_settings, self.set_mute_status) if self.settings.rpi_settings.pin_mute_button: # start the listening for button pressed thread only if the user set a pin rpi_utils.daemon = True rpi_utils.start() # switch high the start led, as kalliope is started. Only if the setting exist if self.settings.rpi_settings: if self.settings.rpi_settings.pin_led_started: logger.debug("[MainController] Switching pin_led_started to ON") RpiUtils.switch_pin_to_on(self.settings.rpi_settings.pin_led_started)
def __init__(self, brain=None): self.brain = brain # get global configuration sl = SettingLoader() self.settings = sl.settings # keep in memory the order to process self.order_to_process = None # Starting the rest API self._start_rest_api() # rpi setting for led and mute button self.rpi_utils = None if self.settings.rpi_settings: # the useer set GPIO pin, we need to instantiate the RpiUtils class in order to setup GPIO self.rpi_utils = RpiUtils(self.settings.rpi_settings, self.muted_button_pressed) if self.settings.rpi_settings.pin_mute_button: # start the listening for button pressed thread only if the user set a pin self.rpi_utils.daemon = True self.rpi_utils.start() # switch high the start led, as kalliope is started. Only if the setting exist if self.settings.rpi_settings: if self.settings.rpi_settings.pin_led_started: logger.debug( "[MainController] Switching pin_led_started to ON") RpiUtils.switch_pin_to_on( self.settings.rpi_settings.pin_led_started) # get the player instance self.player_instance = PlayerLauncher.get_player( settings=self.settings) # save an instance of the trigger self.trigger_instance = None self.trigger_callback_called = False # save the current order listener self.order_listener = None self.order_listener_callback_called = False # boolean used to know id we played the on ready notification at least one time self.on_ready_notification_played_once = False # Initialize the state machine self.machine = Machine(model=self, states=MainController.states, initial='init', queued=True) # define transitions self.machine.add_transition('start_trigger', ['init', 'analysing_order'], 'starting_trigger') self.machine.add_transition('play_ready_sound', 'starting_trigger', 'playing_ready_sound') self.machine.add_transition('wait_trigger_callback', 'playing_ready_sound', 'waiting_for_trigger_callback') self.machine.add_transition('stop_trigger', 'waiting_for_trigger_callback', 'stopping_trigger') self.machine.add_transition('play_wake_up_answer', 'waiting_for_trigger_callback', 'playing_wake_up_answer') self.machine.add_transition('wait_for_order', 'playing_wake_up_answer', 'waiting_for_order_listener_callback') self.machine.add_transition('analyse_order', 'waiting_for_order_listener_callback', 'analysing_order') self.machine.add_ordered_transitions() # add method which are called when changing state self.machine.on_enter_starting_trigger('start_trigger_process') self.machine.on_enter_playing_ready_sound('play_ready_sound_process') self.machine.on_enter_waiting_for_trigger_callback( 'waiting_for_trigger_callback_thread') self.machine.on_enter_playing_wake_up_answer( 'play_wake_up_answer_thread') self.machine.on_enter_stopping_trigger('stop_trigger_process') self.machine.on_enter_start_order_listener( 'start_order_listener_thread') self.machine.on_enter_waiting_for_order_listener_callback( 'waiting_for_order_listener_callback_thread') self.machine.on_enter_analysing_order('analysing_order_thread') self.start_trigger()
class MainController: """ This Class is the global controller of the application. """ states = [ 'init', 'starting_trigger', 'playing_ready_sound', 'waiting_for_trigger_callback', 'stopping_trigger', 'start_order_listener', 'playing_wake_up_answer', 'waiting_for_order_listener_callback', 'analysing_order' ] def __init__(self, brain=None): self.brain = brain # get global configuration sl = SettingLoader() self.settings = sl.settings # keep in memory the order to process self.order_to_process = None # Starting the rest API self._start_rest_api() # rpi setting for led and mute button self.rpi_utils = None if self.settings.rpi_settings: # the useer set GPIO pin, we need to instantiate the RpiUtils class in order to setup GPIO self.rpi_utils = RpiUtils(self.settings.rpi_settings, self.muted_button_pressed) if self.settings.rpi_settings.pin_mute_button: # start the listening for button pressed thread only if the user set a pin self.rpi_utils.daemon = True self.rpi_utils.start() # switch high the start led, as kalliope is started. Only if the setting exist if self.settings.rpi_settings: if self.settings.rpi_settings.pin_led_started: logger.debug( "[MainController] Switching pin_led_started to ON") RpiUtils.switch_pin_to_on( self.settings.rpi_settings.pin_led_started) # get the player instance self.player_instance = PlayerLauncher.get_player( settings=self.settings) # save an instance of the trigger self.trigger_instance = None self.trigger_callback_called = False # save the current order listener self.order_listener = None self.order_listener_callback_called = False # boolean used to know id we played the on ready notification at least one time self.on_ready_notification_played_once = False # Initialize the state machine self.machine = Machine(model=self, states=MainController.states, initial='init', queued=True) # define transitions self.machine.add_transition('start_trigger', ['init', 'analysing_order'], 'starting_trigger') self.machine.add_transition('play_ready_sound', 'starting_trigger', 'playing_ready_sound') self.machine.add_transition('wait_trigger_callback', 'playing_ready_sound', 'waiting_for_trigger_callback') self.machine.add_transition('stop_trigger', 'waiting_for_trigger_callback', 'stopping_trigger') self.machine.add_transition('play_wake_up_answer', 'waiting_for_trigger_callback', 'playing_wake_up_answer') self.machine.add_transition('wait_for_order', 'playing_wake_up_answer', 'waiting_for_order_listener_callback') self.machine.add_transition('analyse_order', 'waiting_for_order_listener_callback', 'analysing_order') self.machine.add_ordered_transitions() # add method which are called when changing state self.machine.on_enter_starting_trigger('start_trigger_process') self.machine.on_enter_playing_ready_sound('play_ready_sound_process') self.machine.on_enter_waiting_for_trigger_callback( 'waiting_for_trigger_callback_thread') self.machine.on_enter_playing_wake_up_answer( 'play_wake_up_answer_thread') self.machine.on_enter_stopping_trigger('stop_trigger_process') self.machine.on_enter_start_order_listener( 'start_order_listener_thread') self.machine.on_enter_waiting_for_order_listener_callback( 'waiting_for_order_listener_callback_thread') self.machine.on_enter_analysing_order('analysing_order_thread') self.start_trigger() def start_trigger_process(self): """ This function will start the trigger thread that listen for the hotword """ logger.debug("[MainController] Entering state: %s" % self.state) self.trigger_instance = TriggerLauncher.get_trigger( settings=self.settings, callback=self.trigger_callback) self.trigger_callback_called = False self.trigger_instance.daemon = True # Wait that the kalliope trigger is pronounced by the user self.trigger_instance.start() self.next_state() def play_ready_sound_process(self): """ Play a sound when Kalliope is ready to be awaken at the first start """ logger.debug("[MainController] Entering state: %s" % self.state) if (not self.on_ready_notification_played_once and self.settings.play_on_ready_notification == "once") or \ self.settings.play_on_ready_notification == "always": # we remember that we played the notification one time self.on_ready_notification_played_once = True # here we tell the user that we are listening if self.settings.on_ready_answers is not None: Say(message=self.settings.on_ready_answers) elif self.settings.on_ready_sounds is not None: random_sound_to_play = self._get_random_sound( self.settings.on_ready_sounds) self.player_instance.play(random_sound_to_play) self.next_state() def waiting_for_trigger_callback_thread(self): """ Method to print in debug that the main process is waiting for a trigger detection """ logger.debug("[MainController] Entering state: %s" % self.state) Utils.print_info("Waiting for trigger detection") # this loop is used to keep the main thread alive while not self.trigger_callback_called: sleep(0.1) self.next_state() def waiting_for_order_listener_callback_thread(self): """ Method to print in debug that the main process is waiting for an order to analyse """ logger.debug("[MainController] Entering state: %s" % self.state) # this loop is used to keep the main thread alive while not self.order_listener_callback_called: sleep(0.1) if self.settings.rpi_settings: if self.settings.rpi_settings.pin_led_listening: RpiUtils.switch_pin_to_off( self.settings.rpi_settings.pin_led_listening) self.next_state() def trigger_callback(self): """ we have detected the hotword, we can now pause the Trigger for a while The user can speak out loud his order during this time. """ logger.debug( "[MainController] Trigger callback called, switching to the next state" ) self.trigger_callback_called = True def stop_trigger_process(self): """ The trigger has been awaken, we don't needed it anymore :return: """ logger.debug("[MainController] Entering state: %s" % self.state) self.trigger_instance.stop() self.next_state() def start_order_listener_thread(self): """ Start the STT engine thread """ logger.debug("[MainController] Entering state: %s" % self.state) # start listening for an order self.order_listener_callback_called = False self.order_listener = OrderListener( callback=self.order_listener_callback) self.order_listener.daemon = True self.order_listener.start() self.next_state() def play_wake_up_answer_thread(self): """ Play a sound or make Kalliope say something to notify the user that she has been awaken and now waiting for order """ logger.debug("[MainController] Entering state: %s" % self.state) # if random wake answer sentence are present, we play this if self.settings.random_wake_up_answers is not None: Say(message=self.settings.random_wake_up_answers) else: random_sound_to_play = self._get_random_sound( self.settings.random_wake_up_sounds) self.player_instance.play(random_sound_to_play) self.next_state() 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( "[MainController] Order listener callback called. Order to process: %s" % order) self.order_to_process = order self.order_listener_callback_called = True def analysing_order_thread(self): """ Start the order analyser with the caught order to process """ logger.debug("[MainController] order in analysing_order_thread %s" % self.order_to_process) SynapseLauncher.run_matching_synapse_from_order(self.order_to_process, self.brain, self.settings, is_api_call=False) # return to the state "unpausing_trigger" self.start_trigger() @staticmethod def _get_random_sound(random_wake_up_sounds): """ Return a path of a sound to play If the path is absolute, test if file exist If the path is relative, we check if the file exist in the sound folder :param random_wake_up_sounds: List of wake_up sounds :return: path of a sound to play """ # take first randomly a path random_path = random.choice(random_wake_up_sounds) logger.debug("[MainController] Selected sound: %s" % random_path) return Utils.get_real_file_path(random_path) def _start_rest_api(self): """ Start the Rest API if asked in the user settings """ # run the api if the user want it if self.settings.rest_api.active: Utils.print_info("Starting REST API Listening port: %s" % self.settings.rest_api.port) app = Flask(__name__) flask_api = FlaskAPI( app=app, port=self.settings.rest_api.port, brain=self.brain, allowed_cors_origin=self.settings.rest_api.allowed_cors_origin) flask_api.daemon = True flask_api.start() def muted_button_pressed(self, muted=False): logger.debug( "[MainController] Mute button pressed. Switch trigger process to muted: %s" % muted) if muted: self.trigger_instance.pause() Utils.print_info("Kalliope now muted") else: self.trigger_instance.unpause() Utils.print_info("Kalliope now listening for trigger detection")
def main(): """Entry point of Kalliope program.""" # parse argument. the script name is removed try: parser = parse_args(sys.argv[1:]) except SystemExit: sys.exit(1) # check if we want debug configure_logging(debug=parser.debug) logger.debug("kalliope args: %s" % parser) # by default, no brain file is set. # Use the default one: brain.yml in the root path brain_file = None # check if user set a brain.yml file if parser.brain_file: brain_file = parser.brain_file # check the user provide a valid action if parser.action not in ACTION_LIST: Utils.print_warning("%s is not a recognised action\n" % parser.action) sys.exit(1) # install modules if parser.action == "install": if not parser.git_url: Utils.print_danger("You must specify the git url") sys.exit(1) else: parameters = {"git_url": parser.git_url} res_manager = ResourcesManager(**parameters) res_manager.install() return # uninstall modules if parser.action == "uninstall": if not parser.neuron_name and not parser.stt_name and not parser.tts_name and not parser.trigger_name: Utils.print_danger( "You must specify a module name with --neuron-name or --stt-name or --tts-name " "or --trigger-name") sys.exit(1) else: res_manager = ResourcesManager() res_manager.uninstall(neuron_name=parser.neuron_name, stt_name=parser.stt_name, tts_name=parser.tts_name, trigger_name=parser.trigger_name) return # load the brain once brain_loader = BrainLoader(file_path=brain_file) brain = brain_loader.brain # load settings # get global configuration once settings_loader = SettingLoader() settings = settings_loader.settings if parser.action == "start": if settings.rpi_settings: # init GPIO once RpiUtils(settings.rpi_settings) # user set a synapse to start if parser.run_synapse is not None: SynapseLauncher.start_synapse_by_name(parser.run_synapse, brain=brain) if parser.run_order is not None: SynapseLauncher.run_matching_synapse_from_order(parser.run_order, brain=brain, settings=settings, is_api_call=False) if (parser.run_synapse is None) and (parser.run_order is None): # first, load events in event manager EventManager(brain.synapses) Utils.print_success("Events loaded") # then start kalliope Utils.print_success("Starting Kalliope") Utils.print_info("Press Ctrl+C for stopping") # catch signal for killing on Ctrl+C pressed signal.signal(signal.SIGINT, signal_handler) # start the state machine try: MainController(brain=brain) except (KeyboardInterrupt, SystemExit): Utils.print_info("Ctrl+C pressed. Killing Kalliope") finally: # we need to switch GPIO pin to default status if we are using a Rpi if settings.rpi_settings: logger.debug("Clean GPIO") import RPi.GPIO as GPIO GPIO.cleanup() if parser.action == "gui": try: ShellGui(brain=brain) except (KeyboardInterrupt, SystemExit): Utils.print_info("Ctrl+C pressed. Killing Kalliope") sys.exit(0)