diff --git a/homeassistant/components/tts/__init__.py b/homeassistant/components/tts/__init__.py index 39e4702e855..c7c0b986878 100644 --- a/homeassistant/components/tts/__init__.py +++ b/homeassistant/components/tts/__init__.py @@ -321,7 +321,9 @@ class SpeechManager: else: options_key = "-" - key = KEY_PATTERN.format(msg_hash, language, options_key, engine).lower() + key = KEY_PATTERN.format( + msg_hash, language.replace("_", "-"), options_key, engine + ).lower() # Is speech already in memory if key in self.mem_cache: @@ -352,9 +354,14 @@ class SpeechManager: # Create file infos filename = f"{key}.{extension}".lower() - data = self.write_tags(filename, data, provider, message, language, options) + # Validate filename + if not _RE_VOICE_FILE.match(filename): + raise HomeAssistantError( + f"TTS filename '{filename}' from {engine} is invalid!" + ) # Save to memory + data = self.write_tags(filename, data, provider, message, language, options) self._async_store_to_memcache(key, filename, data) if cache: diff --git a/tests/components/tts/test_init.py b/tests/components/tts/test_init.py index b4cb9c67af3..a100ef22c65 100644 --- a/tests/components/tts/test_init.py +++ b/tests/components/tts/test_init.py @@ -176,6 +176,41 @@ async def test_setup_component_and_test_service_with_config_language( ).is_file() +async def test_setup_component_and_test_service_with_config_language_special( + hass, empty_cache_dir +): + """Set up the demo platform and call service with extend language.""" + import homeassistant.components.demo.tts as demo_tts + + demo_tts.SUPPORT_LANGUAGES.append("en_US") + calls = async_mock_service(hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) + + config = {tts.DOMAIN: {"platform": "demo", "language": "en_US"}} + + with assert_setup_component(1, tts.DOMAIN): + assert await async_setup_component(hass, tts.DOMAIN, config) + + await hass.services.async_call( + tts.DOMAIN, + "demo_say", + { + "entity_id": "media_player.something", + tts.ATTR_MESSAGE: "There is someone at the door.", + }, + blocking=True, + ) + assert len(calls) == 1 + assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MEDIA_TYPE_MUSIC + assert ( + calls[0].data[ATTR_MEDIA_CONTENT_ID] + == "http://example.local:8123/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491_en-us_-_demo.mp3" + ) + await hass.async_block_till_done() + assert ( + empty_cache_dir / "42f18378fd4393d18c8dd11d03fa9563c1e54491_en-us_-_demo.mp3" + ).is_file() + + async def test_setup_component_and_test_service_with_wrong_conf_language(hass): """Set up the demo platform and call service with wrong config.""" config = {tts.DOMAIN: {"platform": "demo", "language": "ru"}}