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()
Пример #2
0
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()
Пример #3
0
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()
Пример #4
0
    def __init__(
        self,
        name: str,
        parser: Optional[argparse.ArgumentParser] = None,
        mqtt_client: Optional[mqtt.Client] = None,
    ):
        """Initialize the Rhasspy Hermes app.

        Arguments:
            name: The name of this object.

            parser: An argument parser.
                If the argument is not specified, the object creates an
                argument parser itself.

            mqtt_client: An MQTT client. If the argument
                is not specified, the object creates an MQTT client itself.
        """
        if parser is None:
            parser = argparse.ArgumentParser(prog=name)
        # Add default arguments
        hermes_cli.add_hermes_args(parser)

        # Parse command-line arguments
        self.args = parser.parse_args()

        # Set up logging
        hermes_cli.setup_logging(self.args)
        _LOGGER.debug(self.args)

        # Create MQTT client
        if mqtt_client is None:
            mqtt_client = mqtt.Client()

        # Initialize HermesClient
        super().__init__(name, mqtt_client, site_ids=self.args.site_id)

        self._callbacks_hotword: List[Callable[[HotwordDetected],
                                               Awaitable[None]]] = []

        self._callbacks_intent: Dict[str,
                                     List[Callable[[NluIntent],
                                                   Awaitable[None]]], ] = {}

        self._callbacks_intent_not_recognized: List[Callable[
            [NluIntentNotRecognized], Awaitable[None]]] = []

        self._callbacks_dialogue_intent_not_recognized: List[Callable[
            [DialogueIntentNotRecognized], Awaitable[None]]] = []

        self._callbacks_topic: Dict[str, List[Callable[[TopicData, bytes],
                                                       Awaitable[None]]]] = {}

        self._callbacks_topic_regex: List[Callable[[TopicData, bytes],
                                                   Awaitable[None]]] = []

        self._additional_topic: List[str] = []
def get_args() -> argparse.Namespace:
    """Parse command-line arguments."""
    parser = argparse.ArgumentParser(prog="rhasspy-asr-google-hermes")

    parser.add_argument(
        "--credentials",
        required=True,
        help="Path to Google Cloud credentials file (json)",
    )
    parser.add_argument("--language-code",
                        required=True,
                        help="Language code for Google Cloud STT API")

    # 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)

    return parser.parse_args()
Пример #6
0
    def __init__(
        self,
        name: str,
        parser: typing.Optional[argparse.ArgumentParser] = None,
        mqtt_client: typing.Optional[mqtt.Client] = None,
    ):
        """Initialize the Rhasspy Hermes app.

        Args:
            name (str): The name of this object.

            parser (:class:`argparse.ArgumentParser`, optional): An argument parser.
                If the argument is not specified, the object creates an
                argument parser itself.

            mqtt_client (:class:`paho.mqtt.client.Client`, optional): An MQTT client. If the argument
                is not specified, the object creates an MQTT client itself.
        """
        if parser is None:
            parser = argparse.ArgumentParser(prog=name)
        # Add default arguments
        hermes_cli.add_hermes_args(parser)

        # Parse command-line arguments
        self.args = parser.parse_args()

        # Set up logging
        hermes_cli.setup_logging(self.args)
        _LOGGER.debug(self.args)

        # Create MQTT client
        if mqtt_client is None:
            mqtt_client = mqtt.Client()

        # Initialize HermesClient
        super().__init__(name, mqtt_client, site_ids=self.args.site_id)

        self._callbacks_hotword: typing.List[typing.Callable[[HotwordDetected],
                                                             None]] = []

        self._callbacks_intent: typing.Dict[str, typing.List[typing.Callable[
            [NluIntent], typing.Union[ContinueSession, EndSession]]], ] = {}

        self._callbacks_intent_not_recognized: typing.List[typing.Callable[
            [NluIntentNotRecognized], typing.Union[ContinueSession,
                                                   EndSession]]] = []

        self._callbacks_topic: typing.Dict[str, typing.List[typing.Callable[
            [TopicData, bytes], None]]] = {}

        self._callbacks_topic_regex: typing.List[typing.Callable[
            [TopicData, bytes], None]] = []

        self._additional_topic: typing.List[str] = []
