Somewhat fancy voice command recognition software
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

blather.py 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. #!/usr/bin/env python3
  2. # This is part of Kaylee
  3. # -- this code is licensed GPLv3
  4. # Copyright 2013 Jezra
  5. # Copyright 2015 Clayton G. Hobbs
  6. from __future__ import print_function
  7. import sys
  8. import signal
  9. import hashlib
  10. import os.path
  11. import subprocess
  12. from gi.repository import GObject, GLib
  13. import json
  14. from recognizer import Recognizer
  15. from config import Config
  16. class Blather:
  17. def __init__(self):
  18. self.ui = None
  19. self.options = {}
  20. ui_continuous_listen = False
  21. self.continuous_listen = False
  22. self.commands = {}
  23. # Load configuration
  24. self.config = Config()
  25. self.options = vars(self.config.options)
  26. # Read the commands
  27. self.read_commands()
  28. if self.options['interface'] != None:
  29. if self.options['interface'] == "g":
  30. from gtkui import UI
  31. elif self.options['interface'] == "gt":
  32. from gtktrayui import UI
  33. else:
  34. print("no GUI defined")
  35. sys.exit()
  36. self.ui = UI(self.options, self.options['continuous'])
  37. self.ui.connect("command", self.process_command)
  38. # Can we load the icon resource?
  39. icon = self.load_resource("icon.png")
  40. if icon:
  41. self.ui.set_icon_active_asset(icon)
  42. # Can we load the icon_inactive resource?
  43. icon_inactive = self.load_resource("icon_inactive.png")
  44. if icon_inactive:
  45. self.ui.set_icon_inactive_asset(icon_inactive)
  46. if self.options['history']:
  47. self.history = []
  48. # Update the language if necessary
  49. self.update_language()
  50. # Create the recognizer
  51. self.recognizer = Recognizer(self.config)
  52. self.recognizer.connect('finished', self.recognizer_finished)
  53. def read_commands(self):
  54. # Read the commands file
  55. file_lines = open(self.config.command_file)
  56. strings = open(self.config.strings_file, "w")
  57. for line in file_lines:
  58. # Trim the white spaces
  59. line = line.strip()
  60. # If the line has length and the first char isn't a hash
  61. if len(line) and line[0] != "#":
  62. # This is a parsible line
  63. (key, value) = line.split(":", 1)
  64. self.commands[key.strip().lower()] = value.strip()
  65. strings.write(key.strip() + "\n")
  66. # Close the strings file
  67. strings.close()
  68. def log_history(self, text):
  69. if self.options['history']:
  70. self.history.append(text)
  71. if len(self.history) > self.options['history']:
  72. # Pop off the first item
  73. self.history.pop(0)
  74. # Open and truncate the blather history file
  75. hfile = open(self.config.history_file, "w")
  76. for line in self.history:
  77. hfile.write(line + "\n")
  78. # Close the file
  79. hfile.close()
  80. def update_language(self):
  81. """Update the language if its hash has changed"""
  82. # Load the stored hash from the hash file
  83. try:
  84. with open(self.config.hash_file, 'r') as f:
  85. hashes = json.load(f)
  86. stored_hash = hashes['language']
  87. except (IOError, KeyError, TypeError):
  88. # No stored hash
  89. stored_hash = ''
  90. # Calculate the hash the language file has right now
  91. hasher = hashlib.sha256()
  92. with open(self.config.strings_file, 'rb') as sfile:
  93. buf = sfile.read()
  94. hasher.update(buf)
  95. new_hash = hasher.hexdigest()
  96. # If the hashes differ
  97. if stored_hash != new_hash:
  98. # Update the language
  99. # FIXME: Do this with Python, not Bash
  100. self.run_command('./language_updater.sh')
  101. # Store the new hash
  102. new_hashes = {'language': new_hash}
  103. with open(self.config.hash_file, 'w') as f:
  104. json.dump(new_hashes, f)
  105. def run_command(self, cmd):
  106. """Print the command, then run it"""
  107. print(cmd)
  108. subprocess.call(cmd, shell=True)
  109. def recognizer_finished(self, recognizer, text):
  110. t = text.lower()
  111. # Is there a matching command?
  112. if t in self.commands:
  113. # Run the valid_sentence_command if there is a valid sentence command
  114. if self.options['valid_sentence_command']:
  115. subprocess.call(self.options['valid_sentence_command'], shell=True)
  116. cmd = self.commands[t]
  117. # Should we be passing words?
  118. if self.options['pass_words']:
  119. cmd += " " + t
  120. self.run_command(cmd)
  121. else:
  122. self.run_command(cmd)
  123. self.log_history(text)
  124. else:
  125. # Run the invalid_sentence_command if there is an invalid sentence command
  126. if self.options['invalid_sentence_command']:
  127. subprocess.call(self.options['invalid_sentence_command'], shell=True)
  128. print("no matching command {0}".format(t))
  129. # If there is a UI and we are not continuous listen
  130. if self.ui:
  131. if not self.continuous_listen:
  132. # Stop listening
  133. self.recognizer.pause()
  134. # Let the UI know that there is a finish
  135. self.ui.finished(t)
  136. def run(self):
  137. if self.ui:
  138. self.ui.run()
  139. else:
  140. blather.recognizer.listen()
  141. def quit(self):
  142. sys.exit()
  143. def process_command(self, UI, command):
  144. print(command)
  145. if command == "listen":
  146. self.recognizer.listen()
  147. elif command == "stop":
  148. self.recognizer.pause()
  149. elif command == "continuous_listen":
  150. self.continuous_listen = True
  151. self.recognizer.listen()
  152. elif command == "continuous_stop":
  153. self.continuous_listen = False
  154. self.recognizer.pause()
  155. elif command == "quit":
  156. self.quit()
  157. def load_resource(self, string):
  158. local_data = os.path.join(os.path.dirname(__file__), 'data')
  159. paths = ["/usr/share/blather/", "/usr/local/share/blather", local_data]
  160. for path in paths:
  161. resource = os.path.join(path, string)
  162. if os.path.exists(resource):
  163. return resource
  164. # If we get this far, no resource was found
  165. return False
  166. if __name__ == "__main__":
  167. # Make our blather object
  168. blather = Blather()
  169. # Init gobject threads
  170. GObject.threads_init()
  171. # We want a main loop
  172. main_loop = GObject.MainLoop()
  173. # Handle sigint
  174. signal.signal(signal.SIGINT, signal.SIG_DFL)
  175. # Run the blather
  176. blather.run()
  177. # Start the main loop
  178. try:
  179. main_loop.run()
  180. except:
  181. print("time to quit")
  182. main_loop.quit()
  183. sys.exit()