Пример #1
0
    def forward_intent(self, intent_name: str, event_type: str,
                       slots: Dict[str, Any]):
        """Forward existing event to Home Assistant."""

        if self.handle_type == HomeAssistantHandleType.INTENT:
            # Call /api/intent/handle
            post_url = urljoin(self.hass_config["url"], "api/intent/handle")

            # Send to Home Assistant
            kwargs = hass_request_kwargs(self.hass_config, self.pem_file)
            kwargs["json"] = {"name": intent_name, "data": slots}

            if self.pem_file is not None:
                kwargs["verify"] = self.pem_file

            response = requests.post(post_url, **kwargs)
        else:
            # Send event
            post_url = urljoin(self.hass_config["url"],
                               "api/events/" + event_type)

            # Send to Home Assistant
            kwargs = hass_request_kwargs(self.hass_config, self.pem_file)
            kwargs["json"] = slots

            if self.pem_file is not None:
                kwargs["verify"] = self.pem_file

            response = requests.post(post_url, **kwargs)
            self._logger.debug("POSTed intent to %s", post_url)

        response.raise_for_status()
Пример #2
0
    def speak(self, sentence: str) -> bytes:
        """Get WAV buffer for sentence."""
        try:
            tts_url = urljoin(self.hass_config["url"], "api/tts_get_url")

            # Send to Home Assistant
            kwargs = hass_request_kwargs(self.hass_config, self.pem_file)
            kwargs["json"] = {"platform": self.platform, "message": sentence}

            if self.pem_file is not None:
                kwargs["verify"] = self.pem_file

            # POST to /api/tts_get_url
            response = requests.post(tts_url, **kwargs)
            response.raise_for_status()

            response_json = response.json()
            self._logger.debug(response_json)

            # Download MP3
            audio_url = response_json["url"]
            kwargs = hass_request_kwargs(self.hass_config, self.pem_file)

            if self.pem_file is not None:
                kwargs["verify"] = self.pem_file

            # GET audio data
            response = requests.get(audio_url, **kwargs)
            response.raise_for_status()

            audio_bytes = response.content
            self._logger.debug("Received %s byte(s) of audio data",
                               len(audio_bytes))

            # Convert to WAV
            if audio_url.endswith(".mp3"):
                lame_command = ["lame", "--decode", "-", "-"]
                self._logger.debug(lame_command)

                return subprocess.run(lame_command,
                                      input=audio_bytes,
                                      check=True,
                                      stdout=subprocess.PIPE).stdout

            # Assume WAV
            return audio_bytes
        except Exception:
            self._logger.exception("speak")
            return bytes()
Пример #3
0
    def in_started(self, message: Any, sender: RhasspyActor) -> None:
        """Handle messages in started state."""
        if isinstance(message, RecognizeIntent):
            post_url = urljoin(self.hass_config["url"],
                               "api/conversation/process")

            # Send to Home Assistant
            kwargs = hass_request_kwargs(self.hass_config, self.pem_file)
            kwargs["json"] = {"text": message.text}

            if self.pem_file is not None:
                kwargs["verify"] = self.pem_file

            # POST to /api/conversation/process
            response = requests.post(post_url, **kwargs)
            response.raise_for_status()

            response_json = response.json()

            # Extract speech
            if self.handle_speech:
                speech = pydash.get(response_json, "speech.plain.speech", "")
                if speech:
                    # Forward to TTS system
                    self._logger.debug("Handling speech")
                    self.send(sender, SpeakSentence(speech))

            # Return empty intent since conversation doesn't give it to us
            intent = empty_intent()
            intent["text"] = message.text
            intent["raw_text"] = message.text
            intent["speech_confidence"] = message.confidence
            self.send(message.receiver or sender, IntentRecognized(intent))
Пример #4
0
    def get_problems(self) -> Dict[str, Any]:
        """Get problems during startup."""
        problems: Dict[str, Any] = {}
        hass_url = self.hass_config["url"]
        try:
            url = urljoin(self.hass_config["url"], "/api/")
            kwargs = hass_request_kwargs(self.hass_config, self.pem_file)
            requests.get(url, **kwargs)
        except Exception:
            problems[
                "Can't contact server"] = f"Unable to reach your Home Assistant server at {hass_url}. Is it running?"

        return problems
Пример #5
0
    def forward_intent(self, intent_name: str, event_type: str, slots: Dict[str, Any]):
        """Forward existing event to Home Assistant."""

        if self.handle_type == HomeAssistantHandleType.INTENT:
            # Call /api/intent/handle
            post_url = urljoin(self.hass_config["url"], "api/intent/handle")

            # Send to Home Assistant
            kwargs = hass_request_kwargs(self.hass_config, self.pem_file)
            kwargs["json"] = {"name": intent_name, "data": slots}

            if self.pem_file is not None:
                kwargs["verify"] = self.pem_file

            response = requests.post(post_url, **kwargs)
            response.raise_for_status()

            intent = response.json()
            self._logger.debug(intent)

            # Check for speech
            speech_text = intent.get("speech", {}).get("plain", {}).get("speech", "")
            if speech_text and self.speech_actor:
                self.send(self.speech_actor, SpeakSentence(speech_text))
        else:
            # Send event
            post_url = urljoin(self.hass_config["url"], "api/events/" + event_type)

            # Send to Home Assistant
            kwargs = hass_request_kwargs(self.hass_config, self.pem_file)
            kwargs["json"] = slots

            if self.pem_file is not None:
                kwargs["verify"] = self.pem_file

            response = requests.post(post_url, **kwargs)
            self._logger.debug("POSTed intent to %s", post_url)

            response.raise_for_status()
