order.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import logging
  2. from threading import Thread
  3. from time import sleep
  4. from kalliope.core.Cortex import Cortex
  5. from kalliope.core.SynapseLauncher import SynapseLauncher
  6. from kalliope.core.OrderListener import OrderListener
  7. from kalliope import Utils, BrainLoader
  8. from kalliope.core.TriggerLauncher import TriggerLauncher
  9. from transitions import Machine
  10. from kalliope.core.PlayerLauncher import PlayerLauncher
  11. from kalliope.core.ConfigurationManager import SettingLoader
  12. from kalliope.core.HookManager import HookManager
  13. logging.basicConfig()
  14. logger = logging.getLogger("kalliope")
  15. class Order(Thread):
  16. states = ['init',
  17. 'starting_trigger',
  18. 'waiting_for_trigger_callback',
  19. 'stopping_trigger',
  20. 'start_order_listener',
  21. 'waiting_for_order_listener_callback',
  22. 'analysing_order']
  23. def __init__(self):
  24. super(Order, self).__init__()
  25. Utils.print_info('Starting order signal')
  26. # load settings and brain from singleton
  27. sl = SettingLoader()
  28. self.settings = sl.settings
  29. self.brain = BrainLoader().brain
  30. # keep in memory the order to process
  31. self.order_to_process = None
  32. # get the player instance
  33. self.player_instance = PlayerLauncher.get_player(settings=self.settings)
  34. # save an instance of the trigger
  35. self.trigger_instance = None
  36. self.trigger_callback_called = False
  37. self.is_trigger_deaf = self.settings.start_options['deaf']
  38. # save the current order listener
  39. self.order_listener = None
  40. self.order_listener_callback_called = False
  41. # Initialize the state machine
  42. self.machine = Machine(model=self, states=Order.states, initial='init', queued=True)
  43. # define transitions
  44. self.machine.add_transition('start_trigger', ['init', 'analysing_order'], 'starting_trigger')
  45. self.machine.add_transition('wait_trigger_callback', 'starting_trigger', 'waiting_for_trigger_callback')
  46. self.machine.add_transition('stop_trigger', 'waiting_for_trigger_callback', 'stopping_trigger')
  47. self.machine.add_transition('wait_for_order', 'stopping_trigger', 'waiting_for_order_listener_callback')
  48. self.machine.add_transition('analyse_order', 'waiting_for_order_listener_callback', 'analysing_order')
  49. self.machine.add_ordered_transitions()
  50. # add method which are called when changing state
  51. self.machine.on_enter_starting_trigger('start_trigger_process')
  52. self.machine.on_enter_waiting_for_trigger_callback('waiting_for_trigger_callback_thread')
  53. self.machine.on_enter_stopping_trigger('stop_trigger_process')
  54. self.machine.on_enter_start_order_listener('start_order_listener_thread')
  55. self.machine.on_enter_waiting_for_order_listener_callback('waiting_for_order_listener_callback_thread')
  56. self.machine.on_enter_analysing_order('analysing_order_thread')
  57. def run(self):
  58. # run hook on_start
  59. HookManager.on_start()
  60. self.start_trigger()
  61. def start_trigger_process(self):
  62. """
  63. This function will start the trigger thread that listen for the hotword
  64. """
  65. logger.debug("[MainController] Entering state: %s" % self.state)
  66. HookManager.on_waiting_for_trigger()
  67. self.trigger_instance = TriggerLauncher.get_trigger(settings=self.settings, callback=self.trigger_callback)
  68. self.trigger_callback_called = False
  69. self.trigger_instance.daemon = True
  70. # Wait that the kalliope trigger is pronounced by the user
  71. self.trigger_instance.start()
  72. self.next_state()
  73. def waiting_for_trigger_callback_thread(self):
  74. """
  75. Method to print in debug that the main process is waiting for a trigger detection
  76. """
  77. logger.debug("[MainController] Entering state: %s" % self.state)
  78. if self.is_trigger_deaf: # the user asked to deaf inside the deaf neuron
  79. Utils.print_info("Kalliope is deaf")
  80. self.trigger_instance.pause()
  81. else:
  82. Utils.print_info("Waiting for trigger detection")
  83. # this loop is used to keep the main thread alive
  84. while not self.trigger_callback_called:
  85. sleep(0.1)
  86. # if here, then the trigger has been called
  87. HookManager.on_triggered()
  88. self.next_state()
  89. def waiting_for_order_listener_callback_thread(self):
  90. """
  91. Method to print in debug that the main process is waiting for an order to analyse
  92. """
  93. logger.debug("[MainController] Entering state: %s" % self.state)
  94. # this loop is used to keep the main thread alive
  95. while not self.order_listener_callback_called:
  96. sleep(0.1)
  97. # TODO on end listening here
  98. self.next_state()
  99. def trigger_callback(self):
  100. """
  101. we have detected the hotword, we can now pause the Trigger for a while
  102. The user can speak out loud his order during this time.
  103. """
  104. logger.debug("[MainController] Trigger callback called, switching to the next state")
  105. self.trigger_callback_called = True
  106. def stop_trigger_process(self):
  107. """
  108. The trigger has been awaken, we don't needed it anymore
  109. :return:
  110. """
  111. logger.debug("[MainController] Entering state: %s" % self.state)
  112. self.trigger_instance.stop()
  113. self.next_state()
  114. def start_order_listener_thread(self):
  115. """
  116. Start the STT engine thread
  117. """
  118. logger.debug("[MainController] Entering state: %s" % self.state)
  119. HookManager.on_start_listening()
  120. # start listening for an order
  121. self.order_listener_callback_called = False
  122. self.order_listener = OrderListener(callback=self.order_listener_callback)
  123. self.order_listener.daemon = True
  124. self.order_listener.start()
  125. self.next_state()
  126. def order_listener_callback(self, order):
  127. """
  128. Receive an order, try to retrieve it in the brain.yml to launch to attached plugins
  129. :param order: the sentence received
  130. :type order: str
  131. """
  132. logger.debug("[MainController] Order listener callback called. Order to process: %s" % order)
  133. HookManager.on_stop_listening()
  134. self.order_to_process = order
  135. self.order_listener_callback_called = True
  136. # save in kalliope memory the last order
  137. Cortex.save('kalliope_last_order', order)
  138. def analysing_order_thread(self):
  139. """
  140. Start the order analyser with the caught order to process
  141. """
  142. logger.debug("[MainController] order in analysing_order_thread %s" % self.order_to_process)
  143. SynapseLauncher.run_matching_synapse_from_order(self.order_to_process,
  144. self.brain,
  145. self.settings,
  146. is_api_call=False)
  147. # return to the state "unpausing_trigger"
  148. self.start_trigger()
  149. def set_deaf_status(self, deaf=False):
  150. """
  151. Define is the trigger is listening or not
  152. :param deaf: Boolean. If true, kalliope is trigger is paused
  153. """
  154. logger.debug("[MainController] deaf button pressed. Switch trigger process to deaf : %s" % deaf)
  155. self.is_trigger_deaf = deaf
  156. if deaf:
  157. self.trigger_instance.pause()
  158. Utils.print_info("Kalliope now deaf, trigger has been paused")
  159. HookManager.on_deaf()
  160. else:
  161. self.trigger_instance.unpause()
  162. Utils.print_info("Kalliope now listening for trigger detection")
  163. HookManager.on_undeaf()
  164. def get_deaf_status(self):
  165. """
  166. return the current state of the trigger (deaf or not)
  167. :return: Boolean
  168. """
  169. return self.is_trigger_deaf