From c4eddc8d11d251663f44f9e8cef8f6cf4842a8ac Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 30 Apr 2025 16:57:02 -0400 Subject: [PATCH] Ensure legacy TTS providers are hidden if entity exists (#143992) --- homeassistant/components/cloud/tts.py | 10 +++-- homeassistant/components/tts/__init__.py | 3 ++ homeassistant/components/tts/legacy.py | 1 + homeassistant/components/tts/media_source.py | 21 +++++++---- tests/components/tts/test_init.py | 39 ++++++++++++++++++++ tests/components/tts/test_media_source.py | 7 ++++ 6 files changed, 71 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/cloud/tts.py b/homeassistant/components/cloud/tts.py index ca3e0719998..85ca599fa87 100644 --- a/homeassistant/components/cloud/tts.py +++ b/homeassistant/components/cloud/tts.py @@ -418,9 +418,11 @@ class CloudTTSEntity(TextToSpeechEntity): language=language, voice=options.get( ATTR_VOICE, - self._voice - if language == self._language - else DEFAULT_VOICES[language], + ( + self._voice + if language == self._language + else DEFAULT_VOICES[language] + ), ), gender=options.get(ATTR_GENDER), ), @@ -435,6 +437,8 @@ class CloudTTSEntity(TextToSpeechEntity): class CloudProvider(Provider): """Home Assistant Cloud speech API provider.""" + has_entity = True + def __init__(self, cloud: Cloud[CloudClient]) -> None: """Initialize cloud provider.""" self.cloud = cloud diff --git a/homeassistant/components/tts/__init__.py b/homeassistant/components/tts/__init__.py index 44badaa73d2..b279af31803 100644 --- a/homeassistant/components/tts/__init__.py +++ b/homeassistant/components/tts/__init__.py @@ -1212,6 +1212,9 @@ def websocket_list_engines( if entity.platform: entity_domains.add(entity.platform.platform_name) for engine_id, provider in hass.data[DATA_TTS_MANAGER].providers.items(): + if provider.has_entity: + continue + provider_info = { "engine_id": engine_id, "name": provider.name, diff --git a/homeassistant/components/tts/legacy.py b/homeassistant/components/tts/legacy.py index 6f0541734d1..877ecc034d6 100644 --- a/homeassistant/components/tts/legacy.py +++ b/homeassistant/components/tts/legacy.py @@ -207,6 +207,7 @@ class Provider: hass: HomeAssistant | None = None name: str | None = None + has_entity: bool = False @property def default_language(self) -> str | None: diff --git a/homeassistant/components/tts/media_source.py b/homeassistant/components/tts/media_source.py index 97d2ab549bc..d3c0998bb77 100644 --- a/homeassistant/components/tts/media_source.py +++ b/homeassistant/components/tts/media_source.py @@ -145,13 +145,20 @@ class TTSMediaSource(MediaSource): return self._engine_item(engine, params) # Root. List providers. - children = [ - self._engine_item(engine) - for engine in self.hass.data[DATA_TTS_MANAGER].providers - ] + [ - self._engine_item(entity.entity_id) - for entity in self.hass.data[DATA_COMPONENT].entities - ] + children = sorted( + [ + self._engine_item(engine_id) + for engine_id, provider in self.hass.data[ + DATA_TTS_MANAGER + ].providers.items() + if not provider.has_entity + ] + + [ + self._engine_item(entity.entity_id) + for entity in self.hass.data[DATA_COMPONENT].entities + ], + key=lambda x: x.title, + ) return BrowseMediaSource( domain=DOMAIN, identifier=None, diff --git a/tests/components/tts/test_init.py b/tests/components/tts/test_init.py index 45424be8481..ea281506f3a 100644 --- a/tests/components/tts/test_init.py +++ b/tests/components/tts/test_init.py @@ -1522,6 +1522,45 @@ async def test_fetching_in_async( ) +@pytest.mark.parametrize( + ("setup", "engine_id"), + [ + ("mock_setup", "test"), + ], + indirect=["setup"], +) +async def test_ws_list_engines_filter_deprecated( + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + setup: str, + engine_id: str, +) -> None: + """Test listing tts engines and supported languages.""" + client = await hass_ws_client() + + await client.send_json_auto_id({"type": "tts/engine/list"}) + + msg = await client.receive_json() + assert msg["success"] + assert msg["result"] == { + "providers": [ + { + "name": "Test", + "engine_id": engine_id, + "supported_languages": ["de_CH", "de_DE", "en_GB", "en_US"], + } + ] + } + + hass.data[tts.DATA_TTS_MANAGER].providers[engine_id].has_entity = True + + await client.send_json_auto_id({"type": "tts/engine/list"}) + + msg = await client.receive_json() + assert msg["success"] + assert msg["result"] == {"providers": []} + + @pytest.mark.parametrize( ("setup", "engine_id", "extra_data"), [ diff --git a/tests/components/tts/test_media_source.py b/tests/components/tts/test_media_source.py index 4ff0a44a4bb..c9d70c7f43e 100644 --- a/tests/components/tts/test_media_source.py +++ b/tests/components/tts/test_media_source.py @@ -114,6 +114,13 @@ async def test_legacy_resolving( await mock_setup(hass, mock_provider) mock_get_tts_audio = mock_provider.get_tts_audio + mock_provider.has_entity = True + root = await media_source.async_browse_media(hass, "media-source://tts") + assert len(root.children) == 0 + mock_provider.has_entity = False + root = await media_source.async_browse_media(hass, "media-source://tts") + assert len(root.children) == 1 + mock_get_tts_audio.reset_mock() media_id = "media-source://tts/test?message=Hello%20World" media = await media_source.async_resolve_media(hass, media_id, None)