Пример #7
0
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 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()
Пример #9
0
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()
Пример #10
0
def get_args() -> argparse.Namespace:
    """Parse command-line arguments."""
    parser = argparse.ArgumentParser(prog="rhasspy-asr-pocketsphinx-hermes")
    parser.add_argument(
        "--acoustic-model",
        required=True,
        help="Path to Pocketsphinx acoustic model directory (hmm)",
    )
    parser.add_argument(
        "--dictionary",
        required=True,
        help="Path to read/write pronunciation dictionary file",
    )
    parser.add_argument(
        "--dictionary-casing",
        choices=["upper", "lower", "ignore"],
        default="ignore",
        help="Case transformation for dictionary words (training, default: ignore)",
    )
    parser.add_argument(
        "--language-model",
        required=True,
        help="Path to read/write ARPA language model file",
    )
    parser.add_argument(
        "--mllr-matrix", default=None, help="Path to read tuned MLLR matrix file"
    )

    parser.add_argument(
        "--base-dictionary",
        action="append",
        help="Path(s) to base pronunciation dictionary file(s) (training)",
    )
    parser.add_argument(
        "--g2p-model",
        help="Phonetisaurus FST model for guessing word pronunciations (training)",
    )
    parser.add_argument(
        "--g2p-casing",
        choices=["upper", "lower", "ignore"],
        default="ignore",
        help="Case transformation for g2p words (training, default: ignore)",
    )
    parser.add_argument(
        "--unknown-words", help="Path to write missing words from dictionary (training)"
    )
    parser.add_argument(
        "--no-overwrite-train",
        action="store_true",
        help="Don't overwrite dictionary/language model during training",
    )
    parser.add_argument("--intent-graph", help="Path to intent graph (gzipped pickle)")

    # Mixed language modeling
    parser.add_argument(
        "--base-language-model-fst",
        help="Path to base language model FST (training, mixed)",
    )
    parser.add_argument(
        "--base-language-model-weight",
        type=float,
        default=0,
        help="Weight to give base langauge model (training, mixed)",
    )
    parser.add_argument(
        "--mixed-language-model-fst",
        help="Path to write mixed langauge model FST (training, mixed)",
    )
    parser.add_argument("--lang", help="Set lang in hotword detected message")

    # 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-max-seconds",
        type=float,
        help="Maximum 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)",
    )
    parser.add_argument(
        "--voice-silence-method",
        choices=[e.value for e in SilenceMethod],
        default=SilenceMethod.VAD_ONLY,
        help="Method used to determine if an audio frame contains silence (see rhasspy-silence)",
    )
    parser.add_argument(
        "--voice-current-energy-threshold",
        type=float,
        help="Debiased energy threshold of current audio frame (see --voice-silence-method)",
    )
    parser.add_argument(
        "--voice-max-energy",
        type=float,
        help="Fixed maximum energy for ratio calculation (default: observed, see --voice-silence-method)",
    )
    parser.add_argument(
        "--voice-max-current-energy-ratio-threshold",
        type=float,
        help="Threshold of ratio between max energy and current audio frame (see --voice-silence-method)",
    )

    hermes_cli.add_hermes_args(parser)

    return parser.parse_args()
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()
Пример #12
0
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()
Пример #13
0
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()
Пример #14
0
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."""

    # 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()
Пример #16
0
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()
Пример #17
0
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()
Пример #18
0
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-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()
Пример #20
0
    def __init__(self,
                 name: str,
                 parser: Optional[argparse.ArgumentParser] = None,
                 mqtt_client: Optional[mqtt.Client] = None,
                 **kwargs):
        """Initialize the Rhasspy Hermes app.

        Arguments:
            name: The name of this object.

            parser: An argument parser.
                If the argument is not specified, the object creates an
                argument parser itself.

            mqtt_client: An MQTT client. If the argument
                is not specified, the object creates an MQTT client itself.
        """
        if parser is None:
            parser = argparse.ArgumentParser(prog=name)
        # Add default arguments
        hermes_cli.add_hermes_args(parser)

        # overwrite argument defaults inside parser with argparse.SUPPRESS
        # so arguments that are not provided get ignored
        suppress_parser = deepcopy(parser)
        for action in suppress_parser._actions:
            action.default = argparse.SUPPRESS

        supplied_args = vars(suppress_parser.parse_args())
        default_args = vars(parser.parse_args([]))

        # Command-line arguments take precedence over the arguments of the HermesApp.__init__
        args = {**default_args, **kwargs, **supplied_args}
        self.args = argparse.Namespace(**args)

        # Set up logging
        hermes_cli.setup_logging(self.args)
        _LOGGER.debug(self.args)

        # Create MQTT client
        if mqtt_client is None:
            mqtt_client = mqtt.Client()

        # Initialize HermesClient
        # pylint: disable=no-member
        super().__init__(name, mqtt_client, site_ids=self.args.site_id)

        self._callbacks_hotword: List[Callable[[HotwordDetected],
                                               Awaitable[None]]] = []

        self._callbacks_intent: Dict[str,
                                     List[Callable[[NluIntent],
                                                   Awaitable[None]]], ] = {}

        self._callbacks_intent_not_recognized: List[Callable[
            [NluIntentNotRecognized], Awaitable[None]]] = []

        self._callbacks_dialogue_intent_not_recognized: List[Callable[
            [DialogueIntentNotRecognized], Awaitable[None]]] = []

        self._callbacks_topic: Dict[str, List[Callable[[TopicData, bytes],
                                                       Awaitable[None]]]] = {}

        self._callbacks_topic_regex: List[Callable[[TopicData, bytes],
                                                   Awaitable[None]]] = []

        self._additional_topic: List[str] = []
Пример #21
0
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()
Пример #22
0
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()
Пример #23
0
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()
Пример #24
0
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()
Пример #25
0
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()
Пример #26
0
def get_args() -> argparse.Namespace:
    """Parse command-line arguments."""
    parser = argparse.ArgumentParser(prog="rhasspy-asr-kaldi-hermes")

    # Model settings
    parser.add_argument(
        "--model-type",
        default="nnet3",
        help="Type of Kaldi model (nnet3 or gmm, default: nnet3)",
    )
    parser.add_argument("--model-dir",
                        required=True,
                        help="Path to Kaldi model directory")
    parser.add_argument(
        "--graph-dir",
        help="Path to directory with HCLG.fst (defaults to $model_dir/graph)",
    )

    # Training settings
    parser.add_argument(
        "--dictionary",
        help="Path to write pronunciation dictionary file (training)")
    parser.add_argument(
        "--dictionary-casing",
        choices=["upper", "lower", "ignore"],
        default="ignore",
        help=
        "Case transformation for dictionary words (training, default: ignore)",
    )
    parser.add_argument(
        "--language-model",
        help="Path to write ARPA language model file (training)")
    parser.add_argument(
        "--language-model-type",
        default=LanguageModelType.ARPA,
        choices=[v.value for v in LanguageModelType],
        help="Type of language model to generate (default: arpa)",
    )
    parser.add_argument(
        "--base-dictionary",
        action="append",
        help="Path(s) to base pronunciation dictionary file(s) (training)",
    )
    parser.add_argument(
        "--g2p-model",
        help=
        "Phonetisaurus FST model for guessing word pronunciations (training)",
    )
    parser.add_argument(
        "--g2p-casing",
        choices=["upper", "lower", "ignore"],
        default="ignore",
        help="Case transformation for g2p words (training, default: ignore)",
    )
    parser.add_argument(
        "--unknown-words",
        help="Path to write missing words from dictionary (training)")
    parser.add_argument(
        "--spn-phone",
        default="SPN",
        help="Spoken noise phone name used for <unk> (default: SPN)",
    )
    parser.add_argument(
        "--no-overwrite-train",
        action="store_true",
        help="Don't overwrite HCLG.fst during training",
    )
    parser.add_argument(
        "--reuse-transcribers",
        action="store_true",
        help="Don't automatically reload Kaldi model after each transcription",
    )

    # Mixed language modeling
    parser.add_argument(
        "--base-language-model-fst",
        help="Path to base language model FST (training, mixed)",
    )
    parser.add_argument(
        "--base-language-model-weight",
        type=float,
        default=0,
        help="Weight to give base langauge model (training, mixed)",
    )
    parser.add_argument(
        "--mixed-language-model-fst",
        help="Path to write mixed langauge model FST (training, mixed)",
    )

    # 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)

    return parser.parse_args()
Пример #27
0
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()
Пример #28
0
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()
Пример #29
0
def get_args() -> argparse.Namespace:
    """Parse command-line arguments."""
    parser = argparse.ArgumentParser(prog="rhasspy-asr-deepspeech-hermes")

    # Model settings
    parser.add_argument(
        "--model", required=True, help="Path to the model (protocol buffer binary file)"
    )
    parser.add_argument("--alphabet", help="Path to alphabet.txt file")
    parser.add_argument(
        "--scorer", required=True,
        help="Path to the external scorer file",
    )
    parser.add_argument(
        "--beam-width", type=int, default=500, help="Beam width for the CTC decoder"
    )
    parser.add_argument(
        "--lm-alpha", type=float, default=0.75, help="Language model weight (lm_alpha)"
    )
    parser.add_argument(
        "--lm-beta", type=float, default=1.85, help="Word insertion bonus (lm_beta)"
    )
    parser.add_argument(
        "--no-overwrite-train",
        action="store_true",
        help="Don't overwrite language model/trie during training",
    )

    # Mixed language modeling
    parser.add_argument(
        "--base-language-model-fst",
        help="Path to base language model FST (training, mixed)",
    )
    parser.add_argument(
        "--base-language-model-weight",
        type=float,
        default=0,
        help="Weight to give base langauge model (training, mixed)",
    )
    parser.add_argument(
        "--mixed-language-model-fst",
        help="Path to write mixed langauge model FST (training, mixed)",
    )

    # 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)

    return parser.parse_args()
def get_args() -> argparse.Namespace:
    """Parse command-line arguments."""
    parser = argparse.ArgumentParser(prog="rhasspy-asr-deepspeech-hermes")

    # Model settings
    parser.add_argument("--model",
                        required=True,
                        help="Path to the model (protocol buffer binary file)")
    parser.add_argument("--alphabet", help="Path to alphabet.txt file")
    parser.add_argument("--language-model",
                        help="Path to read/write ARPA language model file")
    parser.add_argument(
        "--scorer",
        help=
        "Path to the scorer package file created with native_client/generate_scorer_package",
    )
    parser.add_argument("--beam-width",
                        type=int,
                        default=500,
                        help="Beam width for the CTC decoder")
    parser.add_argument(
        "--lm-alpha",
        type=float,
        default=0.931289039105002,
        help="Language model weight (lm_alpha)",
    )
    parser.add_argument(
        "--lm-beta",
        type=float,
        default=1.1834137581510284,
        help="Word insertion bonus (lm_beta)",
    )
    parser.add_argument(
        "--no-overwrite-train",
        action="store_true",
        help="Don't overwrite language model/scorer during training",
    )

    # Mixed language modeling
    parser.add_argument(
        "--base-language-model-fst",
        help="Path to base language model FST (training, mixed)",
    )
    parser.add_argument(
        "--base-language-model-weight",
        type=float,
        default=0,
        help="Weight to give base langauge model (training, mixed)",
    )
    parser.add_argument(
        "--mixed-language-model-fst",
        help="Path to write mixed langauge model FST (training, mixed)",
    )

    parser.add_argument("--lang", help="Set lang in hotword detected message")

    # 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-max-seconds",
        type=float,
        help="Maximum 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)",
    )
    parser.add_argument(
        "--voice-silence-method",
        choices=[e.value for e in SilenceMethod],
        default=SilenceMethod.VAD_ONLY,
        help=
        "Method used to determine if an audio frame contains silence (see rhasspy-silence)",
    )
    parser.add_argument(
        "--voice-current-energy-threshold",
        type=float,
        help=
        "Debiased energy threshold of current audio frame (see --voice-silence-method)",
    )
    parser.add_argument(
        "--voice-max-energy",
        type=float,
        help=
        "Fixed maximum energy for ratio calculation (default: observed, see --voice-silence-method)",
    )
    parser.add_argument(
        "--voice-max-current-energy-ratio-threshold",
        type=float,
        help=
        "Threshold of ratio between max energy and current audio frame (see --voice-silence-method)",
    )

    hermes_cli.add_hermes_args(parser)

    return parser.parse_args()