def main(): """Main entry point.""" # Parse command-line arguments parser = argparse.ArgumentParser(prog="TimeApp") hermes_cli.add_hermes_args(parser) args = parser.parse_args() # Add default MQTT arguments hermes_cli.setup_logging(args) _LOGGER.debug(args) # Create MQTT client mqtt_client = mqtt.Client() hermes_client = TimeApp(mqtt_client, site_ids=args.site_id) # Try to connect _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(mqtt_client, args) mqtt_client.loop_start() try: # Run main loop asyncio.run(hermes_client.handle_messages_async()) except KeyboardInterrupt: pass finally: mqtt_client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-tts-cli-hermes") parser.add_argument( "--tts-command", required=True, help="Text to speech command to execute with text as an argument", ) parser.add_argument( "--play-command", help="Command to play WAV data from stdin (default: publish playBytes)", ) parser.add_argument("--voices-command", help="Command to list voices (one per line)") parser.add_argument("--language", default="", help="Default language passed to command") parser.add_argument( "--temporary-wav", action="store_true", help="Pass path to temporary WAV file to TTS command", ) parser.add_argument( "--text-on-stdin", action="store_true", help="Pass input text to TTS command's stdin instead of as arguments", ) hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) # Listen for messages client = mqtt.Client() hermes = TtsHermesMqtt( client, args.tts_command, play_command=args.play_command, voices_command=args.voices_command, use_temp_wav=args.temporary_wav, text_on_stdin=args.text_on_stdin, language=args.language, site_ids=args.site_id, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def run_mqtt(args: argparse.Namespace): """Runs Hermes ASR MQTT service.""" # Convert to Paths if args.model: args.model = Path(args.model) if args.scorer: args.scorer = Path(args.scorer) if args.alphabet: args.alphabet = Path(args.alphabet) if args.base_language_model_fst: args.base_language_model_fst = Path(args.base_language_model_fst) if args.mixed_language_model_fst: args.mixed_language_model_fst = Path(args.mixed_language_model_fst) def make_transcriber(): return DeepSpeechTranscriber( args.model, args.scorer, beam_width=args.beam_width, lm_alpha=args.lm_alpha, lm_beta=args.lm_beta, ) # Listen for messages client = mqtt.Client() hermes = AsrHermesMqtt( client, transcriber_factory=make_transcriber, model_path=args.model, scorer_path=args.scorer, alphabet_path=args.alphabet, base_language_model_fst=args.base_language_model_fst, base_language_model_weight=args.base_language_model_weight, mixed_language_model_fst=args.mixed_language_model_fst, no_overwrite_train=args.no_overwrite_train, skip_seconds=args.voice_skip_seconds, min_seconds=args.voice_min_seconds, speech_seconds=args.voice_speech_seconds, silence_seconds=args.voice_silence_seconds, before_seconds=args.voice_before_seconds, vad_mode=args.voice_sensitivity, site_ids=args.site_id, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-nlu-hermes") parser.add_argument("--intent-graph", help="Path to intent graph (gzipped pickle)") parser.add_argument( "--casing", choices=["upper", "lower", "ignore"], default="ignore", help="Case transformation for input text (default: ignore)", ) parser.add_argument("--no-fuzzy", action="store_true", help="Disable fuzzy matching in graph search") parser.add_argument( "--replace-numbers", action="store_true", help="Replace digits with words in queries (75 -> seventy five)", ) parser.add_argument( "--language", help="Language/locale used for number replacement (default: en)") hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) # Convert to Paths if args.intent_graph: args.intent_graph = Path(args.intent_graph) # Listen for messages client = mqtt.Client() hermes = NluHermesMqtt( client, graph_path=args.intent_graph, word_transform=get_word_transform(args.casing), replace_numbers=args.replace_numbers, language=args.language, fuzzy=(not args.no_fuzzy), site_ids=args.site_id, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-dialogue-hermes") parser.add_argument( "--wakeword-id", action="append", help="Wakeword ID(s) to listen for (default=all)", ) parser.add_argument( "--session-timeout", type=float, default=30.0, help="Seconds before a dialogue session times out (default: 30)", ) parser.add_argument("--sound", nargs=2, action="append", help="Add WAV id/path") hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) sound_paths: typing.Dict[str, Path] = { sound[0]: Path(sound[1]) for sound in args.sound or [] } # Listen for messages client = mqtt.Client() hermes = DialogueHermesMqtt( client, site_ids=args.site_id, wakeword_ids=args.wakeword_id, session_timeout=args.session_timeout, sound_paths=sound_paths, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def run_mqtt(args: argparse.Namespace): """Runs Hermes ASR MQTT service.""" # Load transciber _LOGGER.debug( "Loading Google Cloud decoder with (credentials=%s, language_code=%s)", args.credentials, args.language_code, ) # Convert to paths args.credentials = Path(args.credentials) transcriber = GoogleCloudTranscriber( args.credentials, args.language_code, debug=args.debug, ) # Listen for messages client = mqtt.Client() hermes = AsrHermesMqtt( client, transcriber, skip_seconds=args.voice_skip_seconds, min_seconds=args.voice_min_seconds, speech_seconds=args.voice_speech_seconds, silence_seconds=args.voice_silence_seconds, before_seconds=args.voice_before_seconds, vad_mode=args.voice_sensitivity, site_ids=args.site_id, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def run(self): """Run the app. This method: - subscribes to all MQTT topics for the functions you decorated; - connects to the MQTT broker; - starts the MQTT event loop and reacts to received MQTT messages. """ # Subscribe to callbacks self._subscribe_callbacks() # Try to connect _LOGGER.debug("Connecting to %s:%s", self.args.host, self.args.port) hermes_cli.connect(self.mqtt_client, self.args) self.mqtt_client.loop_start() try: # Run main loop asyncio.run(self.handle_messages_async()) except KeyboardInterrupt: pass finally: self.mqtt_client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-speakers-cli-hermes") parser.add_argument("--play-command", required=True, help="Command to play WAV data from stdin") parser.add_argument("--list-command", help="Command to list available output devices") hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) args.play_command = shlex.split(args.play_command) if args.list_command: args.list_command = shlex.split(args.list_command) # Listen for messages client = mqtt.Client() hermes = SpeakersHermesMqtt(client, args.play_command, list_command=args.list_command, site_ids=args.site_id) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-wake-precise-hermes") parser.add_argument("--model", help="Precise model file to use (.pb)") parser.add_argument("--engine", help="Path to precise-engine executable") parser.add_argument( "--model-dir", action="append", default=[], help="Directories with Precise models", ) parser.add_argument( "--sensitivity", type=float, default=0.5, help="Model sensitivity (default: 0.5)", ) parser.add_argument( "--trigger-level", type=int, default=3, help="Activation threshold before prediction (default: 3)", ) parser.add_argument( "--wakeword-id", default="", help="Wakeword ID for model (default: use file name)", ) parser.add_argument( "--log-predictions", action="store_true", help="Log prediction probabilities for each audio chunk (very verbose)", ) parser.add_argument( "--udp-audio", nargs=3, action="append", help="Host/port/siteId for UDP audio input", ) hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) hermes: typing.Optional[WakeHermesMqtt] = None if args.model_dir: args.model_dir = [Path(d) for d in args.model_dir] # Use embedded models too args.model_dir.append(_DIR / "models") if not args.model: # Use default embedded model args.model = _DIR / "models" / "hey-mycroft-2.pb" else: maybe_model = Path(args.model) if not maybe_model.is_file(): # Resolve against model dirs for model_dir in args.model_dir: maybe_model = model_dir / args.model if maybe_model.is_file(): break args.model = maybe_model if not args.engine: # Check for environment variable if "PRECISE_ENGINE_DIR" in os.environ: args.engine = Path( os.environ["PRECISE_ENGINE_DIR"]) / "precise-engine" else: # Look in PATH maybe_engine = shutil.which("precise-engine") if maybe_engine: # Use in PATH args.engine = Path(maybe_engine) else: # Use embedded engine args.engine = _DIR / "precise-engine" / "precise-engine" _LOGGER.debug("Using engine at %s", str(args.engine)) udp_audio = [] if args.udp_audio: udp_audio = [(host, int(port), site_id) for host, port, site_id in args.udp_audio] # Listen for messages client = mqtt.Client() hermes = WakeHermesMqtt( client, args.model, args.engine, sensitivity=args.sensitivity, trigger_level=args.trigger_level, wakeword_id=args.wakeword_id, model_dirs=args.model_dir, log_predictions=args.log_predictions, udp_audio=udp_audio, site_ids=args.site_id, ) hermes.load_engine() _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop() if hermes: hermes.stop_runner()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-dialogue-hermes") parser.add_argument( "--wakeword-id", action="append", help="Wakeword ID(s) to listen for (default=all)", ) parser.add_argument( "--session-timeout", type=float, default=30.0, help="Seconds before a dialogue session times out (default: 30)", ) parser.add_argument( "--sound", nargs=2, action="append", help="Add WAV id/path or directory of WAV files", ) parser.add_argument("--no-sound", action="append", help="Disable notification sounds for site id") parser.add_argument( "--volume", type=float, help="Volume scalar for feedback sounds (0-1, default: 1)", ) parser.add_argument( "--group-separator", help= "String that separates site group from the rest of the site id (default: none)", ) parser.add_argument( "--min-asr-confidence", type=float, help= "Minimum ASR confidence/likelihood value before nluNotRecognized is produced", ) parser.add_argument( "--say-chars-per-second", type=float, default=33.0, help= "Number of characters to per second of speech for estimating TTS timeout", ) parser.add_argument( "--sound-suffix", action="append", help= "Add file extension (suffix) to search for with feedback sound path is a directory (e.g. '.wav')", ) hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) sound_paths: typing.Dict[str, Path] = { sound[0]: Path(sound[1]) for sound in args.sound or [] } if args.no_sound: _LOGGER.debug("Sound is disabled for sites %s", args.no_sound) if args.sound_suffix: args.sound_suffix = set(args.sound_suffix) # Listen for messages client = mqtt.Client() hermes = DialogueHermesMqtt( client, site_ids=args.site_id, wakeword_ids=args.wakeword_id, session_timeout=args.session_timeout, sound_paths=sound_paths, no_sound=args.no_sound, volume=args.volume, group_separator=args.group_separator, min_asr_confidence=args.min_asr_confidence, say_chars_per_second=args.say_chars_per_second, sound_suffixes=args.sound_suffix, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def run_mqtt(args: argparse.Namespace): """Runs Hermes ASR MQTT service.""" # Load transciber _LOGGER.debug( "Loading Pocketsphinx decoder with (hmm=%s, dict=%s, lm=%s, mllr=%s)", args.acoustic_model, args.dictionary, args.language_model, args.mllr_matrix, ) # Convert to paths args.acoustic_model = Path(args.acoustic_model) args.dictionary = Path(args.dictionary) args.language_model = Path(args.language_model) if args.mllr_matrix: args.mllr_matrix = Path(args.mllr_matrix) if args.base_dictionary: args.base_dictionary = [Path(p) for p in args.base_dictionary] if args.g2p_model: args.g2p_model = Path(args.g2p_model) if args.intent_graph: args.intent_graph = Path(args.intent_graph) if args.unknown_words: args.unknown_words = Path(args.unknown_words) if args.base_language_model_fst: args.base_language_model_fst = Path(args.base_language_model_fst) if args.mixed_language_model_fst: args.mixed_language_model_fst = Path(args.mixed_language_model_fst) def make_transcriber(language_model: Path): return PocketsphinxTranscriber( args.acoustic_model, args.dictionary, language_model, mllr_matrix=args.mllr_matrix, debug=args.debug, ) # Listen for messages client = mqtt.Client() hermes = AsrHermesMqtt( client, make_transcriber, dictionary=args.dictionary, language_model=args.language_model, base_dictionaries=args.base_dictionary, dictionary_word_transform=get_word_transform(args.dictionary_casing), g2p_model=args.g2p_model, g2p_word_transform=get_word_transform(args.g2p_casing), unknown_words=args.unknown_words, no_overwrite_train=args.no_overwrite_train, intent_graph_path=args.intent_graph, base_language_model_fst=args.base_language_model_fst, base_language_model_weight=args.base_language_model_weight, mixed_language_model_fst=args.mixed_language_model_fst, skip_seconds=args.voice_skip_seconds, min_seconds=args.voice_min_seconds, max_seconds=args.voice_max_seconds, speech_seconds=args.voice_speech_seconds, silence_seconds=args.voice_silence_seconds, before_seconds=args.voice_before_seconds, vad_mode=args.voice_sensitivity, silence_method=args.voice_silence_method, current_energy_threshold=args.voice_current_energy_threshold, max_energy=args.voice_max_energy, max_current_energy_ratio_threshold=args.voice_max_current_energy_ratio_threshold, site_ids=args.site_id, lang=args.lang, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop() hermes.cleanup()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-wake-raven-hermes") parser.add_argument( "--keyword", action="append", nargs="+", default=[], help="Directory with WAV templates and settings (setting-name=value)", ) parser.add_argument( "--probability-threshold", type=float, default=0.5, help="Probability above which detection occurs (default: 0.5)", ) parser.add_argument( "--distance-threshold", type=float, default=0.22, help= "Normalized dynamic time warping distance threshold for template matching (default: 0.22)", ) parser.add_argument( "--minimum-matches", type=int, default=1, help= "Number of templates that must match to produce output (default: 1)", ) parser.add_argument( "--refractory-seconds", type=float, default=2.0, help="Seconds before wake word can be activated again (default: 2)", ) parser.add_argument( "--window-shift-seconds", type=float, default=Raven.DEFAULT_SHIFT_SECONDS, help= f"Seconds to shift sliding time window on audio buffer (default: {Raven.DEFAULT_SHIFT_SECONDS})", ) parser.add_argument( "--dtw-window-size", type=int, default=5, help= "Size of band around slanted diagonal during dynamic time warping calculation (default: 5)", ) parser.add_argument( "--vad-sensitivity", type=int, choices=[1, 2, 3], default=3, help="Webrtcvad VAD sensitivity (1-3)", ) parser.add_argument( "--current-threshold", type=float, help="Debiased energy threshold of current audio frame", ) parser.add_argument( "--max-energy", type=float, help="Fixed maximum energy for ratio calculation (default: observed)", ) parser.add_argument( "--max-current-ratio-threshold", type=float, help="Threshold of ratio between max energy and current audio frame", ) parser.add_argument( "--silence-method", choices=[e.value for e in SilenceMethod], default=SilenceMethod.VAD_ONLY, help="Method for detecting silence", ) parser.add_argument( "--average-templates", action="store_true", help= "Average wakeword templates together to reduce number of calculations", ) parser.add_argument( "--udp-audio", nargs=3, action="append", help="Host/port/siteId for UDP audio input", ) parser.add_argument( "--examples-dir", help="Save positive example audio to directory as WAV files") parser.add_argument( "--examples-format", default="{keyword}/examples/%Y%m%d-%H%M%S.wav", help= "Format of positive example WAV file names using strftime (relative to examples-dir)", ) parser.add_argument( "--log-predictions", action="store_true", help="Log prediction probabilities for each audio chunk (very verbose)", ) parser.add_argument("--lang", help="Set lang in hotword detected message") hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) hermes: typing.Optional[WakeHermesMqtt] = None # ------------------------------------------------------------------------- if args.examples_dir: # Directory to save positive example WAV files args.examples_dir = Path(args.examples_dir) args.examples_dir.mkdir(parents=True, exist_ok=True) if args.keyword: missing_keywords = not any( list(Path(k[0]).glob("*.wav")) for k in args.keyword) else: missing_keywords = True if missing_keywords: args.keyword = [[_DIR / "templates"]] _LOGGER.debug( "No keywords provided. Use built-in 'okay rhasspy' templates.") # Create silence detector recorder = WebRtcVadRecorder( vad_mode=args.vad_sensitivity, silence_method=args.silence_method, current_energy_threshold=args.current_threshold, max_energy=args.max_energy, max_current_ratio_threshold=args.max_current_ratio_threshold, ) # Load audio templates ravens: typing.List[Raven] = [] for keyword_settings in args.keyword: template_dir = Path(keyword_settings[0]) wav_paths = list(template_dir.glob("*.wav")) if not wav_paths: _LOGGER.warning("No WAV files found in %s", template_dir) continue keyword_name = template_dir.name if not missing_keywords else "okay-rhasspy" # Load audio templates keyword_templates = [ Raven.wav_to_template(p, name=str(p), shift_sec=args.window_shift_seconds) for p in wav_paths ] raven_args = { "templates": keyword_templates, "keyword_name": keyword_name, "recorder": recorder, "probability_threshold": args.probability_threshold, "minimum_matches": args.minimum_matches, "distance_threshold": args.distance_threshold, "refractory_sec": args.refractory_seconds, "shift_sec": args.window_shift_seconds, "debug": args.log_predictions, } # Apply settings average_templates = args.average_templates for setting_str in keyword_settings[1:]: setting_name, setting_value = setting_str.strip().split("=", maxsplit=1) setting_name = setting_name.lower().replace("_", "-") if setting_name == "name": raven_args["keyword_name"] = setting_value elif setting_name == "probability-threshold": raven_args["probability_threshold"] = float(setting_value) elif setting_name == "minimum-matches": raven_args["minimum_matches"] = int(setting_value) elif setting_name == "average-templates": average_templates = setting_value.lower().strip() == "true" if average_templates: _LOGGER.debug("Averaging %s templates for %s", len(keyword_templates), template_dir) raven_args["templates"] = [ Template.average_templates(keyword_templates) ] # Create instance of Raven in a separate thread for keyword ravens.append(Raven(**raven_args)) udp_audio = [] if args.udp_audio: udp_audio = [(host, int(port), site_id) for host, port, site_id in args.udp_audio] # Listen for messages client = mqtt.Client() hermes = WakeHermesMqtt( client, ravens=ravens, examples_dir=args.examples_dir, examples_format=args.examples_format, udp_audio=udp_audio, site_ids=args.site_id, lang=args.lang, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop() hermes.stop()
def run_mqtt(args: argparse.Namespace): """Runs Hermes ASR MQTT service.""" # Convert to Paths if args.model_dir: args.model_dir = Path(args.model_dir) if args.graph_dir: args.graph_dir = Path(args.graph_dir) else: args.graph_dir = args.model_dir / "graph" if args.base_dictionary: args.base_dictionary = [Path(p) for p in args.base_dictionary] if args.g2p_model: args.g2p_model = Path(args.g2p_model) if args.dictionary: args.dictionary = Path(args.dictionary) if args.language_model: args.language_model = Path(args.language_model) args.language_model_type = LanguageModelType(args.language_model_type) if args.unknown_words: args.unknown_words = Path(args.unknown_words) if args.base_language_model_fst: args.base_language_model_fst = Path(args.base_language_model_fst) if args.mixed_language_model_fst: args.mixed_language_model_fst = Path(args.mixed_language_model_fst) # Load frequent words frequent_words: typing.Optional[typing.Set[str]] = None if args.frequent_words: args.frequent_words = Path(args.frequent_words) if args.frequent_words.is_file(): frequent_words = set() _LOGGER.debug("Loading frequent words from %s", args.frequent_words) with open(args.frequent_words, "r") as frequent_words_file: for line in frequent_words_file: line = line.strip() if line: frequent_words.add(line) if len(frequent_words) >= args.max_frequent_words: break # Load transciber _LOGGER.debug( "Loading Kaldi model from %s (graph=%s)", str(args.model_dir), str(args.graph_dir), ) def make_transcriber(port_num: typing.Optional[int] = None): if port_num is None: port_num = utils.get_free_port() return KaldiCommandLineTranscriber(args.model_type, args.model_dir, args.graph_dir, port_num=port_num) # Listen for messages client = mqtt.Client() hermes = AsrHermesMqtt( client, make_transcriber, model_dir=args.model_dir, graph_dir=args.graph_dir, base_dictionaries=args.base_dictionary, dictionary_path=args.dictionary, dictionary_word_transform=get_word_transform(args.dictionary_casing), language_model_path=args.language_model, language_model_type=args.language_model_type, g2p_model=args.g2p_model, g2p_word_transform=get_word_transform(args.g2p_casing), unknown_words=args.unknown_words, no_overwrite_train=args.no_overwrite_train, base_language_model_fst=args.base_language_model_fst, base_language_model_weight=args.base_language_model_weight, mixed_language_model_fst=args.mixed_language_model_fst, skip_seconds=args.voice_skip_seconds, min_seconds=args.voice_min_seconds, max_seconds=args.voice_max_seconds, speech_seconds=args.voice_speech_seconds, silence_seconds=args.voice_silence_seconds, before_seconds=args.voice_before_seconds, vad_mode=args.voice_sensitivity, silence_method=args.voice_silence_method, current_energy_threshold=args.voice_current_energy_threshold, max_energy=args.voice_max_energy, max_current_energy_ratio_threshold=args. voice_max_current_energy_ratio_threshold, reuse_transcribers=args.reuse_transcribers, sil_phone=args.sil_phone, spn_phone=args.spn_phone, allow_unknown_words=args.allow_unknown_words, frequent_words=frequent_words, unknown_words_probability=args.unknown_words_probability, unknown_token=args.unknown_token, max_unknown_words=args.max_unknown_words, silence_probability=args.silence_probability, cancel_word=args.cancel_word, cancel_probability=args.cancel_probability, site_ids=args.site_id, lang=args.lang, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-wake-porcupine-hermes") parser.add_argument( "--keyword", required=True, action="append", help="Path(s) to one or more Porcupine keyword file(s) (.ppn)", ) parser.add_argument( "--keyword-dir", action="append", default=[], help="Path to directory with keyword files", ) parser.add_argument("--library", help="Path to Porcupine shared library (.so)") parser.add_argument("--model", help="Path to Porcupine model (.pv)") parser.add_argument( "--wakeword-id", action="append", help="Wakeword IDs of each keyword (default: use file name)", ) parser.add_argument( "--sensitivity", action="append", help="Sensitivities of keywords (default: 0.5)", ) parser.add_argument("--stdin-audio", action="store_true", help="Read WAV audio from stdin") parser.add_argument( "--udp-audio", nargs=3, action="append", help="Host/port/siteId for UDP audio input", ) hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) # Load porcupine sensitivities = [ float(ks[1]) for ks in itertools.zip_longest( args.keyword, args.sensitivity or [], fillvalue=0.5) ] machine = platform.machine() if args.keyword_dir: args.keyword_dir = [Path(d) for d in args.keyword_dir] # Add embedded keywords too keyword_base = _DIR / "porcupine" / "resources" / "keyword_files" if machine in ["armv6l", "armv7l", "armv8"]: # Raspberry Pi args.keyword_dir.append(keyword_base / "raspberrypi") else: # Desktop/server args.keyword_dir.append(keyword_base / "linux") # Resolve all keyword files against keyword dirs for i, keyword in enumerate(args.keyword): maybe_keyword = Path(keyword) if not maybe_keyword.is_file(): # Try to resolve agains keyword dirs for keyword_dir in args.keyword_dir: maybe_keyword = keyword_dir / keyword if maybe_keyword.is_file(): # Overwrite with resolved path args.keyword[i] = str(maybe_keyword) if not args.library: # Use embedded library lib_dir = os.path.join(_DIR, "porcupine", "lib") if machine == "armv6l": # Pi 0/1 lib_dir = os.path.join(lib_dir, "raspberry-pi", "arm11") elif machine in ["armv7l", "armv8"]: # Pi 2 uses Cortex A7 # Pi 3 uses Cortex A53 # Pi 4 uses Cortex A72 cpu_model = guess_cpu_model() _LOGGER.debug("Guessing you have an ARM %s", cpu_model) lib_dir = os.path.join(lib_dir, "raspberry-pi", str(cpu_model.value)) else: # Assume x86_64 lib_dir = os.path.join(lib_dir, "linux", "x86_64") args.library = os.path.join(lib_dir, "libpv_porcupine.so") if not args.model: # Use embedded model args.model = os.path.join(_DIR, "porcupine", "lib", "common", "porcupine_params.pv") _LOGGER.debug( "Loading porcupine (kw=%s, kwdirs=%s, sensitivity=%s, library=%s, model=%s)", args.keyword, [str(d) for d in args.keyword_dir], sensitivities, args.library, args.model, ) porcupine_handle = Porcupine( args.library, args.model, keyword_file_paths=[str(kw) for kw in args.keyword], sensitivities=sensitivities, ) keyword_names = [ kn[1] for kn in itertools.zip_longest( args.keyword, args.wakeword_id or [], fillvalue="") ] if args.stdin_audio: # Read WAV from stdin, detect, and exit client = None hermes = WakeHermesMqtt(client, porcupine_handle, args.keyword, keyword_names, sensitivities) if os.isatty(sys.stdin.fileno()): print("Reading WAV data from stdin...", file=sys.stderr) wav_bytes = sys.stdin.buffer.read() # Print results as JSON for result in hermes.handle_audio_frame(wav_bytes): result_dict = attr.asdict(result) json.dump(result_dict, sys.stdout) return udp_audio = [] if args.udp_audio: udp_audio = [(host, int(port), site_id) for host, port, site_id in args.udp_audio] # Listen for messages client = mqtt.Client() hermes = WakeHermesMqtt( client, porcupine_handle, args.keyword, keyword_names, sensitivities, keyword_dirs=args.keyword_dir, udp_audio=udp_audio, site_ids=args.site_id, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-rasa-nlu-hermes") parser.add_argument("--rasa-url", required=True, help="URL of Rasa NLU server") parser.add_argument("--intent-graph", help="Path to rhasspy intent graph JSON file") parser.add_argument("--examples-path", help="Path to write examples markdown file") parser.add_argument("--rasa-config", help="Path to Rasa NLU's config.yml file") parser.add_argument( "--rasa-project", default="rhasspy", help="Project name used when training Rasa NLU (default: rhasspy)", ) parser.add_argument( "--rasa-model-dir", default="models", help= "Directory name where Rasa NLU stores its model files (default: models)", ) parser.add_argument( "--rasa-language", default="en", help="Language used for Rasa NLU training (default: en)", ) parser.add_argument( "--write-graph", action="store_true", help="Write training graph to intent-graph path", ) parser.add_argument( "--casing", choices=["upper", "lower", "ignore"], default="ignore", help="Case transformation for input text (default: ignore)", ) parser.add_argument( "--replace-numbers", action="store_true", help="Replace digits with words in queries (75 -> seventy five)", ) parser.add_argument( "--number-language", help="Language/locale used for number replacement (default: en)", ) parser.add_argument("--certfile", help="SSL certificate file") parser.add_argument("--keyfile", help="SSL private key file (optional)") hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) # Convert to Paths if args.intent_graph: args.intent_graph = Path(args.intent_graph) if args.examples_path: args.examples_path = Path(args.examples_path) if args.rasa_config: args.rasa_config = Path(args.rasa_config) # Listen for messages client = mqtt.Client() hermes = NluHermesMqtt( client, args.rasa_url, graph_path=args.intent_graph, examples_md_path=args.examples_path, config_path=args.rasa_config, write_graph=args.write_graph, word_transform=get_word_transform(args.casing), replace_numbers=args.replace_numbers, number_language=args.number_language, rasa_language=args.rasa_language, rasa_project=args.rasa_project, rasa_model_dir=args.rasa_model_dir, certfile=args.certfile, keyfile=args.keyfile, site_ids=args.site_id, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-microphone-pyaudio-hermes") parser.add_argument( "--list-devices", action="store_true", help="List available input devices" ) parser.add_argument("--device-index", type=int, help="Index of microphone to use") parser.add_argument( "--sample-rate", type=int, help="Sample rate of recorded audio in hertz (e.g., 16000)", ) parser.add_argument( "--sample-width", type=int, help="Sample width of recorded audio in bytes (e.g., 2)", ) parser.add_argument( "--channels", type=int, help="Number of channels in recorded audio (e.g., 1)" ) parser.add_argument( "--output-site-id", help="If set, output audio data to a different site id" ) parser.add_argument( "--udp-audio-host", default="127.0.0.1", help="Host for UDP audio (default: 127.0.0.1)", ) parser.add_argument( "--udp-audio-port", type=int, help="Send raw audio to UDP port outside ASR listening", ) parser.add_argument( "--frames-per-buffer", type=int, default=1024, help="Number of audio frames to read from microphone at at time (default: 1024)", ) hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) if args.list_devices: # List available input devices and exit list_devices() return # Verify arguments if not args.list_devices and ( (args.sample_rate is None) or (args.sample_width is None) or (args.channels is None) ): _LOGGER.fatal("--sample-rate, --sample-width, and --channels are required") sys.exit(1) # Listen for messages client = mqtt.Client() hermes = MicrophoneHermesMqtt( client, args.sample_rate, args.sample_width, args.channels, device_index=args.device_index, site_ids=args.site_id, output_site_id=args.output_site_id, udp_audio_host=args.udp_audio_host, udp_audio_port=args.udp_audio_port, frames_per_buffer=args.frames_per_buffer, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-wake-raven-hermes") parser.add_argument( "--template-dir", help= "Directory with Raven WAV templates (default: templates in Python module)", ) parser.add_argument( "--probability-threshold", type=float, default=0.5, help="Probability above which detection occurs (default: 0.5)", ) parser.add_argument( "--distance-threshold", type=float, default=0.22, help= "Normalized dynamic time warping distance threshold for template matching (default: 0.22)", ) parser.add_argument( "--minimum-matches", type=int, default=1, help= "Number of templates that must match to produce output (default: 1)", ) parser.add_argument( "--refractory-seconds", type=float, default=2.0, help="Seconds before wake word can be activated again (default: 2)", ) parser.add_argument( "--window-shift-seconds", type=float, default=0.05, help= "Seconds to shift sliding time window on audio buffer (default: 0.05)", ) parser.add_argument( "--dtw-window-size", type=int, default=5, help= "Size of band around slanted diagonal during dynamic time warping calculation (default: 5)", ) parser.add_argument( "--vad-sensitivity", type=int, choices=[1, 2, 3], default=3, help="Webrtcvad VAD sensitivity (1-3)", ) parser.add_argument( "--current-threshold", type=float, help="Debiased energy threshold of current audio frame", ) parser.add_argument( "--max-energy", type=float, help="Fixed maximum energy for ratio calculation (default: observed)", ) parser.add_argument( "--max-current-ratio-threshold", type=float, help="Threshold of ratio between max energy and current audio frame", ) parser.add_argument( "--silence-method", choices=[e.value for e in SilenceMethod], default=SilenceMethod.VAD_ONLY, help="Method for detecting silence", ) parser.add_argument( "--average-templates", action="store_true", help= "Average wakeword templates together to reduce number of calculations", ) parser.add_argument( "--wakeword-id", default="", help="Wakeword ID for model (default: use file name)", ) parser.add_argument( "--udp-audio", nargs=3, action="append", help="Host/port/siteId for UDP audio input", ) parser.add_argument( "--log-predictions", action="store_true", help="Log prediction probabilities for each audio chunk (very verbose)", ) hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) hermes: typing.Optional[WakeHermesMqtt] = None wav_paths: typing.List[Path] = [] if args.template_dir: args.template_dir = Path(args.template_dir) if args.template_dir.is_dir(): _LOGGER.debug("Loading WAV templates from %s", args.template_dir) wav_paths = list(args.template_dir.glob("*.wav")) if not wav_paths: _LOGGER.warning("No WAV templates found!") if not wav_paths: args.template_dir = _DIR / "templates" _LOGGER.debug("Loading WAV templates from %s", args.template_dir) wav_paths = list(args.template_dir.glob("*.wav")) # Create silence detector recorder = WebRtcVadRecorder( vad_mode=args.vad_sensitivity, silence_method=args.silence_method, current_energy_threshold=args.current_threshold, max_energy=args.max_energy, max_current_ratio_threshold=args.max_current_ratio_threshold, ) # Load audio templates templates = [Raven.wav_to_template(p, name=p.name) for p in wav_paths] if args.average_templates: _LOGGER.debug("Averaging %s templates", len(templates)) templates = [Template.average_templates(templates)] raven = Raven( templates=templates, recorder=recorder, probability_threshold=args.probability_threshold, minimum_matches=args.minimum_matches, distance_threshold=args.distance_threshold, refractory_sec=args.refractory_seconds, shift_sec=args.window_shift_seconds, debug=args.log_predictions, ) udp_audio = [] if args.udp_audio: udp_audio = [(host, int(port), site_id) for host, port, site_id in args.udp_audio] # Listen for messages client = mqtt.Client() hermes = WakeHermesMqtt( client, raven=raven, minimum_matches=args.minimum_matches, wakeword_id=args.wakeword_id, udp_audio=udp_audio, site_ids=args.site_id, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-wake-snowboy-hermes") parser.add_argument( "--model", required=True, action="append", nargs="+", help="Snowboy model settings (model, sensitivity, audio_gain, apply_frontend)", ) parser.add_argument( "--model-dir", action="append", default=[], help="Directories with snowboy models", ) parser.add_argument( "--wakeword-id", action="append", help="Wakeword IDs of each keyword (default: use file name)", ) parser.add_argument( "--stdin-audio", action="store_true", help="Read WAV audio from stdin" ) parser.add_argument( "--udp-audio", nargs=3, action="append", help="Host/port/siteId for UDP audio input", ) parser.add_argument( "--udp-raw-audio", action="append", help="Site id(s) where UDP audio is raw 16Khz 16-bit mono PCM instead of WAV chunks", ) parser.add_argument( "--udp-forward-mqtt", action="append", help="Site id(s) to forward audio to MQTT after detection", ) parser.add_argument("--lang", help="Set lang in hotword detected message") hermes_cli.add_hermes_args(parser) args = parser.parse_args() # logging.basicConfig wouldn't work if a handler already existed. # snowboy must mess with logging, so this resets it. logging.getLogger().handlers = [] hermes_cli.setup_logging(args) _LOGGER.debug(args) if args.model_dir: args.model_dir = [Path(d) for d in args.model_dir] # Use embedded models too args.model_dir.append(_DIR / "models") # Load model settings models: typing.List[SnowboyModel] = [] for model_settings in args.model: model_path = Path(model_settings[0]) if not model_path.is_file(): # Resolve relative to model directories for model_dir in args.model_dir: maybe_path = model_dir / model_path.name if maybe_path.is_file(): model_path = maybe_path break _LOGGER.debug("Loading model from %s", str(model_path)) model = SnowboyModel(model_path=model_path) if len(model_settings) > 1: model.sensitivity = model_settings[1] if len(model_settings) > 2: model.audio_gain = float(model_settings[2]) if len(model_settings) > 3: model.apply_frontend = model_settings[3].strip().lower() == "true" models.append(model) wakeword_ids = [ kn[1] for kn in itertools.zip_longest( args.model, args.wakeword_id or [], fillvalue="" ) ] if args.stdin_audio: # Read WAV from stdin, detect, and exit client = None hermes = WakeHermesMqtt(client, models, wakeword_ids) for site_id in args.site_id: hermes.load_detectors(site_id) if os.isatty(sys.stdin.fileno()): print("Reading WAV data from stdin...", file=sys.stderr) wav_bytes = sys.stdin.buffer.read() # Print results as JSON for result in hermes.handle_audio_frame(wav_bytes): result_dict = dataclasses.asdict(result) json.dump(result_dict, sys.stdout, ensure_ascii=False) return udp_audio = [] if args.udp_audio: udp_audio = [ (host, int(port), site_id) for host, port, site_id in args.udp_audio ] # Listen for messages client = mqtt.Client() hermes = WakeHermesMqtt( client, models, wakeword_ids, model_dirs=args.model_dir, udp_audio=udp_audio, udp_raw_audio=args.udp_raw_audio, udp_forward_mqtt=args.udp_forward_mqtt, site_ids=args.site_id, lang=args.lang, ) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") hermes.stop() client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-fuzzywuzzy-hermes") parser.add_argument("--examples", help="Path to examples JSON file") parser.add_argument("--intent-graph", help="Path to intent graph (gzipped pickle)") parser.add_argument( "--casing", choices=["upper", "lower", "ignore"], default="ignore", help="Case transformation for input text (default: ignore)", ) parser.add_argument( "--replace-numbers", action="store_true", help="Replace digits with words in queries (75 -> seventy five)", ) parser.add_argument( "--language", help="Language/locale used for number replacement (default: en)" ) parser.add_argument( "--confidence-threshold", type=float, default=0.0, help="Minimum confidence needed before intent not recognized (default: 0)", ) hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) # Convert to Paths if args.examples: args.examples = Path(args.examples) if args.intent_graph: args.intent_graph = Path(args.intent_graph) # Listen for messages client = mqtt.Client() hermes = NluHermesMqtt( client, intent_graph_path=args.intent_graph, examples_path=args.examples, replace_numbers=args.replace_numbers, language=args.language, confidence_threshold=args.confidence_threshold, site_ids=args.site_id, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-wake-pocketsphinx-hermes") parser.add_argument( "--acoustic-model", required=True, help="Path to Pocketsphinx acoustic model directory (hmm)", ) parser.add_argument( "--dictionary", required=True, action="append", help="Path to pronunciation dictionary file(s)", ) parser.add_argument("--keyphrase", required=True, help="Keyword phrase to listen for") parser.add_argument( "--keyphrase-threshold", type=float, default=1e-40, help="Threshold for keyphrase (default: 1e-40)", ) parser.add_argument("--mllr-matrix", default=None, help="Path to tuned MLLR matrix file") parser.add_argument( "--wakeword-id", default="", help="Wakeword ID of each keyphrase (default: use keyphrase)", ) parser.add_argument( "--udp-audio", nargs=3, action="append", help="Host/port/siteId for UDP audio input", ) parser.add_argument( "--udp-raw-audio", action="append", help= "Site id(s) where UDP audio is raw 16Khz 16-bit mono PCM instead of WAV chunks", ) parser.add_argument( "--udp-forward-mqtt", action="append", help="Site id(s) to forward audio to MQTT after detection", ) parser.add_argument("--lang", help="Set lang in hotword detected message") hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) # Convert to paths args.acoustic_model = Path(args.acoustic_model) args.dictionary = [Path(d) for d in args.dictionary] if args.mllr_matrix: args.mllr_matrix = Path(args.mllr_matrix) udp_audio = [] if args.udp_audio: udp_audio = [(host, int(port), site_id) for host, port, site_id in args.udp_audio] # Listen for messages client = mqtt.Client() hermes = WakeHermesMqtt( client, args.keyphrase, args.acoustic_model, args.dictionary, wakeword_id=args.wakeword_id, keyphrase_threshold=args.keyphrase_threshold, mllr_matrix=args.mllr_matrix, udp_audio=udp_audio, udp_raw_audio=args.udp_raw_audio, udp_forward_mqtt=args.udp_forward_mqtt, site_ids=args.site_id, debug=args.debug, lang=args.lang, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") hermes.stop() client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-tts-larynx-hermes") parser.add_argument( "--voice", required=True, nargs=6, action="append", metavar=( "voice_name", "language", "tts_type", "tts_path", "vocoder_type", "vocoder_path", ), help="Load voice from TTS/vocoder model", ) parser.add_argument("--cache-dir", help="Directory to cache WAV files") parser.add_argument( "--play-command", help="Command to play WAV data from stdin (default: publish playBytes)", ) parser.add_argument("--default-voice", default="default", help="Name of default voice to use") parser.add_argument("--volume", type=float, help="Volume scale for output audio (0-1, default: 1)") parser.add_argument( "--tts-setting", nargs=3, action="append", default=[], metavar=("voice_name", "key", "value"), help="Pass key/value setting to TTS (e.g., length_scale for GlowTTS)", ) parser.add_argument( "--vocoder-setting", nargs=3, action="append", default=[], metavar=("voice_name", "key", "value"), help="Pass key/value setting to vocoder (e.g., denoiser_strength)", ) parser.add_argument( "--gruut-dir", action="append", help="Directories to search for gruut language data", ) parser.add_argument( "--optimizations", choices=["auto", "on", "off"], default="auto", help="Enable/disable Onnx optimizations (auto=disable on armv7l)", ) hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) if args.debug: logging.getLogger().setLevel(logging.DEBUG) else: logging.getLogger().setLevel(logging.INFO) _LOGGER.debug(args) # Convert to paths if args.cache_dir: args.cache_dir = Path(args.cache_dir) if args.gruut_dir: args.gruut_dir = [Path(p) for p in args.gruut_dir] setattr(args, "no_optimizations", False) if args.optimizations == "off": args.no_optimizations = True elif args.optimizations == "auto": if platform.machine() == "armv7l": # Enabling optimizations on 32-bit ARM crashes args.no_optimizations = True # Load voice details voices = {} for ( voice_name, language, tts_type, tts_path, vocoder_type, vocoder_path, ) in args.voice: voices[voice_name] = VoiceInfo( name=voice_name, language=language, tts_model_type=tts_type, tts_model_path=Path(tts_path).absolute(), vocoder_model_type=vocoder_type, vocoder_model_path=Path(vocoder_path).absolute(), ) # Load TTS/vocoder settings for voice_name, tts_key, tts_value in args.tts_setting: voice = voices.get(voice_name) if voice: tts_settings = voice.tts_settings or {} tts_settings[tts_key] = tts_value voice.tts_settings = tts_settings for voice_name, vocoder_key, vocoder_value in args.vocoder_setting: voice = voices.get(voice_name) if voice: vocoder_settings = voice.vocoder_settings or {} vocoder_settings[vocoder_key] = vocoder_value voice.vocoder_settings = vocoder_settings _LOGGER.debug(voices) # Listen for messages client = mqtt.Client() hermes = TtsHermesMqtt( client, voices=voices, default_voice=args.default_voice, cache_dir=args.cache_dir, play_command=args.play_command, volume=args.volume, gruut_dirs=args.gruut_dir, no_optimizations=args.no_optimizations, site_ids=args.site_id, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-remote-http-hermes") parser.add_argument( "--asr-url", help= "URL of remote speech to text server (e.g., http://localhost:12101/api/speech-to-text)", ) parser.add_argument("--asr-command", help="Command to execute for ASR (WAV to text)") parser.add_argument( "--asr-train-url", help="URL for training speech to text server (POST with JSON)", ) parser.add_argument("--asr-train-command", help="Command to train ASR system (JSON to stdin)") parser.add_argument( "--nlu-url", help= "URL of remote intent recognition server (e.g., http://localhost:12101/api/text-to-intent)", ) parser.add_argument("--nlu-command", help="Command to execute for NLU (text to intent)") parser.add_argument( "--nlu-train-url", help="URL for training intent recognition server (POST with JSON)", ) parser.add_argument("--nlu-train-command", help="Command to train NLU system (JSON to stdin)") parser.add_argument( "--tts-url", help= "URL of remote text to speech server (e.g., http://localhost:12101/api/text-to-speech)", ) parser.add_argument( "--wake-command", help= "Command to execute for wake word detection (raw audio to wakeword id)", ) parser.add_argument( "--wake-sample-rate", default=16000, help="Sample rate in hertz required by wake command (default: 16000)", ) parser.add_argument( "--wake-sample-width", default=2, help="Sample width in bytes required by wake command (default: 2)", ) parser.add_argument( "--wake-channels", default=1, help="Number of channels required by wake command (default: 1)", ) parser.add_argument("--handle-url", help="URL of remote intent handling server") parser.add_argument( "--handle-command", help="Command to execute for intent handling (JSON on stdin)", ) parser.add_argument( "--casing", choices=["upper", "lower", "ignore"], default="ignore", help="Case transformation for words (default: ignore)", ) parser.add_argument("--certfile", help="SSL certificate file") parser.add_argument("--keyfile", help="SSL private key file (optional)") parser.add_argument("--webhook", nargs=2, action="append", help="Topic/URL pairs for webhook(s)") # Silence detection parser.add_argument( "--voice-skip-seconds", type=float, default=0.0, help="Seconds of audio to skip before a voice command", ) parser.add_argument( "--voice-min-seconds", type=float, default=1.0, help="Minimum number of seconds for a voice command", ) parser.add_argument( "--voice-speech-seconds", type=float, default=0.3, help="Consecutive seconds of speech before start", ) parser.add_argument( "--voice-silence-seconds", type=float, default=0.5, help="Consecutive seconds of silence before stop", ) parser.add_argument( "--voice-before-seconds", type=float, default=0.5, help="Seconds to record before start", ) parser.add_argument( "--voice-sensitivity", type=int, choices=[1, 2, 3], default=3, help="VAD sensitivity (1-3)", ) hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) # Split commands if args.asr_command: args.asr_command = shlex.split(args.asr_command) if args.asr_train_command: args.asr_train_command = shlex.split(args.asr_train_command) if args.nlu_command: args.nlu_command = shlex.split(args.nlu_command) if args.nlu_train_command: args.nlu_train_command = shlex.split(args.nlu_train_command) if args.wake_command: args.wake_command = shlex.split(args.wake_command) if args.handle_command: args.handle_command = shlex.split(args.handle_command) if args.webhook: webhooks = defaultdict(list) for topic, url in args.webhook: webhooks[topic].append(url) else: webhooks = None # Listen for messages client = mqtt.Client() hermes = RemoteHermesMqtt( client, asr_url=args.asr_url, asr_train_url=args.asr_train_url, asr_command=args.asr_command, asr_train_command=args.asr_train_command, nlu_url=args.nlu_url, nlu_train_url=args.nlu_train_url, nlu_command=args.nlu_command, nlu_train_command=args.nlu_train_command, tts_url=args.tts_url, wake_command=args.wake_command, wake_sample_rate=args.wake_sample_rate, wake_sample_width=args.wake_sample_width, wake_channels=args.wake_channels, handle_url=args.handle_url, handle_command=args.handle_command, word_transform=get_word_transform(args.casing), certfile=args.certfile, keyfile=args.keyfile, webhooks=webhooks, skip_seconds=args.voice_skip_seconds, min_seconds=args.voice_min_seconds, speech_seconds=args.voice_speech_seconds, silence_seconds=args.voice_silence_seconds, before_seconds=args.voice_before_seconds, vad_mode=args.voice_sensitivity, site_ids=args.site_id, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") # Needed if using wake "command" system hermes.stop_wake_command() client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-wake-porcupine-hermes") parser.add_argument( "--keyword", required=True, action="append", help="Path(s) to one or more Porcupine keyword file(s) (.ppn)", ) parser.add_argument( "--keyword-dir", action="append", default=[], help="Path to directory with keyword files", ) parser.add_argument( "--wakeword-id", action="append", help="Wakeword IDs of each keyword (default: use file name)", ) parser.add_argument( "--sensitivity", action="append", help="Sensitivities of keywords (default: 0.5)", ) parser.add_argument("--stdin-audio", action="store_true", help="Read WAV audio from stdin") parser.add_argument( "--udp-audio", nargs=3, action="append", help="Host/port/siteId for UDP audio input", ) parser.add_argument( "--udp-raw-audio", action="append", help= "Site id(s) where UDP audio is raw 16Khz 16-bit mono PCM instead of WAV chunks", ) parser.add_argument( "--udp-forward-mqtt", action="append", help="Site id(s) to forward audio to MQTT after detection", ) parser.add_argument("--lang", help="Set lang in hotword detected message") # --- DEPRECATED (using pvporcupine now) --- parser.add_argument("--library", help="Path to Porcupine shared library (.so)") parser.add_argument("--model", help="Path to Porcupine model (.pv)") # --- DEPRECATED (using pvporcupine now) --- hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) # Load porcupine sensitivities = [ float(ks[1]) for ks in itertools.zip_longest( args.keyword, args.sensitivity or [], fillvalue=0.5) ] if args.keyword_dir: args.keyword_dir = [Path(d) for d in args.keyword_dir] # Add embedded keywords too keyword_base = Path(next(iter( pvporcupine.pv_keyword_paths("").values()))).parent args.keyword_dir.append(keyword_base) _LOGGER.debug("Keyword dirs: %s", args.keyword_dir) # Resolve all keyword files against keyword dirs for i, keyword in enumerate(args.keyword): resolved = False maybe_keyword = Path(keyword) if maybe_keyword.is_file(): resolved = True else: keyword_name = maybe_keyword.stem # Try to resolve agains keyword dirs for keyword_dir in args.keyword_dir: maybe_keyword = keyword_dir / keyword if maybe_keyword.is_file(): # Overwrite with resolved path args.keyword[i] = str(maybe_keyword) resolved = True break # porcupine.ppn => porcupine_linux.ppn for real_keyword in keyword_dir.glob(f"{keyword_name}_*"): # Overwrite with resolved path args.keyword[i] = str(real_keyword) resolved = True break if not resolved: _LOGGER.warning("Failed to resolve keyword: %s", keyword) _LOGGER.debug( "Loading porcupine (kw=%s, kwdirs=%s, sensitivity=%s)", args.keyword, [str(d) for d in args.keyword_dir], sensitivities, ) keyword_names = [ kn[1] for kn in itertools.zip_longest( args.keyword, args.wakeword_id or [], fillvalue="") ] if args.stdin_audio: # Read WAV from stdin, detect, and exit client = None hermes = WakeHermesMqtt(client, args.keyword, keyword_names, sensitivities) if os.isatty(sys.stdin.fileno()): print("Reading WAV data from stdin...", file=sys.stderr) wav_bytes = sys.stdin.buffer.read() # Print results as JSON for result in hermes.handle_audio_frame(wav_bytes): result_dict = attr.asdict(result) json.dump(result_dict, sys.stdout) return udp_audio = [] if args.udp_audio: udp_audio = [(host, int(port), site_id) for host, port, site_id in args.udp_audio] # Listen for messages client = mqtt.Client() hermes = WakeHermesMqtt( client, args.keyword, keyword_names, sensitivities, keyword_dirs=args.keyword_dir, udp_audio=udp_audio, udp_raw_audio=args.udp_raw_audio, udp_forward_mqtt=args.udp_forward_mqtt, site_ids=args.site_id, lang=args.lang, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") hermes.stop() client.loop_stop()
def main(): """Main method.""" # TODO: these should be imported as well # MAX_ODAS_SOURCES = int(config['INCOME_STREAM']['n_sources']) # CHUNK_SIZE_INCOME_STREAM = int(config['INCOME_STREAM']['chunk_size']) # Get some of the default values (config file ovveride it) SAMPLE_RATE_INCOME_STREAM = int(config['INCOME_STREAM']['sample_rate']) BYTES_PER_SAMPLE_INCOME_STREAM = int( config['INCOME_STREAM']['n_bits']) // 8 ODAS_CONFIG = (config['ODAS']['odas_config']) ODAS_RCV_CONFIG_FILE = (config['ODAS']['odas_rcv_config']) parser = argparse.ArgumentParser(prog="rhasspy-lisa-odas-hermes") parser.add_argument( "--sample-rate", type=int, default=SAMPLE_RATE_INCOME_STREAM, help="Sample rate of recorded audio in hertz (e.g., 16000)", ) parser.add_argument( "--sample-width", type=int, default=BYTES_PER_SAMPLE_INCOME_STREAM, help="Sample width of recorded audio in bytes (e.g., 2)", ) parser.add_argument( "--channels", type=int, default=1, # or MAX_ODAS_SOURCES, help="Number of channels in recorded audio (e.g. 1)") parser.add_argument( "--demux", action='store_const', const=True, default=False, help= "Stream always one channel out by selecting the one with higher priority (priority mode latest source). " "If channels is provided is then discarded and only one streamed") parser.add_argument( "--dump-detected", default=None, help= "If a proper path dir is provided a dump of the audio signal sent to Rhasspy is created. The file will contain the wave header" ) parser.add_argument( "--output-site-id", help="If set, output audio data to a different site id") parser.add_argument( "--udp-audio-host", default="127.0.0.1", help="Host for UDP audio (default: 127.0.0.1)", ) parser.add_argument( "--udp-audio-port", type=int, help="Send raw audio to UDP port outside ASR listening", ) # TODO: merge in one config all the odas params (lisa rcv + wrapper) # TODO: reload from file with filename from argparse when merged parser.add_argument( "--odas-config", default=ODAS_CONFIG, help="ODAS configuration, default is: " + str(ODAS_CONFIG), ) parser.add_argument( "--odas-rcv-config", default=ODAS_RCV_CONFIG_FILE, help="ODAS receiver configuration, default is: " + str(ODAS_RCV_CONFIG_FILE), ) hermes_cli.add_hermes_args(parser) args = parser.parse_args() # TODO: reload if args.odas_config is not null, call load_configuration(file_name): hermes_cli.setup_logging(args) _LOGGER.debug(args) # Verify arguments if (args.sample_rate is None) or (args.sample_width is None) or (args.channels is None): print("--sample-rate, --sample-width, and --channels are required") _LOGGER.fatal( "--sample-rate, --sample-width, and --channels are required") sys.exit(-1) if args.demux and args.channels > 1: print( "In demux mode mode only one channel is streamed, channels arguments ignored" ) _LOGGER.fatal( "In demux mode mode only one channel is streamed, channels arguments is ignored" ) sys.exit(-2) if args.dump_detected is not None: if not path.isdir(args.dump_detected): _LOGGER.fatal( "A non existing folder for dump the output chunks is provided, but it is not a valid folder: " + str(args.dump_detected)) sys.exit(-3) # Listen for messages client = mqtt.Client() hermes = LisaHermesMqtt(client, sample_rate=args.sample_rate, sample_width=args.sample_width, channels=args.channels, site_ids=args.site_id, output_site_id=args.output_site_id, udp_audio_host=args.udp_audio_host, udp_audio_port=args.udp_audio_port, odas_config=args.odas_config, demux=args.demux, odas_rcv_config=args.odas_rcv_config, dump_out_dir=args.dump_detected) _LOGGER.info("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop print("Ctrl-C to exit") asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: client.exit_request = True print('Ctrl-C detected') finally: _LOGGER.debug("Shutting down") client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-dialogue-hermes") parser.add_argument( "--wakeword-id", action="append", help="Wakeword ID(s) to listen for (default=all)", ) parser.add_argument( "--session-timeout", type=float, default=30.0, help="Seconds before a dialogue session times out (default: 30)", ) parser.add_argument( "--sound", nargs=2, action="append", help= "Add sound id/path (path being a WAV file or a directory containing WAV files)" ) parser.add_argument("--no-sound", action="append", help="Disable notification sounds for site id") parser.add_argument( "--volume", type=float, help="Volume scalar for feedback sounds (0-1, default: 1)", ) parser.add_argument( "--group-separator", help= "String that separates site group from the rest of the site id (default: none)", ) hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) sound_paths: typing.Dict[str, Path] = { sound[0]: Path(sound[1]) for sound in args.sound or [] } if args.no_sound: _LOGGER.debug("Sound is disabled for sites %s", args.no_sound) # Listen for messages client = mqtt.Client() hermes = DialogueHermesMqtt( client, site_ids=args.site_id, wakeword_ids=args.wakeword_id, session_timeout=args.session_timeout, sound_paths=sound_paths, no_sound=args.no_sound, volume=args.volume, group_separator=args.group_separator, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-microphone-cli-hermes") parser.add_argument("--record-command", required=True, help="Command to record raw audio data") parser.add_argument( "--sample-rate", type=int, required=True, help="Sample rate of recorded audio in hertz (e.g., 16000)", ) parser.add_argument( "--sample-width", type=int, required=True, help="Sample width of recorded audio in bytes (e.g., 2)", ) parser.add_argument( "--channels", type=int, required=True, help="Number of channels in recorded audio (e.g., 1)", ) parser.add_argument("--list-command", help="Command to list available microphones") parser.add_argument("--test-command", help="Command to test a specific microphone") parser.add_argument( "--output-site-id", help="If set, output audio data to a different site_id") parser.add_argument( "--udp-audio-host", default="127.0.0.1", help="Host for UDP audio (default: 127.0.0.1)", ) parser.add_argument( "--udp-audio-port", type=int, help="Send raw audio to UDP port outside ASR listening", ) hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) if args.list_command: args.list_command = shlex.split(args.list_command) # Listen for messages client = mqtt.Client() hermes = MicrophoneHermesMqtt( client, shlex.split(args.record_command), args.sample_rate, args.sample_width, args.channels, list_command=args.list_command, test_command=args.test_command, site_ids=args.site_id, output_site_id=args.output_site_id, udp_audio_host=args.udp_audio_host, udp_audio_port=args.udp_audio_port, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def main(): """Main method.""" hw_board = 'DummyBoard' led_pattern = 'GoogleHome' parser = argparse.ArgumentParser(prog="rhasspy-lisa-led-manager") parser.add_argument( "--hw-board", nargs='?', default=hw_board, const=hw_board, help="Select one supported board between: " + str(LedManagerHermesMqtt.get_available_hw()) + ', default is: ' + hw_board, ) parser.add_argument( "--led-pattern", nargs='?', default=led_pattern, const=led_pattern, help="Select one led pattern: " + str(LedManagerHermesMqtt.get_available_patterns()) + ', default is: ' + led_pattern, ) # parser.add_argument( # "--hw-led", # action="append", # help="Select one supported board between: " + str(LedManagerHermesMqtt.get_available_hw()), # ) # parser.add_argument( # "--session-timeout", # type=float, # default=30.0, # help="Seconds before a dialogue session times out (default: 30)", # ) # parser.add_argument("--sound", nargs=2, action="append", help="Add WAV id/path") # parser.add_argument( # "--no-sound", action="append", help="Disable notification sounds for site id" # ) hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) if args.hw_board: hw_board = args.hw_board _LOGGER.debug("Selected hw is %s", args.hw_board) else: _LOGGER.debug("Selected hw is default %s", hw_board) if args.led_pattern: led_pattern = args.led_pattern _LOGGER.debug("Selected led pattern is %s", args.led_pattern) else: _LOGGER.debug("Selected led pattern is default %s", led_pattern) # Listen for messages client = mqtt.Client() try: hermes = LedManagerHermesMqtt( client, site_ids=args.site_id, hw_led=hw_board, pattern=led_pattern, ) _LOGGER.debug("Site %s Connecting to %s:%s", args.site_id, args.host, args.port) hermes_cli.connect(client, args) client.loop_start() except LedManagerHermesMqttException as e: _LOGGER.fatal("Fatal Error creating Led Manager -> " + str(e)) return -1 try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-snips-nlu-hermes") parser.add_argument( "--language", required=True, help="Snips language (de, en, es, fr, it, ja, ko, pt_br, pt_pt, zh)", ) parser.add_argument("--engine-path", help="Path to read/write trained engine") parser.add_argument("--dataset-path", help="Path to write YAML dataset during training") parser.add_argument( "--casing", choices=["upper", "lower", "ignore"], default="ignore", help="Case transformation for input text (default: ignore)", ) parser.add_argument( "--no-overwrite-train", action="store_true", help="Don't overwrite Snips engine configuration during training", ) hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) # Convert to Paths if args.engine_path: args.engine_path = Path(args.engine_path) if args.dataset_path: args.dataset_path = Path(args.dataset_path) # Listen for messages client = mqtt.Client() hermes = NluHermesMqtt( client, snips_language=args.language, engine_path=args.engine_path, dataset_path=args.dataset_path, word_transform=get_word_transform(args.casing), no_overwrite_train=args.no_overwrite_train, site_ids=args.site_id, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def run_mqtt(args: argparse.Namespace): """Runs Hermes ASR MQTT service.""" # Convert to Paths if args.model_dir: args.model_dir = Path(args.model_dir) if args.graph_dir: args.graph_dir = Path(args.graph_dir) else: args.graph_dir = args.model_dir / "graph" if args.base_dictionary: args.base_dictionary = [Path(p) for p in args.base_dictionary] if args.g2p_model: args.g2p_model = Path(args.g2p_model) if args.dictionary: args.dictionary = Path(args.dictionary) if args.language_model: args.language_model = Path(args.language_model) args.language_model_type = LanguageModelType(args.language_model_type) if args.unknown_words: args.unknown_words = Path(args.unknown_words) if args.base_language_model_fst: args.base_language_model_fst = Path(args.base_language_model_fst) if args.mixed_language_model_fst: args.mixed_language_model_fst = Path(args.mixed_language_model_fst) # Load transciber _LOGGER.debug( "Loading Kaldi model from %s (graph=%s)", str(args.model_dir), str(args.graph_dir), ) def make_transcriber(port_num: typing.Optional[int] = None): if port_num is None: port_num = utils.get_free_port() return KaldiCommandLineTranscriber(args.model_type, args.model_dir, args.graph_dir, port_num=port_num) # Listen for messages client = mqtt.Client() hermes = AsrHermesMqtt( client, make_transcriber, model_dir=args.model_dir, graph_dir=args.graph_dir, base_dictionaries=args.base_dictionary, dictionary_path=args.dictionary, dictionary_word_transform=get_word_transform(args.dictionary_casing), language_model_path=args.language_model, language_model_type=args.language_model_type, g2p_model=args.g2p_model, g2p_word_transform=get_word_transform(args.g2p_casing), unknown_words=args.unknown_words, no_overwrite_train=args.no_overwrite_train, base_language_model_fst=args.base_language_model_fst, base_language_model_weight=args.base_language_model_weight, mixed_language_model_fst=args.mixed_language_model_fst, skip_seconds=args.voice_skip_seconds, min_seconds=args.voice_min_seconds, speech_seconds=args.voice_speech_seconds, silence_seconds=args.voice_silence_seconds, before_seconds=args.voice_before_seconds, vad_mode=args.voice_sensitivity, reuse_transcribers=args.reuse_transcribers, spn_phone=args.spn_phone, site_ids=args.site_id, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()
def main(): """Main method.""" parser = argparse.ArgumentParser(prog="rhasspy-tts-wavenet-hermes") parser.add_argument( "--credentials-json", required=True, help="Path to Google Wavenet credentials JSON file", ) parser.add_argument("--cache-dir", required=True, help="Directory to cache WAV files") parser.add_argument( "--voice", default="en-US-Wavenet-C", help="Chosen voice (default: en-US-Wavenet-C)", ) parser.add_argument( "--sample-rate", default=22050, type=int, help="Chosen sample rate of the outpt wave sample (default: 22050)", ) parser.add_argument( "--play-command", help="Command to play WAV data from stdin (default: publish playBytes)", ) parser.add_argument("--volume", type=float, help="Volume scale for output audio (0-1, default: 1)") hermes_cli.add_hermes_args(parser) args = parser.parse_args() hermes_cli.setup_logging(args) _LOGGER.debug(args) args.credentials_json = Path(args.credentials_json) args.cache_dir = Path(args.cache_dir) # Listen for messages client = mqtt.Client() hermes = TtsHermesMqtt( client, credentials_json=args.credentials_json, cache_dir=args.cache_dir, voice=args.voice, sample_rate=args.sample_rate, play_command=args.play_command, volume=args.volume, site_ids=args.site_id, ) _LOGGER.debug("Connecting to %s:%s", args.host, args.port) hermes_cli.connect(client, args) client.loop_start() try: # Run event loop asyncio.run(hermes.handle_messages_async()) except KeyboardInterrupt: pass finally: _LOGGER.debug("Shutting down") client.loop_stop()