SettingLoader.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. import logging
  2. import os
  3. from six import with_metaclass
  4. from kalliope.core.Models.RecognitionOptions import RecognitionOptions
  5. from .YAMLLoader import YAMLLoader
  6. from kalliope.core.Models.Resources import Resources
  7. from kalliope.core.Utils.Utils import Utils
  8. from kalliope.core.Models import Singleton
  9. from kalliope.core.Models.RestAPI import RestAPI
  10. from kalliope.core.Models.Settings import Settings
  11. from kalliope.core.Models.Stt import Stt
  12. from kalliope.core.Models.Trigger import Trigger
  13. from kalliope.core.Models.Player import Player
  14. from kalliope.core.Models.Tts import Tts
  15. from kalliope.core.Utils.FileManager import FileManager
  16. FILE_NAME = "settings.yml"
  17. logging.basicConfig()
  18. logger = logging.getLogger("kalliope")
  19. class SettingInvalidException(Exception):
  20. """
  21. Some data must match the expected value/type
  22. .. seealso:: Settings
  23. """
  24. pass
  25. class NullSettingException(Exception):
  26. """
  27. Some Attributes can not be Null
  28. .. seealso:: Settings
  29. """
  30. pass
  31. class SettingNotFound(Exception):
  32. """
  33. Some Attributes are missing
  34. .. seealso:: Settings
  35. """
  36. pass
  37. class SettingLoader(with_metaclass(Singleton, object)):
  38. """
  39. This Class is used to get the Settings YAML and the Settings as an object
  40. """
  41. def __init__(self, file_path=None):
  42. self.file_path = file_path
  43. if self.file_path is None:
  44. self.file_path = Utils.get_real_file_path(FILE_NAME)
  45. else:
  46. self.file_path = Utils.get_real_file_path(file_path)
  47. # if the returned file path is none, the file doesn't exist
  48. if self.file_path is None:
  49. raise SettingNotFound("Settings.yml file not found")
  50. self.yaml_config = self._get_yaml_config()
  51. self.settings = self._get_settings()
  52. def _get_yaml_config(self):
  53. """
  54. Class Methods which loads default or the provided YAML file and return it as a String
  55. :return: The loaded settings YAML
  56. :rtype: dict
  57. :Example:
  58. settings_yaml = SettingLoader.get_yaml_config(/var/tmp/settings.yml)
  59. .. warnings:: Class Method
  60. """
  61. return YAMLLoader.get_config(self.file_path)
  62. def _get_settings(self):
  63. """
  64. Class Methods which loads default or the provided YAML file and return a Settings Object
  65. :return: The loaded Settings
  66. :rtype: Settings
  67. :Example:
  68. settings = SettingLoader.get_settings(file_path="/var/tmp/settings.yml")
  69. .. seealso:: Settings
  70. .. warnings:: Class Method
  71. """
  72. # create a new setting
  73. setting_object = Settings()
  74. # Get the setting parameters
  75. settings = self._get_yaml_config()
  76. default_stt_name = self._get_default_speech_to_text(settings)
  77. default_tts_name = self._get_default_text_to_speech(settings)
  78. default_trigger_name = self._get_default_trigger(settings)
  79. default_player_name = self._get_default_player(settings)
  80. stts = self._get_stts(settings)
  81. ttss = self._get_ttss(settings)
  82. triggers = self._get_triggers(settings)
  83. players = self._get_players(settings)
  84. rest_api = self._get_rest_api(settings)
  85. cache_path = self._get_cache_path(settings)
  86. resources = self._get_resources(settings)
  87. variables = self._get_variables(settings)
  88. recognition_options = self._get_recognition_options(settings)
  89. options = self._get_options(settings)
  90. hooks = self._get_hooks(settings)
  91. # Load the setting singleton with the parameters
  92. setting_object.default_tts_name = default_tts_name
  93. setting_object.default_stt_name = default_stt_name
  94. setting_object.default_trigger_name = default_trigger_name
  95. setting_object.default_player_name = default_player_name
  96. setting_object.stts = stts
  97. setting_object.ttss = ttss
  98. setting_object.triggers = triggers
  99. setting_object.players = players
  100. setting_object.rest_api = rest_api
  101. setting_object.cache_path = cache_path
  102. setting_object.resources = resources
  103. setting_object.variables = variables
  104. setting_object.recognition_options = recognition_options
  105. setting_object.options = options
  106. setting_object.hooks = hooks
  107. return setting_object
  108. @staticmethod
  109. def _get_default_speech_to_text(settings):
  110. """
  111. Get the default speech to text defined in the settings.yml file
  112. :param settings: The YAML settings file
  113. :type settings: dict
  114. :return: the default speech to text
  115. :rtype: str
  116. :Example:
  117. default_stt_name = cls._get_default_speech_to_text(settings)
  118. .. seealso:: Stt
  119. .. raises:: NullSettingException, SettingNotFound
  120. .. warnings:: Static and Private
  121. """
  122. try:
  123. default_speech_to_text = settings["default_speech_to_text"]
  124. if default_speech_to_text is None:
  125. raise NullSettingException("Attribute default_speech_to_text is null")
  126. logger.debug("Default STT: %s" % default_speech_to_text)
  127. return default_speech_to_text
  128. except KeyError as e:
  129. raise SettingNotFound("%s setting not found" % e)
  130. @staticmethod
  131. def _get_default_text_to_speech(settings):
  132. """
  133. Get the default text to speech defined in the settings.yml file
  134. :param settings: The YAML settings file
  135. :type settings: dict
  136. :return: the default text to speech
  137. :rtype: str
  138. :Example:
  139. default_tts_name = cls._get_default_text_to_speech(settings)
  140. .. seealso:: Tts
  141. .. raises:: NullSettingException, SettingNotFound
  142. .. warnings:: Static and Private
  143. """
  144. try:
  145. default_text_to_speech = settings["default_text_to_speech"]
  146. if default_text_to_speech is None:
  147. raise NullSettingException("Attribute default_text_to_speech is null")
  148. logger.debug("Default TTS: %s" % default_text_to_speech)
  149. return default_text_to_speech
  150. except KeyError as e:
  151. raise SettingNotFound("%s setting not found" % e)
  152. @staticmethod
  153. def _get_default_trigger(settings):
  154. """
  155. Get the default trigger defined in the settings.yml file
  156. :param settings: The YAML settings file
  157. :type settings: dict
  158. :return: the default trigger
  159. :rtype: str
  160. :Example:
  161. default_trigger_name = cls._get_default_trigger(settings)
  162. .. seealso:: Trigger
  163. .. raises:: NullSettingException, SettingNotFound
  164. .. warnings:: Static and Private
  165. """
  166. try:
  167. default_trigger = settings["default_trigger"]
  168. if default_trigger is None:
  169. raise NullSettingException("Attribute default_trigger is null")
  170. logger.debug("Default Trigger name: %s" % default_trigger)
  171. return default_trigger
  172. except KeyError as e:
  173. raise SettingNotFound("%s setting not found" % e)
  174. @staticmethod
  175. def _get_default_player(settings):
  176. """
  177. Get the default player defined in the settings.yml file
  178. :param settings: The YAML settings file
  179. :type settings: dict
  180. :return: the default player
  181. :rtype: str
  182. :Example:
  183. default_player_name = cls._get_default_player(settings)
  184. .. seealso:: Player
  185. .. raises:: NullSettingException, SettingNotFound
  186. .. warnings:: Static and Private
  187. """
  188. try:
  189. default_player = settings["default_player"]
  190. if default_player is None:
  191. raise NullSettingException("Attribute default_player is null")
  192. logger.debug("Default Player name: %s" % default_player)
  193. return default_player
  194. except KeyError as e:
  195. raise SettingNotFound("%s setting not found" % e)
  196. @staticmethod
  197. def _get_stts(settings):
  198. """
  199. Return a list of stt object
  200. :param settings: The YAML settings file
  201. :type settings: dict
  202. :return: List of Stt
  203. :rtype: list
  204. :Example:
  205. stts = cls._get_stts(settings)
  206. .. seealso:: Stt
  207. .. raises:: SettingNotFound
  208. .. warnings:: Static Method and Private
  209. """
  210. try:
  211. speechs_to_text_list = settings["speech_to_text"]
  212. except KeyError:
  213. raise SettingNotFound("speech_to_text settings not found")
  214. stts = list()
  215. for speechs_to_text_el in speechs_to_text_list:
  216. if isinstance(speechs_to_text_el, dict):
  217. for stt_name in speechs_to_text_el:
  218. name = stt_name
  219. parameters = speechs_to_text_el[name]
  220. new_stt = Stt(name=name, parameters=parameters)
  221. stts.append(new_stt)
  222. else:
  223. # the stt does not have parameter
  224. new_stt = Stt(name=speechs_to_text_el, parameters=dict())
  225. stts.append(new_stt)
  226. return stts
  227. @staticmethod
  228. def _get_ttss(settings):
  229. """
  230. Return a list of stt object
  231. :param settings: The YAML settings file
  232. :type settings: dict
  233. :return: List of Ttss
  234. :rtype: list
  235. :Example:
  236. ttss = cls._get_ttss(settings)
  237. .. seealso:: Tts
  238. .. raises:: SettingNotFound
  239. .. warnings:: Static Method and Private
  240. """
  241. try:
  242. text_to_speech_list = settings["text_to_speech"]
  243. except KeyError as e:
  244. raise SettingNotFound("%s setting not found" % e)
  245. ttss = list()
  246. for text_to_speech_el in text_to_speech_list:
  247. if isinstance(text_to_speech_el, dict):
  248. for tts_name in text_to_speech_el:
  249. name = tts_name
  250. parameters = text_to_speech_el[name]
  251. new_tts = Tts(name=name, parameters=parameters)
  252. ttss.append(new_tts)
  253. else:
  254. # the neuron does not have parameter
  255. new_tts = Tts(name=text_to_speech_el)
  256. ttss.append(new_tts)
  257. return ttss
  258. @staticmethod
  259. def _get_triggers(settings):
  260. """
  261. Return a list of Trigger object
  262. :param settings: The YAML settings file
  263. :type settings: dict
  264. :return: List of Trigger
  265. :rtype: list
  266. :Example:
  267. triggers = cls._get_triggers(settings)
  268. .. seealso:: Trigger
  269. .. raises:: SettingNotFound
  270. .. warnings:: Static Method and Private
  271. """
  272. try:
  273. triggers_list = settings["triggers"]
  274. except KeyError as e:
  275. raise SettingNotFound("%s setting not found" % e)
  276. triggers = list()
  277. for trigger_el in triggers_list:
  278. if isinstance(trigger_el, dict):
  279. for trigger_name in trigger_el:
  280. name = trigger_name
  281. parameters = trigger_el[name]
  282. new_trigger = Trigger(name=name, parameters=parameters)
  283. triggers.append(new_trigger)
  284. else:
  285. # the neuron does not have parameter
  286. new_trigger = Trigger(name=trigger_el)
  287. triggers.append(new_trigger)
  288. return triggers
  289. @staticmethod
  290. def _get_players(settings):
  291. """
  292. Return a list of Player object
  293. :param settings: The YAML settings file
  294. :type settings: dict
  295. :return: List of Player
  296. :rtype: list
  297. :Example:
  298. players = cls._get_players(settings)
  299. .. seealso:: players
  300. .. raises:: SettingNotFound
  301. .. warnings:: Static Method and Private
  302. """
  303. try:
  304. players_list = settings["players"]
  305. except KeyError as e:
  306. raise SettingNotFound("%s setting not found" % e)
  307. players = list()
  308. for player_el in players_list:
  309. if isinstance(player_el, dict):
  310. for player_name in player_el:
  311. name = player_name
  312. parameters = player_el[name]
  313. new_player = Player(name=name, parameters=parameters)
  314. players.append(new_player)
  315. else:
  316. # the player does not have parameters
  317. new_player = Player(name=player_el)
  318. players.append(new_player)
  319. return players
  320. @staticmethod
  321. def _get_rest_api(settings):
  322. """
  323. Return the settings of the RestApi
  324. :param settings: The YAML settings file
  325. :type settings: dict
  326. :return: the RestApi object
  327. :rtype: RestApi
  328. :Example:
  329. rest_api = cls._get_rest_api(settings)
  330. .. seealso:: RestApi
  331. .. raises:: SettingNotFound, NullSettingException, SettingInvalidException
  332. .. warnings:: Class Method and Private
  333. """
  334. try:
  335. rest_api = settings["rest_api"]
  336. except KeyError as e:
  337. raise SettingNotFound("%s setting not found" % e)
  338. if rest_api is not None:
  339. try:
  340. password_protected = rest_api["password_protected"]
  341. if password_protected is None:
  342. raise NullSettingException("password_protected setting cannot be null")
  343. login = rest_api["login"]
  344. password = rest_api["password"]
  345. if password_protected:
  346. if login is None:
  347. raise NullSettingException("login setting cannot be null if password_protected is True")
  348. if password is None:
  349. raise NullSettingException("password setting cannot be null if password_protected is True")
  350. active = rest_api["active"]
  351. if active is None:
  352. raise NullSettingException("active setting cannot be null")
  353. port = rest_api["port"]
  354. if port is None:
  355. raise NullSettingException("port setting cannot be null")
  356. # check that the port in an integer
  357. try:
  358. port = int(port)
  359. except ValueError:
  360. raise SettingInvalidException("port must be an integer")
  361. # check the port is a valid port number
  362. if not 1024 <= port <= 65535:
  363. raise SettingInvalidException("port must be in range 1024-65535")
  364. # check the CORS request settings
  365. allowed_cors_origin = False
  366. if "allowed_cors_origin" in rest_api:
  367. allowed_cors_origin = rest_api["allowed_cors_origin"]
  368. except KeyError as e:
  369. raise SettingNotFound("%s settings not found" % e)
  370. # config ok, we can return the rest api object
  371. rest_api_obj = RestAPI(password_protected=password_protected, login=login, password=password,
  372. active=active, port=port, allowed_cors_origin=allowed_cors_origin)
  373. return rest_api_obj
  374. else:
  375. raise NullSettingException("rest_api settings cannot be null")
  376. @staticmethod
  377. def _get_cache_path(settings):
  378. """
  379. Return the path where to store the cache
  380. :param settings: The YAML settings file
  381. :type settings: dict
  382. :return: the path to store the cache
  383. :rtype: String
  384. :Example:
  385. cache_path = cls._get_cache_path(settings)
  386. .. seealso::
  387. .. raises:: SettingNotFound, NullSettingException, SettingInvalidException
  388. .. warnings:: Class Method and Private
  389. """
  390. try:
  391. cache_path = settings["cache_path"]
  392. except KeyError as e:
  393. raise SettingNotFound("%s setting not found" % e)
  394. if cache_path is None:
  395. raise NullSettingException("cache_path setting cannot be null")
  396. # test if that path is usable
  397. if FileManager.is_path_exists_or_creatable(cache_path):
  398. return cache_path
  399. else:
  400. raise SettingInvalidException("The cache_path seems to be invalid: %s" % cache_path)
  401. @staticmethod
  402. def _get_resources(settings):
  403. """
  404. Return a resources object that contains path of third party modules
  405. :param settings: The YAML settings file
  406. :type settings: dict
  407. :return: the resource object
  408. :rtype: Resources
  409. :Example:
  410. resource_directory = cls._get_resource_dir(settings)
  411. .. seealso::
  412. .. raises:: SettingNotFound, NullSettingException, SettingInvalidException
  413. .. warnings:: Class Method and Private
  414. """
  415. # return an empty resource object anyway
  416. resource_object = Resources()
  417. try:
  418. resource_dir = settings["resource_directory"]
  419. logger.debug("Resource directory synapse: %s" % resource_dir)
  420. neuron_folder = None
  421. stt_folder = None
  422. tts_folder = None
  423. trigger_folder = None
  424. signal_folder = None
  425. if "neuron" in resource_dir:
  426. neuron_folder = resource_dir["neuron"]
  427. if os.path.exists(neuron_folder):
  428. logger.debug("[SettingLoader] Neuron resource folder path loaded: %s" % neuron_folder)
  429. resource_object.neuron_folder = neuron_folder
  430. else:
  431. raise SettingInvalidException("The path %s does not exist on the system" % neuron_folder)
  432. if "stt" in resource_dir:
  433. stt_folder = resource_dir["stt"]
  434. if os.path.exists(stt_folder):
  435. logger.debug("[SettingLoader] STT resource folder path loaded: %s" % stt_folder)
  436. resource_object.stt_folder = stt_folder
  437. else:
  438. raise SettingInvalidException("The path %s does not exist on the system" % stt_folder)
  439. if "tts" in resource_dir:
  440. tts_folder = resource_dir["tts"]
  441. if os.path.exists(tts_folder):
  442. logger.debug("[SettingLoader] TTS resource folder path loaded: %s" % tts_folder)
  443. resource_object.tts_folder = tts_folder
  444. else:
  445. raise SettingInvalidException("The path %s does not exist on the system" % tts_folder)
  446. if "trigger" in resource_dir:
  447. trigger_folder = resource_dir["trigger"]
  448. if os.path.exists(trigger_folder):
  449. logger.debug("[SettingLoader] Trigger resource folder path loaded: %s" % trigger_folder)
  450. resource_object.trigger_folder = trigger_folder
  451. else:
  452. raise SettingInvalidException("The path %s does not exist on the system" % trigger_folder)
  453. if "signal" in resource_dir:
  454. signal_folder = resource_dir["signal"]
  455. if os.path.exists(signal_folder):
  456. logger.debug("[SettingLoader] Signal resource folder path loaded: %s" % signal_folder)
  457. resource_object.signal_folder = signal_folder
  458. else:
  459. raise SettingInvalidException("The path %s does not exist on the system" % signal_folder)
  460. if neuron_folder is None \
  461. and stt_folder is None \
  462. and tts_folder is None \
  463. and trigger_folder is None \
  464. and signal_folder is None:
  465. raise SettingInvalidException("No required folder has been provided in the setting resource_directory. "
  466. "Define : \'neuron\' or/and \'stt\' or/and \'tts\' or/and \'trigger\' "
  467. "or/and \'signal\'")
  468. except KeyError:
  469. logger.debug("Resource directory not found in settings")
  470. return resource_object
  471. return resource_object
  472. @staticmethod
  473. def _get_variables(settings):
  474. """
  475. Return the dict of variables from the settings.
  476. :param settings: The YAML settings file
  477. :return: dict
  478. """
  479. variables = dict()
  480. try:
  481. variables_files_name = settings["var_files"]
  482. # In case files are declared in settings.yml, make sure kalliope can access them.
  483. for files in variables_files_name:
  484. var = Utils.get_real_file_path(files)
  485. if var is None:
  486. raise SettingInvalidException("Variables file %s not found" % files)
  487. else:
  488. variables.update(YAMLLoader.get_config(var))
  489. return variables
  490. except KeyError:
  491. # User does not provide this settings
  492. return dict()
  493. @staticmethod
  494. def _get_recognition_options(settings):
  495. """
  496. return the value of stt_threshold
  497. :param settings: The loaded YAML settings file
  498. :return: integer or 1200 by default if not set
  499. """
  500. recognition_options = RecognitionOptions()
  501. try:
  502. recognition_options_dict = settings["recognition_options"]
  503. if "energy_threshold" in recognition_options_dict:
  504. recognition_options.energy_threshold = recognition_options_dict["energy_threshold"]
  505. logger.debug("[SettingsLoader] energy_threshold set to %s" % recognition_options.energy_threshold)
  506. if "adjust_for_ambient_noise_second" in recognition_options_dict:
  507. recognition_options.adjust_for_ambient_noise_second = recognition_options_dict["adjust_for_ambient_noise_second"]
  508. logger.debug("[SettingsLoader] adjust_for_ambient_noise_second set to %s"
  509. % recognition_options.adjust_for_ambient_noise_second)
  510. return recognition_options
  511. except KeyError:
  512. logger.debug("[SettingsLoader] no recognition_options defined. Set to default")
  513. logger.debug("[SettingsLoader] recognition_options: %s" % str(recognition_options))
  514. return recognition_options
  515. @staticmethod
  516. def _get_options(settings):
  517. """
  518. Return the start options settings
  519. :param settings: The YAML settings file
  520. :type settings: dict
  521. :return: A dict containing the start options
  522. :rtype: dict
  523. """
  524. options = dict()
  525. deaf = False
  526. mute = False
  527. try:
  528. options = settings["options"]
  529. except KeyError:
  530. options = None
  531. if options is not None:
  532. if options['deaf']:
  533. deaf = options['deaf']
  534. if options['mute']:
  535. mute = options['mute']
  536. options['deaf'] = deaf
  537. options['mute'] = mute
  538. logger.debug("Start options: %s" % options)
  539. return options
  540. @staticmethod
  541. def _get_hooks(settings):
  542. """
  543. Return hooks settings
  544. :param settings: The YAML settings file
  545. :return: A dict containing hooks
  546. :rtype: dict
  547. """
  548. try:
  549. hooks = settings["hooks"]
  550. except KeyError:
  551. # if the user haven't set any hooks we define an empty dict
  552. hooks = dict()
  553. all_hook = [
  554. "on_start",
  555. "on_waiting_for_trigger",
  556. "on_triggered",
  557. "on_start_listening",
  558. "on_stop_listening",
  559. "on_order_found",
  560. "on_order_not_found",
  561. "on_deaf",
  562. "on_undeaf",
  563. "on_start_speaking",
  564. "on_stop_speaking"
  565. ]
  566. for key in all_hook:
  567. if key not in hooks:
  568. hooks[key] = None
  569. return hooks