def __init__( self, profile_name: str, system_profiles_dir: str, user_profiles_dir: str, actor_system: Optional[ActorSystem] = None, ) -> None: self._logger = logging.getLogger(self.__class__.__name__) self.profiles_dirs: List[str] = [ user_profiles_dir, system_profiles_dir ] self.profile_name = profile_name self.actor_system = actor_system self.profile = Profile(self.profile_name, system_profiles_dir, user_profiles_dir) self._logger.debug("Loaded profile from %s", self.profile.json_path) self._logger.debug("Profile files will be written to %s", self.profile.write_path()) self.defaults = Profile.load_defaults(system_profiles_dir) self.loop = asyncio.get_event_loop() self._session: Optional[ aiohttp.ClientSession] = aiohttp.ClientSession() self.dialogue_manager: Optional[RhasspyActor] = None self.download_status: typing.List[str] = []
def start_rhasspy() -> None: global core default_settings = Profile.load_defaults(profiles_dirs) # Get name of profile profile_name = ( args.profile or os.environ.get("RHASSPY_PROFILE", None) or pydash.get(default_settings, "rhasspy.default_profile", "en") ) # Load core core = RhasspyCore(profile_name, profiles_dirs) # Set environment variables os.environ["RHASSPY_BASE_DIR"] = os.getcwd() os.environ["RHASSPY_PROFILE"] = core.profile.name os.environ["RHASSPY_PROFILE_DIR"] = core.profile.write_dir() # Add profile settings from the command line extra_settings = {} for key, value in args.set: try: value = json.loads(value) except: pass logger.debug("Profile: {0}={1}".format(key, value)) extra_settings[key] = value core.profile.set(key, value) core.start() logger.info("Started")
def start_rhasspy() -> None: global core default_settings = Profile.load_defaults(profiles_dirs) # Get name of profile profile_name = args.profile \ or os.environ.get('RHASSPY_PROFILE', None) \ or pydash.get(default_settings, 'rhasspy.default_profile', 'en') # Load core core = RhasspyCore(profile_name, profiles_dirs) # Set environment variables os.environ['RHASSPY_BASE_DIR'] = os.getcwd() os.environ['RHASSPY_PROFILE'] = core.profile.name os.environ['RHASSPY_PROFILE_DIR'] = core.profile.write_dir() # Add profile settings from the command line extra_settings = {} for key, value in args.set: try: value = json.loads(value) except: pass logger.debug('Profile: {0}={1}'.format(key, value)) extra_settings[key] = value core.profile.set(key, value) core.start()
async def main() -> None: global mic_stdin_running, mic_stdin_thread # Parse command-line arguments parser = argparse.ArgumentParser(description="Rhasspy") parser.add_argument("--profile", "-p", required=True, type=str, help="Name of profile to use") parser.add_argument( "--system-profiles", type=str, help="Directory with base profile files (read only)", default=os.path.join(os.getcwd(), "profiles"), ) parser.add_argument( "--user-profiles", type=str, help="Directory with user profile files (read/write)", default=os.path.expanduser("~/.config/rhasspy/profiles"), ) parser.add_argument( "--set", "-s", nargs=2, action="append", help="Set a profile setting value", default=[], ) parser.add_argument("--debug", action="store_true", help="Print DEBUG log to console") parser.add_argument( "--no-check", action="store_true", help="Don't check profile for necessary files", ) sub_parsers = parser.add_subparsers(dest="command") sub_parsers.required = True # info info_parser = sub_parsers.add_parser("info", help="Profile information") info_parser.add_argument("--defaults", action="store_true", help="Only print default settings") sentences_parser = sub_parsers.add_parser( "sentences", help="Print profile sentences.ini") # validate # validate_parser = sub_parsers.add_parser( # "validate", help="Validate profile against schema" # ) # wav2text wav2text_parser = sub_parsers.add_parser( "wav2text", help="WAV file to text transcription") wav2text_parser.add_argument("wav_files", nargs="*", help="Paths to WAV files") # text2intent text2intent_parser = sub_parsers.add_parser("text2intent", help="Text parsed to intent") text2intent_parser.add_argument("sentences", nargs="*", help="Sentences to parse") text2intent_parser.add_argument("--handle", action="store_true", help="Pass result to intent handler") # wav2intent wav2intent_parser = sub_parsers.add_parser( "wav2intent", help="WAV file to parsed intent") wav2intent_parser.add_argument("wav_files", nargs="*", help="Paths to WAV files") wav2intent_parser.add_argument("--handle", action="store_true", help="Pass result to intent handler") # train train_parser = sub_parsers.add_parser("train", help="Re-train profile") # record # record_parser = sub_parsers.add_parser('record', help='Record test phrases for profile') # record_parser.add_argument('--directory', help='Directory to write WAV files and intent JSON files') # record-wake # record_wake_parser = sub_parsers.add_parser('record-wake', help='Record wake word examples for profile') # record_wake_parser.add_argument('--directory', help='Directory to write WAV files') # record_wake_parser.add_argument('--negative', action='store_true', help='Record negative examples (not the wake word)') # tune # tune_parser = sub_parsers.add_parser('tune', help='Tune speech acoustic model for profile') # tune_parser.add_argument('--directory', help='Directory with WAV files and intent JSON files') # tune-wake # tune_wake_parser = sub_parsers.add_parser('tune-wake', help='Tune wake acoustic model for profile') # tune_wake_parser.add_argument('--directory', help='Directory with WAV files') # test # test_parser = sub_parsers.add_parser('test', help='Test speech/intent recognizers for profile') # test_parser.add_argument('directory', help='Directory with WAV files and intent JSON files') # test-wake # test_wake_parser = sub_parsers.add_parser( # "test-wake", help="Test wake word examples for profile" # ) # test_wake_parser.add_argument("directory", help="Directory with WAV files") # test_wake_parser.add_argument( # "--threads", type=int, default=4, help="Number of threads to use" # ) # test_wake_parser.add_argument( # "--system", type=str, default=None, help="Override wake word system" # ) # mic2wav mic2wav_parser = sub_parsers.add_parser("mic2wav", help="Voice command to WAV data") mic2wav_parser.add_argument( "--timeout", type=float, default=None, help="Maximum number of seconds to record (default=profile)", ) # mic2text mic2text_parser = sub_parsers.add_parser( "mic2text", help="Voice command to text transcription") mic2text_parser.add_argument( "--timeout", type=float, default=None, help="Maximum number of seconds to record (default=profile)", ) # mic2intent mic2intent_parser = sub_parsers.add_parser( "mic2intent", help="Voice command to parsed intent") mic2intent_parser.add_argument("--stdin", action="store_true", help="Read audio data from stdin") mic2intent_parser.add_argument("--handle", action="store_true", help="Pass result to intent handler") mic2intent_parser.add_argument( "--timeout", type=float, default=None, help="Maximum number of seconds to record (default=profile)", ) # word2phonemes word2phonemes_parser = sub_parsers.add_parser( "word2phonemes", help="Get pronunciation(s) for word(s)") word2phonemes_parser.add_argument("words", nargs="*", help="Word(s) to pronounce") word2phonemes_parser.add_argument("-n", type=int, default=1, help="Maximum number of pronunciations") # word2wav word2wav_parser = sub_parsers.add_parser("word2wav", help="Pronounce word") word2wav_parser.add_argument("word", help="Word to pronounce") # wav2mqtt wav2mqtt_parser = sub_parsers.add_parser("wav2mqtt", help="Push WAV file(s) to MQTT") wav2mqtt_parser.add_argument("wav_files", nargs="*", help="Paths to WAV files") wav2mqtt_parser.add_argument( "--frames", type=int, default=480, help="WAV frames per MQTT message (default=0 for all)", ) wav2mqtt_parser.add_argument("--site-id", type=str, default="default", help="Hermes siteId (default=default)") wav2mqtt_parser.add_argument( "--silence-before", type=float, default=0, help="Seconds of silence to add before each WAV", ) wav2mqtt_parser.add_argument( "--silence-after", type=float, default=0, help="Seconds of silence to add after each WAV", ) wav2mqtt_parser.add_argument( "--pause", type=float, default=0.01, help="Seconds to wait before sending next chunk (default=0.01)", ) # text2wav text2wav_parser = sub_parsers.add_parser( "text2wav", help="Output WAV file using text to speech system") text2wav_parser.add_argument("sentence", help="Sentence to speak") # text2speech text2speech_parser = sub_parsers.add_parser( "text2speech", help="Speak sentences using text to speech system") text2speech_parser.add_argument("sentences", nargs="*", help="Sentences to speak") # sleep sleep_parser = sub_parsers.add_parser("sleep", help="Wait for wake word") # download download_parser = sub_parsers.add_parser("download", help="Download profile files") download_parser.add_argument( "--delete", action="store_true", help="Clear download cache before downloading") # check check_parser = sub_parsers.add_parser( "check", help="Check downloaded profile files") # ------------------------------------------------------------------------- args = parser.parse_args() if args.debug: logging.root.setLevel(logging.DEBUG) profiles_dirs = [args.system_profiles, args.user_profiles] logger.debug(profiles_dirs) default_settings = Profile.load_defaults(args.system_profiles) # Create rhasspy core from rhasspy.core import RhasspyCore core = RhasspyCore(args.profile, args.system_profiles, args.user_profiles) # Add profile settings from the command line extra_settings = {} for key, value in args.set: try: value = json.loads(value) except: pass logger.debug("Profile: {0}={1}".format(key, value)) extra_settings[key] = value core.profile.set(key, value) # Handle command if args.command == "info": if args.defaults: # Print default settings json.dump(core.defaults, sys.stdout, indent=4) else: # Print profile settings json.dump(core.profile.json, sys.stdout, indent=4) # elif args.command == "validate": # from cerberus import Validator # schema_path = os.path.join(os.path.dirname(__file__), "profile_schema.json") # with open(schema_path, "r") as schema_file: # v = Validator(json.load(schema_file)) # if v.validate(core.profile.json): # print("VALID") # else: # print("INVALID") # for err in v._errors: # print(err) elif args.command == "sentences": sentences_path = core.profile.read_path( core.profile.get("speech_to_text.sentences_ini", "sentences.ini")) with open(sentences_path, "r") as sentences_file: sys.stdout.write(sentences_file.read()) else: # Patch profile profile = core.profile profile.set("rhasspy.listen_on_start", False) profile.set("rhasspy.preload_profile", False) if args.command == "wav2mqtt": profile.set("mqtt.enabled", True) elif args.command in ["mic2intent"] and args.stdin: profile.set("microphone.system", "stdin") profile.set("microphone.stdin.auto_start", False) mic_stdin_running = True elif args.command == "text2wav": profile.set("sounds.system", "dummy") # Set environment variables os.environ["RHASSPY_BASE_DIR"] = os.getcwd() os.environ["RHASSPY_PROFILE"] = core.profile.name os.environ["RHASSPY_PROFILE_DIR"] = core.profile.write_dir() # Execute command command_funcs = { "wav2text": wav2text, "text2intent": text2intent, "wav2intent": wav2intent, "train": train_profile, # 'record': record, # 'record-wake': record_wake, # 'tune': tune, # 'tune-wake': tune_wake, # 'test': test, # "test-wake": test_wake, "mic2text": mic2text, "mic2intent": mic2intent, "mic2wav": mic2wav, "word2phonemes": word2phonemes, "word2wav": word2wav, "wav2mqtt": wav2mqtt, "text2wav": text2wav, "text2speech": text2speech, "sleep": sleep, "download": download, "check": check, } if not args.command in ["test-wake"]: # Automatically start core await core.start() if not args.no_check and (args.command not in ["check", "download"]): # Verify that profile has necessary files missing_files = core.check_profile() if len(missing_files) > 0: logger.fatal( f"Missing required files for {profile.name}: {missing_files.keys()}. Please run download command and try again." ) sys.exit(1) if mic_stdin_running: logger.debug("Reading audio data from stdin") mic_stdin_thread = threading.Thread(target=read_audio_stdin, args=(core, ), daemon=True) mic_stdin_thread.start() # Run command try: await command_funcs[args.command](core, profile, args) if mic_stdin_thread is not None: mic_stdin_running = False mic_stdin_thread.join() finally: await core.shutdown()