Пример #6
0
    def forward_intent(self, event_type: str, slots: Dict[str, Any]):
        """Forward existing event to Home Assistant."""
        # Base URL of Home Assistant server
        post_url = urljoin(self.hass_config["url"], "api/events/" + event_type)

        # Send to Home Assistant
        kwargs = hass_request_kwargs(self.hass_config, self.pem_file)
        kwargs["json"] = slots

        if self.pem_file is not None:
            kwargs["verify"] = self.pem_file

        response = requests.post(post_url, **kwargs)
        self._logger.debug("POSTed intent to %s", post_url)
        response.raise_for_status()
Пример #7
0
    def get_problems(self) -> Dict[str, Any]:
        """Get problems at startup."""
        problems: Dict[str, Any] = {}

        if not self.platform:
            problems[
                "Missing platform name"] = "Expected Home Assistant STT platform name in speech_to_text.hass_stt.platform"

        stt_url = urljoin(self.hass_config["url"], f"api/stt/{self.platform}")
        try:
            kwargs = hass_request_kwargs(self.hass_config, self.pem_file)
            requests.get(stt_url, **kwargs)
        except Exception:
            problems[
                "Can't contact server"] = f"Unable to reach your Home Assistant STT platform at {stt_url}. Is the platform configured?"

        return problems
Пример #8
0
    def get_problems(self) -> Dict[str, Any]:
        """Get problems at startup."""
        problems: Dict[str, Any] = {}

        if not shutil.which("lame"):
            problems[
                "Missing LAME MP3 encoding"] = "LAME MP3 encoder is not installed. Try apt-get install lame"

        if not self.platform:
            problems[
                "Missing platform name"] = "Expected Home Assistant TTS platform name in text_to_speech.hass_tts.platform"

        api_url = urljoin(self.hass_config["url"], "api/")
        try:
            kwargs = hass_request_kwargs(self.hass_config, self.pem_file)
            requests.get(api_url, **kwargs)
        except Exception:
            problems[
                "Can't contact server"] = f"Unable to reach your Home Assistant at {api_url}. Is it running?"

        return problems
Пример #9
0
    def transcribe_wav(self, wav_data: bytes) -> str:
        """Get text Home Assistant STT platform."""
        try:
            assert self.platform, "Missing platform name"

            # Convert WAV to desired format
            wav_data = maybe_convert_wav(
                wav_data,
                rate=self.sample_rate,
                width=self.bit_rate,
                channels=self.channels,
            )

            stt_url = urljoin(self.hass_config["url"], f"api/stt/{self.platform}")

            # Send to Home Assistant
            kwargs = hass_request_kwargs(self.hass_config, self.pem_file)

            if self.pem_file is not None:
                kwargs["verify"] = self.pem_file

            headers = kwargs.get("headers", {})
            headers["X-Speech-Content"] = "; ".join(
                [
                    "format=wav",
                    "codec=pcm",
                    f"sample_rate={self.sample_rate}",
                    f"bit_rate={self.bit_rate}",
                    f"channel={self.channels}",
                    f"language={self.language}",
                ]
            )

            def generate_chunks() -> Iterable[bytes]:
                with io.BytesIO(wav_data) as wav_buffer:
                    with wave.open(wav_buffer, "rb") as wav_file:
                        # Send empty WAV as initial chunk (header only)
                        with io.BytesIO() as empty_wav_buffer:
                            empty_wav_file: wave.Wave_write = wave.open(
                                empty_wav_buffer, "wb"
                            )
                            with empty_wav_file:
                                empty_wav_file.setframerate(wav_file.getframerate())
                                empty_wav_file.setsampwidth(wav_file.getsampwidth())
                                empty_wav_file.setnchannels(wav_file.getnchannels())

                            yield empty_wav_buffer.getvalue()

                        # Stream chunks
                        audio_data = wav_file.readframes(wav_file.getnframes())
                        while audio_data:
                            chunk = audio_data[: self.chunk_size]
                            yield chunk
                            audio_data = audio_data[self.chunk_size :]

            # POST WAV data to STT
            response = requests.post(stt_url, data=generate_chunks(), **kwargs)
            response.raise_for_status()

            response_json = response.json()
            self._logger.debug(response_json)

            assert response_json["result"] == "success"
            return response_json["text"]

        except Exception:
            self._logger.exception("transcribe_wav")
            return ""